How is Getvalue() Calculated?



  • Hi there,

    How exactly is getvalue() calculated? I understand it is the sum of everything you have invested in market + cash?

    This should then mean that getvalue() -getcash() = sum of current value of everything invested in market (stocks).

    However, when I add up the value of my stocks held, it does not equal this.

    See my example print out below (totalwealth = getvalue(), invested = getvalue() - cash()), my invested value says only $27,000. But when you add up my stocks held, its more like $66,000...

    2012-12-28, ORDER CANCELLED - TP: ABC, Trade Ref: 1037
    2012-12-28, SELL EXECUTED - Closed Long Pos: SUN, Ref: 1039, Price: 9.97, PNL: 123.47, Purchase Cost: 8379.68, Comm 10.24
    2012-12-28, SELL EXECUTED - Closed Long Pos: CTX, Ref: 1040, Price: 19.10, PNL: 520.93, Purchase Cost: 8284.17, Comm 10.61
    2012-12-28, BUY EXECUTED - Entered Long Pos: TTS, Ref: 1041, Price: 3.01, Cost: 8298.57, Size: 2757.00, Comm 10.00
    2012-12-28, BUY EXECUTED - Entered Long Pos: CHC, Ref: 1042, Price: 3.32, Cost: 8373.04, Size: 2522.00, Comm 10.09
    2012-12-28, OPERATION PROFIT: SUN, GROSS 123.47, NET 103.13
    2012-12-28, CANCELLING STOP-LOSS: SUN, Ref: 216
    2012-12-28, CANCELLING TAKE-PROFIT: SUN, Ref: 216
    2012-12-28, OPERATION PROFIT: CTX, GROSS 520.93, NET 500.34
    2012-12-28, CANCELLING STOP-LOSS: CTX, Ref: 211
    2012-12-28, CANCELLING TAKE-PROFIT: CTX, Ref: 211
    2012-12-28, Stock held: TTS, Close: 3.01, score: 0.93, Score Yest: 0.92, Posn: 2757.00, hold days 1
    2012-12-28, CREATE SL: TTS, Trigger Price: 2.71
    2012-12-28, CREATE TP: TTS, Trigger Price: 9.03
    2012-12-28, Stock held: MTS, Close: 3.38, score: 1.00, Score Yest: 0.99, Posn: 2569.00, hold days 6
    2012-12-28, Stock held: FLT, Close: 27.03, score: 0.99, Score Yest: 1.00, Posn: 302.00, hold days 27
    2012-12-28, CREATE BUY: ABC, Close: 3.11, score: 0.94
    2012-12-28, Stock held: MYR, Close: 2.01, score: 1.00, Score Yest: 1.00, Posn: 4221.00, hold days 25
    2012-12-28, Stock held: CHC, Close: 3.31, score: 0.95, Score Yest: 0.92, Posn: 2522.00, hold days 1
    2012-12-28, CREATE SL: CHC, Trigger Price: 2.98
    2012-12-28, CREATE TP: CHC, Trigger Price: 9.93
    2012-12-28, Stock held: MQA, Close: 1.68, score: 0.97, Score Yest: 0.97, Posn: 5076.00, hold days 7
    2012-12-28, CREATE BUY: SMX, Close: 4.64, score: 0.91
    2012-12-28, Stocks Held: 6, Total Wealth: 104603, Invested: 27280, Cash-On-Hand (pre-today's buys): 77323
    2012-12-28, Ending Wealth: 104603, Invested: 27280, Cash-On-Hand: 77323,
    

    Perhaps I am interpreting the getvalue() definition wrong? Otherwise, any ideas why these numbers dont equate?

    Thank you


  • administrators

    Not much can be really said about an isolated log. You mention that the value of your stocks is more like $66,000 but why is it so is also not shown.

    You may be short on some, you may have your own commission scheme objects changing the calculation value, you may have tagged some as a future and only the margin is taken into account ...



  • @backtrader, given your response, I am assuming my assumption of the definition of getvalue() is correct.

    Given that I am trying to determine what the value of my invested stocks is. I was hoping to use getvalue() - getcash(), which is what this log line does: 2012-12-28, Ending Wealth: 104603, Invested: 27280, Cash-On-Hand: 77323

    However when you add up each of the stock held: lines; i.e.

    2012-12-28, Stock held: TTS, Close: 3.01, score: 0.93, Score Yest: 0.92, Posn: 2757.00, hold days 1
    2012-12-28, Stock held: MTS, Close: 3.38, score: 1.00, Score Yest: 0.99, Posn: 2569.00, hold days 6
    2012-12-28, Stock held: FLT, Close: 27.03, score: 0.99, Score Yest: 1.00, Posn: 302.00, hold days 27
    2012-12-28, Stock held: MYR, Close: 2.01, score: 1.00, Score Yest: 1.00, Posn: 4221.00, hold days 25
    2012-12-28, Stock held: CHC, Close: 3.31, score: 0.95, Score Yest: 0.92, Posn: 2522.00, hold days 1
    2012-12-28, Stock held: MQA, Close: 1.68, score: 0.97, Score Yest: 0.97, Posn: 5076.00, hold days 7
    

    You get $66,000 (Close*Posn for each line). Hence my question. Why would getvalue() - getcash() not equal the summation of stocks held?


  • administrators

    That's still an isolated log. What you are printing and from where and what the actual operations are is unclear.

    To start with:Sum(Close * Posn) is nowhere near 66000, which is apparently the basis of the problem.

    Things may be wrong in the platform and contain 1 million bugs, but any kind of statement should at least try to show why it is wrong.



  • Hi @backtrader, appologies for my maths error, must have been a different run... :-)

    As requested, here is my full script. Please ignore references to "Score" (i have set to "1" and commented out irrelevant bits).

    The issue remains though, at the end of the analysis 14 stocks are held, but the "invested" amount in this case is -$95k...!

    My script, log and plot are below.

    I look forward to your advise on how to fix this! I would like to use Stop-Loss / Take-Profit functionality, however I will need to sort out this negative stock investment issue for multiple stocks!

    Thank you kindly,
    CWE

    from __future__ import (absolute_import, division, print_function,
                            unicode_literals)
    import argparse
    import pandas as pd
    import numpy as np
    import datetime
    from scipy.stats import norm
    import math
    import backtrader as bt
    import backtrader.indicators as btind
    import backtrader.feeds as btfeeds
    import glob
    import ntpath
    
    
    def parse_args():
        parser = argparse.ArgumentParser(description='MultiData Strategy')
    
        parser.add_argument('--Custom_Alg',
                            default=False, # True OR False... NOT 'True' OR 'False'
                            help='True = Use custom alg')
    
        parser.add_argument('--SLTP_On',
                            default=True, # True OR False... NOT 'True' OR 'False'
                            help='True = Use Stop-Loss & Take-Profit Orders, False = do NOT use SL & TP orders')
    
        parser.add_argument('--stoploss',
                            action='store',
                            default=0.10, type=float,
                            help=('sell a long position if loss exceeds'))
    
        parser.add_argument('--takeprofit',
                            action='store',
                            default=2.00, type=float,
                            help=('Exit a long position if profit exceeds'))
    
        parser.add_argument('--data0', '-d0',
                            default=r'T:\PD_Stock_Data\DBLOAD',
                            help='Directory of CSV data source')
    
    
        parser.add_argument('--fromdate', '-f',
                            default='2012-01-01',
                            help='Starting date in YYYY-MM-DD format')
    
        parser.add_argument('--todate', '-t',
                            default='2013-12-31',
                            help='Ending date in YYYY-MM-DD format')
    
        parser.add_argument('--limitpct',
                            action='store',
                            default=0.005, type=float,
                            help=('For buying at LIMIT, this will only purchase if the price is less than (1-limitpct)*Closing price'))
    
        parser.add_argument('--validdays',
                            action='store',
                            default=30, type=int,
                            help=('The number of days which a buy order remains valid'))
    
        parser.add_argument('--sellscore',
                            action='store',
                            default=-0.91, type=float,
                            help=('Max score for a sell'))
    
        parser.add_argument('--marketindex',
                            default='XJO',
                            help='XAO = All Ords, XJO = ASX200')
    
        parser.add_argument('--startingcash',
                            default=100000, type=int,
                            help='Starting Cash')
    
        parser.add_argument('--minholddays',
                            default=3, type=int,
                            help='Dont exit a market position until have held stock for at least this many days (excl. Stop-Loss and TP). May assist stopping exiting/cancelling orders when they are still being accepted by broker (i.e. day after entering mkt).')
    
        parser.add_argument('--pctperstock',
                            action='store', #0.083 = 1/12... i.e. a portfolio of up to 12 stocks
                            default=0.083, type=float, #i.e. 10% portfolio value in each stock
                            help=('Pct of portfolio starting cash to invest in each stock purchase'))
    
        parser.add_argument('--maxpctperstock',
                            action='store',
                            default=0.20, type=float,
                            help=('Max pct portfolio to invest in any porticular stock'))
    
        parser.add_argument('--mintrade',
                            default=1000, type=float,
                            help='Smallest dollar value to invest in a stock (if cash level below amount required for pctperstock)')
    
        parser.add_argument('--tradefee',
                            default=10.0, type=float,
                            help='CMC Markets Fee per stock trade (BUY OR SELL)')
    
        parser.add_argument('--alg_buyscore', #only used if Custom_Alg ==True
                            action='store',  # 0.91884558
                            default=0.91, type=float,
                            help=('Min score for a buy'))
    
        return parser.parse_args()
    
    #Excel sheet with ASX200 index constituents (used for chosing stocks to analyse in Backtrader)
    def LoadIndicies(Excel_Path, Excel_Sheet):
        # Load ASX200 Excel File
        ASX200 = pd.read_excel(Excel_Path, sheetname=Excel_Sheet)
        Index_Constituents = ASX200.to_dict(orient='list')
        for key, value in Index_Constituents.items():
            Index_Constituents[key] = [x for x in value if str(x) != 'nan']  # drop any "blank" (NaN) tickers from the index constituents table
        IndexDates = sorted(Index_Constituents.keys())
        IndexDates.append(datetime.datetime.now().strftime("%Y-%m-%d"))  # ordered list of the Index constituent Dates, with todays date at the end
        return Index_Constituents, IndexDates
    
    def LoadStockData(CSV_path=None):
        args = parse_args()
        if CSV_path is None:
            raise RuntimeError("no stock folder directory specifed.")
        allFiles = glob.glob(CSV_path + "/*.csv")
        Stocks = {}  # Create a DICTIONARY object to store the entire contents of all dataframes, allows for easy reference to / looping through dataframes by a string of their name, i.e. : 'CSL'
        for file_ in allFiles:
            name = ntpath.basename(file_[:-4])  # Set DF name = basename (not path) of the CSV.  [:-4] gets rid of the '.CSV' extention.
            Stocks[name] = pd.read_csv(file_, index_col='Date', parse_dates=True, header=0)
        return Stocks
    
    
    class StockLoader(btfeeds.PandasData):
        args = parse_args()
        params = (
            ('openinterest', None),     # None= column not present
            ('TOTAL_SCORE', -1))        # -1 = autodetect position or case-wise equal name
        if args.Custom_Alg == True:
            lines = ('TOTAL_SCORE',)
        if args.Custom_Alg == True:
            datafields = btfeeds.PandasData.datafields + (['TOTAL_SCORE'])
        else:
            datafields = btfeeds.PandasData.datafields
    
    class st(bt.Strategy):
        args = parse_args()
        params = ( #NB: self.p = self.params
            ('printlog', True),
        )
    
        def log(self, txt, dt=None, doprint=False):
            if self.p.printlog or doprint:
                dt = dt or self.datas[0].datetime.date(0)
                print('%s - %s' % (dt.isoformat(), txt))
    
        def __init__(self):
            self.order = {} #Order: market entry
            self.order_out = {} #Order: market exit
            self.order_sl = {} #Stop-Loss
            self.order_tp = {} #Take-Profit
            self.bar_executed = {}
            self.sma_short = {}
            self.sma_long = {}
            for i, d in enumerate(d for d in self.datas):
                self.order[d._name] = None
                self.order_out[d._name] = None
                self.order_sl[d._name] = None
                self.order_tp[d._name] = None
                self.bar_executed[d._name] = None
    
                self.sma_short[d._name] = bt.indicators.SimpleMovingAverage(d, period=42) #np.round(pd.rolling_mean(d.Close, window=42),2)
                self.sma_long[d._name] = bt.indicators.SimpleMovingAverage(d, period=252) #np.round(pd.rolling_mean(d.Close, window=252),2)
    
    
        def start(self):
            pass
    
        def notify_trade(self, trade): #NB: "print(Trade.__dict__)"
            if trade.isclosed: #Market Position exited
                self.log('OPERATION PROFIT: %s, Gross: %2f, Net: %2f' %(trade.data._name,
                                                                        trade.pnl,
                                                                        trade.pnlcomm))
    
        def notify_order(self, order):
    
            #used to check order info
            ord = self.order[order.data._name]
            ord_out = self.order_out[order.data._name]
            ord_sl = self.order_sl[order.data._name]
            ord_tp = self.order_tp[order.data._name]
    
            if order.status in [order.Submitted, order.Accepted]:
                return #do nothing
    
            elif order.status in [order.Margin, order.Rejected, order.Completed, order.Cancelled]:
                if order.isbuy():
                    buysell = 'BUY'
                elif order.issell():
                    buysell = 'SELL'
    
                if ord and order == ord:
                    type = 'Enter'
                    self.bar_executed[order.data._name] = len(order.data) #length of dataframe when enter market (used to calc days held)
                elif ord_out and order == ord_out:
                    type = 'Exit'
                elif ord_sl and order == ord_sl:
                    type = 'Stop-Loss'
                elif ord_tp and order == ord_tp:
                    type = 'Take-Profit'
    
                self.log('%s %s: %s, Type: %s, Ref: %s, Price: %.2f, Cost: %.2f, Size: %.2f, Comm %.2f' %(buysell,
                                                                                                          order.Status[order.status],
                                                                                                          order.data._name,
                                                                                                          type,
                                                                                                          order.ref,
                                                                                                          order.executed.price,
                                                                                                          order.executed.value,
                                                                                                          order.executed.size,
                                                                                                          order.executed.comm))
    
            if not order.alive():# indicate no order is pending, allows new orders
                if ord and order == ord:
                    self.order[order.data._name] = None
    
                elif ord_out and order == ord_out:
                    self.order_out[order.data._name] = None
    
                elif ord_sl and order == ord_sl:
                    self.order_sl[order.data._name] = None
    
                elif ord_tp and order == ord_tp:
                    self.order_tp[order.data._name] = None
    
        def prenext(self): #overrides PRENEXT() so that the "NEXT()" calculations runs regardless of when each stock data date range starts.
            self.next()
    
        def next(self):
            today = self.getdatabyname(args.marketindex).datetime.date(0)
            weekday = today.isoweekday() #Monday = 1, Sunday = 7
            if weekday in range(1,8): # analyse on all weekdays (MONDAY to SUNDAY)
                num_long = 0 #number long stocks
                #IdealLongPortf = pd.DataFrame(columns=('Stock', 'Score','Close','Current Position', 'Ideal Position', 'Pos Delta Value', 'Go NoGo')) #ideal stock positions at end of each next() iteration
                for i, d in enumerate(d for d in self.datas if len(d) and d._name != args.marketindex):  # Loop through Universe of Stocks. "If Len(d)" is used to check that all datafeeds have delivered values. as if using minute data, some may have had many minutes, 500, and another may not have 1 record yet (if its still on daily)
                    position = self.broker.getposition(d)
                    positiondol = float(self.broker.getposition(d).size*d.close[0])
                    cash = self.broker.getcash() #total available cash
    
                    if position.size == 0 \
                            and self.order[d._name] is None \
                            and self.order_out[d._name] is None \
                            and self.order_sl[d._name] is None \
                            and self.order_tp[d._name] is None \
                            and d.close[0] > 0 \
                            and self.sma_short[d._name][0] > self.sma_long[d._name][0]:
                            #and d.lines.TOTAL_SCORE[0] >= args.alg_buyscore:
    
                        #IdealLongPortf.append([d._name, d.lines.TOTAL_SCORE[0], d.close[0], position.size, np.NaN, np.NaN,np.NaN])
                        buylimit = d.close[0]*(1-args.limitpct)
    
                        if args.SLTP_On == True:
                            stop_loss = d.close[0]*(1.0 - args.stoploss)
                            take_profit = d.close[0]*(1.0 + args.takeprofit)
    
                            o1 = self.buy(data = d,
                                          exectype=bt.Order.Limit,
                                          price=buylimit,
                                          valid=today + datetime.timedelta(days=args.validdays),
                                          transmit=False)
                            self.log('CREATE BUY: %s, Close: %2f, Buy @: %2f, Oref: %i, Score: %2f' %(d._name,
                                                                                                      d.close[0],
                                                                                                      buylimit,
                                                                                                      o1.ref,
                                                                                                      1)) #d.lines.TOTAL_SCORE[0]))
    
                            o2 = self.sell(data = d,
                                           size = o1.size,         # could be an issue with re-balancing!!!
                                           exectype=bt.Order.Stop,
                                           price=stop_loss,
                                           parent=o1,
                                           transmit=False)
                            self.log('CREATE Stop-Loss: %s, Sell Stop @: %2f, Oref: %i' %(d._name, stop_loss, o2.ref))
    
                            o3 = self.sell(data = d,
                                           size = o1.size,
                                           exectype=bt.Order.Limit,
                                           price=take_profit,
                                           parent=o1,
                                           transmit=True)
                            self.log('CREATE Take-Profit: %s, Sell Limit @: %2f, Oref: %i' %(d._name, take_profit, o3.ref))
    
                            self.order[d._name] = o1
                            self.order_sl[d._name] = o2
                            self.order_tp[d._name] = o3
                        else:
                            o1 = self.buy(data = d,
                                          exectype=bt.Order.Limit,
                                          price=buylimit,
                                          valid=today + datetime.timedelta(days=args.validdays))
                            self.log('CREATE BUY: %s, Close: %2f, Buy @: %2f, Oref: %i, Score: %2f' %(d._name,
                                                                                                      d.close[0],
                                                                                                      buylimit,
                                                                                                      o1.ref,
                                                                                                      1)) #d.lines.TOTAL_SCORE[0]))
                            self.order[d._name] = o1
    
                    elif position.size > 0: # Currently LONG
                        daysheld = len(d) - self.bar_executed[d._name] + 1
                        self.log('Stock Held: %s, Close: %2f, Posn: %i, Posn($): %2f, Hold Days: %i, Score: %2f, Score Yest: %2f' %(d._name,
                                                                                                                                   d.close[0],
                                                                                                                                   position.size,
                                                                                                                                   positiondol,
                                                                                                                                   daysheld,
                                                                                                                                   1, #d.lines.TOTAL_SCORE[0],
                                                                                                                                   1)) #d.lines.TOTAL_SCORE[-1]))
    
                        num_long +=1
    
                        if self.order[d._name] is None \
                            and self.order_out[d._name] is None \
                            and daysheld >= args.minholddays \
                            and self.sma_short[d._name][0] < self.sma_long[d._name][0]:
                            #and d.lines.TOTAL_SCORE[0] < args.alg_buyscore:
                            self.log('CLOSING LONG POSITION: %s, Close: %2f, Score: %2f' %(d._name,
                                                                                     d.close[0],
                                                                                     1)) #d.lines.TOTAL_SCORE[0]))
    
                            # cancel SL/TP 1st to avoid possibility of duplicate exit orders
                            if self.order_sl[d._name]:
                                self.log('CANCELLING SL & TP for: %s, SL ref: %i, TP ref: %i' %(d._name,self.order_sl[d._name].ref, self.order_tp[d._name].ref))
                                self.broker.cancel(self.order_sl[d._name]) #this automatically cancels the TP too
                            #if self.order_tp[d._name]:
                            #    self.log('CANCELLING TP for: %s, ref: %s' %(d._name,self.order_tp[d._name].ref))
                            #    self.broker.cancel(self.order_tp[d._name])
    
                            o4 = self.close(data=d)
                            self.order_out[d._name] = o4
    
                totalwealth = self.broker.getvalue()
                cash = self.broker.getcash()
                invested = totalwealth - cash
    
                self.log("Stocks Held: %s, Total Wealth: %i, Invested: %i, Cash-On-Hand: %i" %(str(num_long),
                                                                                               totalwealth,
                                                                                               invested,
                                                                                               cash))
    
        def stop(self):
            pass
    
    class PortfolioSizer(bt.Sizer):
        def _getsizing(self, comminfo, cash, data, isbuy):
            args = parse_args()
            position = self.broker.getposition(data)
            price = data.close[0]
            investment = args.startingcash * args.pctperstock
            if cash < investment:
                investment = max(cash,args.mintrade) # i.e. never invest less than the "mintrade" $value
            qty = math.floor(investment/price)
    
            # This method returns the desired size for the buy/sell operation
            if isbuy:  # if buying
                if position.size < 0:  # if currently short, buy the amount which are short to close out trade.
                     return -position.size
                elif position.size > 0:
                    return 0  # dont buy if already hold
                else:
                    return qty  # num. stocks to LONG
    
            if not isbuy:  # if selling..
                if position.size < 0:
                    return 0  # dont sell if already SHORT
                elif position.size > 0:
                    return position.size  # currently Long... sell what hold
                else:
                    return qty  # num. stocks to SHORT
    
    
    def RunStrategy():
        args = parse_args()
        cerebro = bt.Cerebro()
        cerebro.addstrategy(st)
        # strats = cerebro.optstrategy(st,maperiod=range(10, 31))
    
        #date range to backtest
        tradingdates = Stocks[args.marketindex].loc[
            (Stocks[args.marketindex].index>=datetime.datetime.strptime(args.fromdate, "%Y-%m-%d")) &
            (Stocks[args.marketindex].index<datetime.datetime.strptime(args.todate, "%Y-%m-%d"))
        ]
    
        #Load 200 stocks into Backtrader (specified in the Index_constituents list)
        for ticker in Index_Constituents[IndexDates[3]]:
            datarange = Stocks[ticker].loc[
                (Stocks[ticker].index>=datetime.datetime.strptime(args.fromdate, "%Y-%m-%d")) &
                (Stocks[ticker].index<datetime.datetime.strptime(args.todate, "%Y-%m-%d"))
            ]
            #REINDEX to make sure the stock has the exact same trading days as the MARKET INDEX. Reindex ffill doesn't fill GAPS. Therefore also apply FILLNA
            datarange.reindex(tradingdates.index, method='ffill').fillna(method='ffill',inplace=True)
            data = StockLoader(dataname=datarange)
            data.plotinfo.plot=False
            cerebro.adddata(data, name=ticker)
    
        data = btfeeds.PandasData(dataname=tradingdates, openinterest=None) #load market index (for date referencing)
        cerebro.adddata(data, name=args.marketindex)
    
        #cerebro.addanalyzer(CurrentBuysAnalyzer, )
        cerebro.addanalyzer(bt.analyzers.TradeAnalyzer, ) #length of holds etc
        cerebro.addanalyzer(bt.analyzers.TimeReturn, timeframe=bt.TimeFrame.Years)
        cerebro.addanalyzer(bt.analyzers.SharpeRatio, timeframe=bt.TimeFrame.Years, riskfreerate=0.03, annualize=True)
        cerebro.addanalyzer(bt.analyzers.SQN)
        cerebro.addanalyzer(bt.analyzers.DrawDown, )
    
        cerebro.broker.setcash(args.startingcash) # set starting cash
        cerebro.addsizer(PortfolioSizer)
    
        commission = float(args.tradefee/(args.pctperstock*args.startingcash))
        print("The Commission rate is: %0.5f" % (commission))
        cerebro.broker.setcommission(commission=commission)
    
        cerebro.run(runonce=False, writer=True)
        cerebro.plot(volume=False, stdstats=False)
        '''
        zdown=True: Rotation of the date labes on the x axis
        stdstats=False: Disable the standard plotted observers
        numfigs=1: Plot on one chart
        '''
    
    if __name__ == '__main__':
        # if python is running this script (module) as the main program, then __name__ == __main__, and this block of code will run.
        #  However, if another script (module) is IMPORTING this script (module), this block of code WILL NOT RUN, but the above functions can be called.
        args = parse_args()
        t1 = datetime.datetime.now()
        print('Processing Commenced: {}'.format(str(t1)))
    
        #Load stocks from local drive for analysis
        Stocks = LoadStockData(args.data0)
        t2 = datetime.datetime.now()
    
        # Dictionary of Index Constituents (and their stock dataframes)
        Index_Constituents, IndexDates = LoadIndicies(
            Excel_Path='T:\Google Drive\Capriole\CAPRIOLEPROCESSOR\TickerUpdater.xlsm',
            Excel_Sheet='ASX200_Const_Updated')
        print('ASX200 constituent list date: {}'.format(IndexDates[3]))
    
        if args.Custom_Alg == True:
            initiate()
            t3 = datetime.datetime.now()
    
        RunStrategy()
        t4 = datetime.datetime.now()
    
        #TIMER
        print('Run-time - TOTAL: {0}'.format(datetime.datetime.now() - t1))
        print('Run-time - Load Data: {0}'.format(t2 - t1))
        if t3:
            print('Run-time - Algorithm: {0}'.format(t3 - t2))
            print('Run-time - Strategy Back-test: {0}'.format(t4 - t3))
        else:
            print('Run-time - Strategy Back-test: {0}'.format(t4 - t2))
    

    And last few days of log extract (wont let me copy all):

    2013-12-20 - Stocks Held: 14, Total Wealth: 159974, Invested: -92641, Cash-On-Hand: 252615
    2013-12-23 - Stock Held: WBC, Close: 31.744440, Posn: 319, Posn($): 10126.476360, Hold Days: 250, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: ANZ, Close: 31.950001, Posn: 332, Posn($): 10607.400332, Hold Days: 250, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: WES, Close: 43.412518, Posn: 225, Posn($): 9767.816550, Hold Days: 250, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: WOW, Close: 33.610001, Posn: 281, Posn($): 9444.410281, Hold Days: 250, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: WPL, Close: 38.900002, Posn: 246, Posn($): 9569.400492, Hold Days: 250, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: CSL, Close: 68.300003, Posn: 152, Posn($): 10381.600456, Hold Days: 250, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: DL_FOX, Close: 37.700001, Posn: 363, Posn($): 13685.100363, Hold Days: 250, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: BXB, Close: 9.110000, Posn: 139, Posn($): 1266.290000, Hold Days: 247, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: GMG, Close: 4.790000, Posn: 273, Posn($): 1307.670000, Hold Days: 246, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: DL_AIO, Close: 5.156790, Posn: 1919, Posn($): 9895.880010, Hold Days: 237, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: CGF, Close: 6.050000, Posn: 1959, Posn($): 11851.950000, Hold Days: 226, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: AGO, Close: 1.185000, Posn: 6803, Posn($): 8061.555000, Hold Days: 10, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: CDD, Close: 3.827047, Posn: 2136, Posn($): 8174.572392, Hold Days: 42, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stock Held: AOG, Close: 1.950000, Posn: 4761, Posn($): 9283.950000, Hold Days: 40, Score: 1.000000, Score Yest: 1.000000
    2013-12-23 - Stocks Held: 14, Total Wealth: 159800, Invested: -92815, Cash-On-Hand: 252615
    2013-12-24 - Stock Held: WBC, Close: 31.963230, Posn: 319, Posn($): 10196.270370, Hold Days: 251, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: ANZ, Close: 32.130001, Posn: 332, Posn($): 10667.160332, Hold Days: 251, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: WES, Close: 43.671818, Posn: 225, Posn($): 9826.159050, Hold Days: 251, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: WOW, Close: 33.700001, Posn: 281, Posn($): 9469.700281, Hold Days: 251, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: WPL, Close: 39.099998, Posn: 246, Posn($): 9618.599508, Hold Days: 251, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: CSL, Close: 68.830002, Posn: 152, Posn($): 10462.160304, Hold Days: 251, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: DL_FOX, Close: 37.980000, Posn: 363, Posn($): 13786.740000, Hold Days: 251, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: BXB, Close: 9.210000, Posn: 139, Posn($): 1280.190000, Hold Days: 248, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: GMG, Close: 4.790000, Posn: 273, Posn($): 1307.670000, Hold Days: 247, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: DL_AIO, Close: 5.165837, Posn: 1919, Posn($): 9913.241203, Hold Days: 238, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: CGF, Close: 6.130000, Posn: 1959, Posn($): 12008.670000, Hold Days: 227, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: AGO, Close: 1.180000, Posn: 6803, Posn($): 8027.540000, Hold Days: 11, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: CDD, Close: 3.872812, Posn: 2136, Posn($): 8272.326432, Hold Days: 43, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stock Held: AOG, Close: 1.950000, Posn: 4761, Posn($): 9283.950000, Hold Days: 41, Score: 1.000000, Score Yest: 1.000000
    2013-12-24 - Stocks Held: 14, Total Wealth: 157471, Invested: -95144, Cash-On-Hand: 252615
    2013-12-27 - Stock Held: WBC, Close: 31.913506, Posn: 319, Posn($): 10180.408414, Hold Days: 252, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: ANZ, Close: 32.160000, Posn: 332, Posn($): 10677.120000, Hold Days: 252, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: WES, Close: 43.921139, Posn: 225, Posn($): 9882.256275, Hold Days: 252, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: WOW, Close: 33.709999, Posn: 281, Posn($): 9472.509719, Hold Days: 252, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: WPL, Close: 39.099998, Posn: 246, Posn($): 9618.599508, Hold Days: 252, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: CSL, Close: 68.400002, Posn: 152, Posn($): 10396.800304, Hold Days: 252, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: DL_FOX, Close: 38.590000, Posn: 363, Posn($): 14008.170000, Hold Days: 252, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: BXB, Close: 9.180000, Posn: 139, Posn($): 1276.020000, Hold Days: 249, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: GMG, Close: 4.750000, Posn: 273, Posn($): 1296.750000, Hold Days: 248, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: DL_AIO, Close: 5.138696, Posn: 1919, Posn($): 9861.157624, Hold Days: 239, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: CGF, Close: 6.160000, Posn: 1959, Posn($): 12067.440000, Hold Days: 228, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: AGO, Close: 1.185000, Posn: 6803, Posn($): 8061.555000, Hold Days: 12, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: CDD, Close: 3.981502, Posn: 2136, Posn($): 8504.488272, Hold Days: 44, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stock Held: AOG, Close: 1.990000, Posn: 4761, Posn($): 9474.390000, Hold Days: 42, Score: 1.000000, Score Yest: 1.000000
    2013-12-27 - Stocks Held: 14, Total Wealth: 158338, Invested: -94277, Cash-On-Hand: 252615
    2013-12-30 - Stock Held: WBC, Close: 32.142239, Posn: 319, Posn($): 10253.374241, Hold Days: 253, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: ANZ, Close: 32.209999, Posn: 332, Posn($): 10693.719668, Hold Days: 253, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: WES, Close: 43.931114, Posn: 225, Posn($): 9884.500650, Hold Days: 253, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: WOW, Close: 33.849998, Posn: 281, Posn($): 9511.849438, Hold Days: 253, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: WPL, Close: 39.169998, Posn: 246, Posn($): 9635.819508, Hold Days: 253, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: CSL, Close: 68.900002, Posn: 152, Posn($): 10472.800304, Hold Days: 253, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: DL_FOX, Close: 38.639999, Posn: 363, Posn($): 14026.319637, Hold Days: 253, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: BXB, Close: 9.180000, Posn: 139, Posn($): 1276.020000, Hold Days: 250, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: GMG, Close: 4.790000, Posn: 273, Posn($): 1307.670000, Hold Days: 249, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: DL_AIO, Close: 5.183931, Posn: 1919, Posn($): 9947.963589, Hold Days: 240, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: CGF, Close: 6.170000, Posn: 1959, Posn($): 12087.030000, Hold Days: 229, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: AGO, Close: 1.195000, Posn: 6803, Posn($): 8129.585000, Hold Days: 13, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: CDD, Close: 4.010105, Posn: 2136, Posn($): 8565.584280, Hold Days: 45, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stock Held: AOG, Close: 2.130000, Posn: 4761, Posn($): 10140.930000, Hold Days: 43, Score: 1.000000, Score Yest: 1.000000
    2013-12-30 - Stocks Held: 14, Total Wealth: 157279, Invested: -95336, Cash-On-Hand: 252615
    ===============================================================================
    Cerebro:
      -----------------------------------------------------------------------------
      - Datas:
        +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
    


  • @backtrader, heres the plot0_1491644228612_Plot.JPG


  • administrators

    The chart seems to indicate that you are entering short positions.

    The code only prints position sizes and holding length for long operations (position.size > 0), which makes it impossible to (using the log) prove or disprove that theory.

    No traces of operations are present in the log, but it seems reasonable to assume that many sell operations are started which contribute to an increase in short positions.

    And that's the key issue:

    • Each short position decreases the value
    • Each short position increases the cash


  • Thanks @backtrader, good feedback but I am unsure how this could be occurring? I have probably made a mistake somewhere, but my logic is based on there being no short positions whatsoever. In my next statement I only enter long positions. And my Sizer() is (hopefully) also supporting that.

    Is my order dictionaries logic and SL/TP creation/cancelling logic sound? Can you please verify that for me?

    In addition, the analyser log says no short positions are entered, heres the log print out following the datas info:

    - Strategies:
        +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
        - st:
          *************************************************************************
          - Params:
            - printlog: True
          *************************************************************************
          - Indicators:
            .......................................................................
            - SimpleMovingAverage:
              - Lines: sma
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Params:
                - period: 252
          *************************************************************************
          - Observers:
            .......................................................................
            - Broker:
              - Lines: cash, value
              - Params: None
            .......................................................................
            - BuySell:
              - Lines: buy, sell
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Params:
                - barplot: True
                - bardist: 0.015
            .......................................................................
            - DataTrades:
              - Lines: BHP, CBA, WBC, ANZ, NAB, TLS, WES, WOW, RIO, WPL, CSL, WFD, DL_FOX, NCM, FMG, AMP, QBE, SUN, ORG, MQG, BXB, STO, CCL, AMC, IAG, OSH, ORI, TCL, AGL, AZJ, SGP, CWN, GMG, GPT, SYD, RMD, CIM, WOR, RHC, ASX, DL_NVN, LLC, IPL, SHL, CTX, MGR, CPU, DXS, DL_AIO, FBU, APA, COH, TTS, JHX, ILU, ALQ, AST, WHC, BEN, QAN, BLD, DL_TOL, SPK, VCX, TWE, MTS, SGR, GNC, FLT, SVW, RRL, DL_CPA, SEK, DUE, BOQ, PTM, AWC, MND, TAH, SKI, OZL, TPM, DL_PNA, HVN, ANN, PRY, ABC, SUL, DL_ALZ, SGM, FGX, BSL, CGF, BPT, MIN, IOF, UGL, CAR, NVT, DOW, ALL, SWM, AGO, IFL, DL_AUT, QUB, NUF, HGG, MSB, DL_ENV, PPT, DLX, SFR, MYR, DL_DJS, CQR, ARI, CWY, EVN, DL_GFF, FXJ, KAR, LYC, BWP, SDL, DL_WTF, IGO, RSG, DL_AQA, FXL, IRE, VAH, MMS, MML, JBH, BKN, CSR, DL_BRS, CDD, ABP, CHC, MGX, IVC, PRU, PDN, BLY, ASL, SAI, SCP, WSA, SIP, BRG, SXY, OGC, CDU, MQA, DL_DML, SLR, MRM, DL_IIN, BDR, SXL, SRX, SBM, GWA, KCN, TEN, DL_MTU, DL_DLS, EWC, AWE, BRU, DL_SKE, GUD, FWD, AAD, NST, DL_PBG, NWH, CAB, AQG, ACR, DL_CPL, DL_MBN, DCG, BBG, TRS, EHL, GBG, AOG, DL_SGT, IMD, TRY, MDL, FDM, SMX, BRL, SAR, DL_GRY, XJO
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Params:
                - usenames: True
          *************************************************************************
          - Analyzers:
            .......................................................................
            - Value:
              - Begin: 100000
              - End: 157279.72151316737
            .......................................................................
            - tradeanalyzer:
              - Params: None
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Analysis:
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - total:
                  - total: 93
                  - open: 52
                  - closed: 41
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - streak:
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - won:
                    - current: 0
                    - longest: 2
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - lost:
                    - current: 6
                    - longest: 29
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - pnl:
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - gross:
                    - total: 21406.054992015008
                    - average: 522.0989022442685
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - net:
                    - total: 20736.80284161358
                    - average: 505.7756790637458
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - won:
                  - total: 4
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - pnl:
                    - total: 43579.02415400791
                    - average: 10894.756038501977
                    - max: 43189.75324506506
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - lost:
                  - total: 37
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - pnl:
                    - total: -22842.22131239433
                    - average: -617.3573327674143
                    - max: -989.3569277108446
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - long:
                  - total: 41
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - pnl:
                    - total: 20736.80284161358
                    - average: 505.7756790637458
                    ###############################################################
                    - won:
                      - total: 43579.02415400791
                      - average: 10894.756038501977
                      - max: 43189.75324506506
                    ###############################################################
                    - lost:
                      - total: -22842.22131239433
                      - average: -617.3573327674143
                      - max: -989.3569277108446
                  - won: 4
                  - lost: 37
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - short:
                  - total: 0
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - pnl:
                    - total: 0.0
                    - average: 0.0
                    ###############################################################
                    - won:
                      - total: 0.0
                      - average: 0.0
                      - max: 0.0
                    ###############################################################
                    - lost:
                      - total: 0.0
                      - average: 0.0
                      - max: 0.0
                  - won: 0
                  - lost: 0
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - len:
                  - total: 2205
                  - average: 53.78048780487805
                  - max: 218
                  - min: 1
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - won:
                    - total: 449
                    - average: 112.25
                    - max: 186
                    - min: 3
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - lost:
                    - total: 1756
                    - average: 47.45945945945946
                    - max: 218
                    - min: 1
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - long:
                    - total: 2205
                    - average: 53.78048780487805
                    - max: 218
                    - min: 1
                    ###############################################################
                    - won:
                      - total: 449
                      - average: 112.25
                      - max: 186
                      - min: 3
                    ###############################################################
                    - lost:
                      - total: 1756
                      - average: 47.45945945945946
                      - max: 218
                      - min: 1
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  - short:
                    - total: 0
                    - average: 0.0
                    - max: 0
                    - min: 9223372036854775807
                    ###############################################################
                    - won:
                      - total: 0
                      - average: 0.0
                      - max: 0
                      - min: 9223372036854775807
                    ###############################################################
                    - lost:
                      - total: 0
                      - average: 0.0
                      - max: 0
                      - min: 9223372036854775807
            .......................................................................
            - timereturn:
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Params:
                - timeframe: 8
                - compression: None
                - _doprenext: True
                - data: None
                - firstopen: True
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Analysis:
                - 2012-12-31: 0.0002235522925362332
                - 2013-12-31: 0.5724456912924965
            .......................................................................
            - sharperatio:
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Params:
                - timeframe: 8
                - compression: 1
                - riskfreerate: 0.03
                - factor: None
                - convertrate: True
                - annualize: True
                - stddev_sample: False
                - daysfactor: None
                - legacyannual: False
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Analysis:
                - sharperatio: 0.895926963750468
            .......................................................................
            - sqn:
              - Params: None
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Analysis:
                - sqn: 0.47926917418139037
                - trades: 41
            .......................................................................
            - drawdown:
              - Params: None
              ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
              - Analysis:
                - len: 128
                - drawdown: 9.523888291773911
                - moneydown: 16555.911499416543
                """""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""""
                - max:
                  - len: 128
                  - drawdown: 15.175583721262242
                  - moneydown: 26380.57202521086
    
    Run-time - TOTAL: 0:03:05.675929
    Run-time - Load Data: 0:00:23.584653
    
    Process finished with exit code 1
    

    Thank you
    PS: I searched the entire log for -ve positions ("posn: -" or "posn($): -") and there are no results, which would also suggest there is no shorting?


  • administrators

    The best approach would be to print the actual position of all assets without restricting it to position.size > 0

    Given that the cash keeps on growing and growing it would seem like if the short trades were never closed. If the assumption is right, they would never show up in the analyzer, because they are still alive.



  • my wrong suggestion



  • Thanks @backtrader, i will try debuging with negative positions. But why are they occuring??
    It must be because of the stop loss and take profit. I dont have this issue when i turn that functionality off. Can you tell me how I have implemented the stop loss and take profit functionality incorrectly? Am I cancelling orders OK? Are my new market positions managing them OK?

    @ab_trader not sure on your comment there :-)


  • administrators

    Another long shot is that the code apparently fill values with NaN with fillna. If the prices are NaN that could have effect in the different calculations of indicators and in the broker.

    To actually try to verify, the first thing would be to go down from 200 data feeds to 1 and then 2.


  • administrators



  • Thanks @backtrader, fillna propogates the last non-NaN value forward to fill any holes (NaN values) in data that may exist. Its a common technique in investment analysis.

    As mentioned, the code above works fine when I set SLTP_On=False and don't use stop-loss take profit functionality, so I am inclined to think I am either most likely executing this functionality wrong, or it doesnt work properly...?

    Can you please advise if I am implementing this functionality correct, with the dictionarys, order cancelling and creation? I.e. the next statement, the init step and notify_order step

    Some extracts of interest - INIT:

            self.order = {} #Order: market entry
            self.order_out = {} #Order: market exit
            self.order_sl = {} #Stop-Loss
            self.order_tp = {} #Take-Profit
            self.bar_executed = {}
            self.sma_short = {}
            self.sma_long = {}
            for i, d in enumerate(d for d in self.datas):
                self.order[d._name] = None
                self.order_out[d._name] = None
                self.order_sl[d._name] = None
                self.order_tp[d._name] = None
                self.bar_executed[d._name] = None
    

    NOTIFY_ORDER:

            if not order.alive():# indicate no order is pending, allows new orders
                if ord and order == ord:
                    self.order[order.data._name] = None
    
                elif ord_out and order == ord_out:
                    self.order_out[order.data._name] = None
    
                elif ord_sl and order == ord_sl:
                    self.order_sl[order.data._name] = None
    
                elif ord_tp and order == ord_tp:
                    self.order_tp[order.data._name] = None
    

    NEXT:

                            # cancel SL/TP 1st to avoid possibility of duplicate exit orders
                            if self.order_sl[d._name]:
                                self.log('CANCELLING SL & TP for: %s, SL ref: %i, TP ref: %i' %(d._name,self.order_sl[d._name].ref, self.order_tp[d._name].ref))
                                self.broker.cancel(self.order_sl[d._name]) #this automatically cancels the TP too
                            #if self.order_tp[d._name]:
                            #    self.log('CANCELLING TP for: %s, ref: %s' %(d._name,self.order_tp[d._name].ref))
                            #    self.broker.cancel(self.order_tp[d._name])
    
                            o4 = self.close(data=d)
                            self.order_out[d._name] = o4
    

  • administrators

    The code has obviously been read above and hints offered. Sensible steps to find the problems could be

    • Reduce the number of assets in play from 200 to 1 or 2. Even better if done with reference data feeds which need no manipulation.
    • If testing with 200 tickers is wished, check for duplicate tickers
    • Output the position of all assets and not only that of the assets for which position.size > 0
    • Stop execution as soon as a position has gone short
    • Match the executed orders to the generated orders which should show which order is the one producing the likely culprit (being the suspicion that for several assets the code is generating short positions which increase the cash level)
    • Use a simple sizer or even a fixed stake to start with

    The reference implementation offered in the blog post above may be used as a starting base if wished.



  • Thank you @backtrader, I can confirm the issue is resolved and likely was a result of my version issues.

    No short positions are entered, and sum of stocks held = getvalue() - getcash() as expected!! :-)

    Cheers!



  • @cwse Hi CWE,

    Can you please let me know what was changed compared to the code in your original post?

    Thanks and Cheers,
    Tamás



  • @holicst I upgraded my backtrader version, and used the logic provided in the above example @backtrader posted



  • @cwse all right, thanks!


Log in to reply
 

Looks like your connection to Backtrader Community was lost, please wait while we try to reconnect.