Skip to content

Shorting the cash

From the very first moment backtrader was enabled for shorting anything, including stock-like and future-like instruments. When a short was done, the cash was decreased and the value of the shorted asset used for the total net liquidation value.

Removing from one side and adding to the other keeps things in balance.

It seems that people prefer to have cash increased, which probably lends to more spending.

With release 1.9.7.105, the broker has changed the default behavior to adding cash and removing value. This can be controlled with the parameter shortcash which defaults to True. Changing it would be done like this:

cerebro.broker.set_shortcash(False)

or:

cerebro.broker = bt.brokers.BackBroker(shortcash=False, **other_kwargs)

In action

The sample below uses a standard moving average crossover and can be used to see the differences. Running it without arguments and the new behavior:

$ ./shortcash.py --plot

image

It can be compared with the behavior disabled with:

$ ./shortcash.py --plot --broker shortcash=False

image

Things that remain the same:

  • Final result

  • Trades

  • Net Liquidation Value evolution

    To see this an extra Observer is added to make sure the scaling allows seeing the evolution in detail

What changes:

  • When shortcash is set to False cash never goes above the initial level, because an operation alwasy costs money

    But with the new default behavior we can already see that the 1st short operation (which happens to be the 1st) adds cash to the system and then how the longs deduct cash from the system (the short is 1st closed, obviously)

Sample Usage

$ ./shortcash.py --help
usage: shortcash.py [-h] [--data DATA] [--cerebro CEREBRO] [--broker BROKER]
                    [--sizer SIZER] [--strat STRAT] [--plot [kwargs]]

shortcash testing ...

optional arguments:
  -h, --help            show this help message and exit
  --data DATA           Data to read in (default:
                        ../../datas/2005-2006-day-001.txt)
  --cerebro CEREBRO     kwargs in key=value format (default: )
  --broker BROKER       kwargs in key=value format (default: )
  --sizer SIZER         kwargs in key=value format (default: )
  --strat STRAT         kwargs in key=value format (default: )
  --plot [kwargs], -p [kwargs]
                        Plot the read data applying any kwargs passed For
                        example: --plot style="candle" (to plot candles)
                        (default: None)

Sample Code

from __future__ import (absolute_import, division, print_function,
                        unicode_literals)

import argparse

import backtrader as bt


class MACrossOver(bt.SignalStrategy):
    params = (('ma', bt.ind.MovAv.SMA), ('p1', 10), ('p2', 30),)

    def __init__(self):
        ma1, ma2 = self.p.ma(period=self.p.p1), self.p.ma(period=self.p.p2)
        self.signal_add(bt.SIGNAL_LONGSHORT, bt.ind.CrossOver(ma1, ma2))


def runstrat(args=None):
    args = parse_args(args)

    cerebro = bt.Cerebro()

    # Data feed
    data0 = bt.feeds.BacktraderCSVData(dataname=args.data)
    cerebro.adddata(data0)

    # Broker
    kwargs = eval('dict(' + args.broker + ')')
    cerebro.broker = bt.brokers.BackBroker(**kwargs)

    # Sizer
    kwargs = eval('dict(' + args.sizer + ')')
    cerebro.addsizer(bt.sizers.FixedSize, **kwargs)

    # Strategy
    kwargs = eval('dict(' + args.strat + ')')
    cerebro.addstrategy(MACrossOver, **kwargs)

    # better net liquidation value view
    cerebro.addobserver(bt.observers.Value)

    # Execute
    cerebro.run(**(eval('dict(' + args.cerebro + ')')))

    if args.plot:  # Plot if requested to
        cerebro.plot(**(eval('dict(' + args.plot + ')')))


def parse_args(pargs=None):

    parser = argparse.ArgumentParser(
        formatter_class=argparse.ArgumentDefaultsHelpFormatter,
        description='shortcash testing ...')

    parser.add_argument('--data', default='../../datas/2005-2006-day-001.txt',
                        required=False, help='Data to read in')

    parser.add_argument('--cerebro', required=False, action='store',
                        default='', help='kwargs in key=value format')

    parser.add_argument('--broker', required=False, action='store',
                        default='', help='kwargs in key=value format')

    parser.add_argument('--sizer', required=False, action='store',
                        default='', help='kwargs in key=value format')

    parser.add_argument('--strat', required=False, action='store',
                        default='', help='kwargs in key=value format')

    parser.add_argument('--plot', '-p', nargs='?', required=False,
                        metavar='kwargs', const='{}',
                        help=('Plot the read data applying any kwargs passed\n'
                              '\n'
                              'For example:\n'
                              '\n'
                              '  --plot style="candle" (to plot candles)\n'))

    return parser.parse_args(pargs)


if __name__ == '__main__':
    runstrat()