Mariner Backtesting - Advanced Write Strategy.log_data

CSV Application Overview:

Now that you know how to read and write csv files, you will learn a complex and more practical use for these methods.

What data is Logged?:

The headers will be the dictionary keys.

 self.log_data = {'EntryTime': '', 'ExitTime': '', 'EntryPrice': '', 'ExitPrice': '', 'Symbol': self.symbol, 'Profit': ''}

The body will be the data value for each header key.

  • The header is created by concatenating (adding together) the dictionary keys.
  • The header and the values are comma separated (CSV).
  • Each of the keys have unique values for every symbol.
  • Each row contains data from a different symbol.
Working Example:

Advanced write_file Aplication MoveBy Example with logging added.

# Copyright Cloudquant, LLC. All right reserved.
from cloudquant.interfaces import Strategy
import ktgfunc


class LogTradeData(Strategy):

    @classmethod
    def is_symbol_qualified(cls, symbol, md, service, account):
        # American Airlines, Walmart, Microsoft, Target
        return symbol in ["AAL", 'WMT', 'MSFT', 'TGT']

    @classmethod
    def on_strategy_start(cls, md, service, account):

        # initialize a variable that will hold the log information
        # a list of rows of data
        cls.full_log = []

        # string variable for the top line of the csv that will be created
        cls.headers = ''

    def on_start(self, md, order, service, account):

        self.log_data = {'EntryTime': '', 'ExitTime': '', 'EntryPrice': '', 'ExitPrice': '', 'Symbol': self.symbol, 'Profit': ''}

        #print symbol and timestamp
        print(self.symbol + "\n"+ service.time_to_string(service.system_time) + "\tin on start\n")

        # create an object of the MoveBy class
        # set Initial Value to the previous close
        # IsIncrease is True when looking for the price to rise
        # MoveByValue is how much you want the price to move by.
        # Setting it to .2 means the target price is 20 cents above the prev_close
        # Set IsPercent to false when MoveByValue is not a percent
        self.entry_condition= ktgfunc.MoveBy(InitialValue=md.stat.prev_close,IsIncrease=True, MoveByValue=.2, IsPercent=False)

        # initialize exit condition
        self.exit_condition = None

        # use self.entry_condition.TargetValue to access the attribute for a print statement
        print(self.symbol + " closed at " + str(md.stat.prev_close) + " yeserday.\nIf the price rises $0.20 to " + str(self.entry_condition.TargetValue) + ", a position will be entered")

        # create variable used to determine if in position.
        self.in_position=False

        # variable to track when the strategy is done
        self.complete = False

    def on_minute_bar(self, event, md, order, service, account, bar):

        if self.complete:
            service.terminate()
            return

        # on every bar the MoveBy attributes will be updated
        self.entry_condition.Calculate(md.L1.last)

        # update calculation if exit_condition has been created
        if self.exit_condition is not None:
            self.exit_condition.Calculate(md.L1.last)

        # buy if the stock has risen more than 20 cents, surpassing the target price
        # self.entry_condition.Distance() will return a negative value when it passes the target price
        if self.in_position is not True and self.entry_condition.Distance < 0:
            print '\n', service.time_to_string(event.timestamp)[11:19] + '\tin on_minute_bar'
            print 'Target price:', self.entry_condition.TargetValue, '\tLast Price:', md.L1.last
            print'[BUY]\t' + self.symbol +"'s price has increased more than $0.20 from the previous close. A buy order has been sent."

            #buy order
            order.algo_buy(self.symbol, "market", intent="init", order_quantity=100)

            # adjust variable to reflect current position
            self.in_position = True

            # return so that the below code is note executed until the next on_minute_bar
            return

        # if in a position, but exit_condition hasn't been set yet:
        if self.in_position and self.exit_condition is None:

            # set the values for the log data
            self.log_data['EntryPrice'] = account[self.symbol].position.entry_price
            # executions[0][5]; [0] is the first execution (first trade) [5] is the time
            self.log_data['EntryTime'] = account[self.symbol].executions[0][4]

            # create an object of the MoveBy class to exit a position
            # Setting it to .2 means the target price is 20 cents above the prev_close
            self.exit_condition = ktgfunc.MoveBy(InitialValue=account[self.symbol].position.entry_price, IsIncrease=True, MoveByValue=.3, IsPercent=False)
            print '\n\n', service.time_to_string(event.timestamp)[11:19] + '\tin on_minute_bar'
            print("Your position in " + self.symbol + " was entered at the price: " + str(
                account[self.symbol].position.entry_price) + ". \nIf the price rises $0.30 to " + str(
                self.exit_condition.TargetValue) + " or the price drops $0.50, the shares will be sold")

        # check that exit_condition has been created
        # sell if the stock has reached or passed the target price
        # which would mean the distance would be negative
        if self.exit_condition is not None and self.exit_condition.Distance <= 0:

            # set exit log information
            self.log_data['ExitTime'] = event.timestamp

            # sell order
            order.algo_sell(self.symbol, "market", intent="exit")

            print '\n\n', service.time_to_string(event.timestamp)[11:19] + '\tin on_minute_bar'
            print 'Target price:', self.exit_condition.TargetValue, '\tLast Price:', md.L1.last
            print "\n\n[SELL]\t" + self.symbol + " moved up at least 30 cents. A sell order has been sent"

            self.complete = True

        # check that exit_condition has been created
        # sell if the stock drops at least 20 cents from the purchase price.
        # meaning the distance from the target value would be above $0.50 from from +0.20
        elif self.exit_condition is not None and self.exit_condition.Distance > .5:

            # set exit log information
            self.log_data['ExitTime'] = event.timestamp

            # sell order
            order.algo_sell(self.symbol, "market", intent="exit")
            print '\n\n', service.time_to_string(event.timestamp)[11:19] + '\tin on_minute_bar'
            print 'Target price:', self.exit_condition.TargetValue, '\tLast Price:', md.L1.last
            print'[SELL]\t', self.symbol + "'s price decreased $0.50 cents from the target price. A sell order has been sent"

            self.complete = True

    def on_finish(self, md, order, service, account):
        # set the profit
        self.log_data['Profit'] = account[self.symbol].realized_pl.entry_pl

        # set the ExitPrice equal to the most recent execution price
        self.log_data['ExitPrice'] = account[self.symbol].executions[-1][3]

        header = ''
        body = ''

        # comma separated headers
        for key in sorted(self.log_data):
            header += ',' + key
            body += ',' + str(self.log_data[key])

        if self.__class__.headers == '':
            # dont include the leading ','
            self.__class__.headers = header[1:]

        # append the row of data to the full log
        # dont include the leading ','
        self.__class__.full_log.append(body[1:])

    @classmethod
    def on_strategy_finish(cls, md, service, account):

        # create a string of the rows of data
        body = ''
        for row in cls.full_log:
            body += row + '\n'

        # write the headers, a newline, and then the body
        # overwrite any existing text
        service.write_file('TradeInfo', cls.headers + '\n' + body, mode='overwrite')

Console

Note: Not actual console, but the user_data file TradeInfo

EntryPrice,EntryTime,ExitPrice,ExitTime,Profit,Symbol
68.0199966431,1486650905000001,68.3700027466,1486651380315000,35.0006103516,WMT
45.2700004578,1486652345006700,45.6399993896,1486653121291000,36.9998931885,AAL
65.0400009155,1486651025000001,65.3600006104,1486655040556000,31.9999694824,TGT
63.5400009155,1486651445000001,63.8499984741,1486656480869000,30.9997558594,MSFT

In the next lesson, advanced_read, the file written in this example will be read and analyzed.