Multiday Position

Trying a very simple script to simulate holding a position over multiple days. Buy execution is successful but if I try to sell as "exit", order is not filled.

Even account position the day after buy shows no position.

Script is below. Any ideas? Thanks

@classmethod
def is_symbol_qualified(cls, symbol, md, service, account):
    return symbol == 'MSFT'

def on_start(self, md, order, service, account):
    self.startflg = 1
    pass

def on_minute_bar(self, event, md, order, service, account, bar):
    #Buy on 1/4/18 @ 10
    if service.time_to_string(service.system_time, '%Y-%m-%d') == '2018-01-04' and event.timestamp>service.time(hour=10,minute=0,second=0,millisecond=0) and self.startflg == 1:
        order.algo_buy(self.symbol, "market", intent="init", order_quantity=100)
        print('Buy')
        self.startflg = 2
    #Sell as "exit" on 1/5/18 @ 10
    if service.time_to_string(service.system_time, '%Y-%m-%d') == '2018-01-05' and event.timestamp>service.time(hour=10,minute=0,second=0,millisecond=0) and self.startflg == 1:
        print account[self.symbol].position
        order.algo_sell(self.symbol, "market", intent="exit")
        print('Sell')
        self.startflg = 2
    pass

Comments

  • ptunneyptunney Posts: 246
    edited February 2018

    VP

    Some things to note about the backtesting system. Whenever a backtest for any date is spun up it is its own unique instance.
    Backtests are run in one of two main ways. Parallelized or as one Single Multiday Job.
    The benefits of Parallelization are obvious, you can run lots of days all at once!
    But if you want to write a mutliday hold model, day 2 needs to know what day 1 did, therefor they have to run one at a time, in order.. much slower.
    A model that takes five minutes to run for each day, running for 30 days, across 30 parallelized backtests takes 5 minutes. The same model run mutliday takes 30x5 .. 2.5 hours.

    Class variable are maintained across the daily tests, self. variables are not.

    If you comment out your exit code and add the following print statement you can see that you are indeed still in a position on the second day...

           print self.symbol,service.time_to_string(service.system_time),"Shares",account[self.symbol].position.shares
    

    There is a really interesting dictionary called a default dict that will return a default value if a value is not found rather than erroring, this can be very useful.
    If you place it as a class variable then it will persist across the days. There are a number of ways to reference a class variable from within your code. I prefer to use the name of the class (in this case mutlidayModel)

    Here is the code I use. Run for any 5 days, it enters on the first day and exits on the fifth...

    # "RUN AS A SINGLE MULTIDAY JOB" on CQLITE
    # If you are on CQ Elite you should "ENABLE FAST SIMULATION"
    
    from cloudquant.interfaces import Strategy
    from collections import defaultdict
    
    class multidayModel(Strategy):
    
        dataStore=defaultdict(lambda: defaultdict(lambda: 0))  #lets call our DefaultDict "dataStore"
    
        @classmethod
        def is_symbol_qualified(cls, symbol, md, service, account):
            return symbol=="DLPH"
    
        def on_start(self, md, order, service, account):
            print "\n",self.symbol," Shares : ",account[self.symbol].position.shares,"  Days : ",multidayModel.dataStore[self.symbol]['inPos']
    
            # if we are in a position, add one to the day. We check the account so that if a symbol splits we can catch it.
            if account[self.symbol].position.shares!=0:
                multidayModel.dataStore[self.symbol]['inPos']=multidayModel.dataStore[self.symbol]['inPos']+1
                print self.symbol,"Day",multidayModel.dataStore[self.symbol]['inPos']
    
            # if nothing in datastore, then we are not in a position so we enter a position.            
            if multidayModel.dataStore[self.symbol]['inPos'] == 0:     
                order.algo_buy(self.symbol,"market","init",order_quantity=100)
                multidayModel.dataStore[self.symbol]['inPos'] = 1
                print "\nEntering Long ",self.symbol
    
            # if there is something in the datastore, is it a 5.. if so, lets exit and set the datastore to 999
            elif multidayModel.dataStore[self.symbol]['inPos'] == 5 :
                order.algo_sell(self.symbol,"market","exit")
                multidayModel.dataStore[self.symbol]['inPos'] = 999
                print "\nDay 5 Exiting Long ",self.symbol
    

    Hopefully this helps!

    Paul

  • ptunneyptunney Posts: 246

    other ways to reference a class variable....

    self.class.variable_name (only when the callback includes self)
    cls.variable_name (only when the callback includes cls)

    The nice thing about using the name of your class (class scriptname(Strategy): at the top of your script) is that it works across the board.

Sign In or Register to comment.