Exemple #1
0
def TrendFollowStrategy(contract,
                        data_csv='',
                        data_list=[],
                        **strategy_params):
    """
    Run a trend following strategy on either the data_csv file
    or the data_list list, if both are passed, I will return error.
    Trading parameters:
    net_change:         how much does today's price have to deviate from
                        ma to consider trend to be starting
    ma_lookback_days:   how many days to build moving average over

    :param contract:
    :param data_csv: csv filename to load data from
    :param data_list: list to load data from
    :param strategy_parmas: dictionary of trading parameters
    :return: (error/success code, list of trade information)
    """

    trades = []
    log_level = strategy_params.pop('log_level', 0)

    if not data_csv and not data_list:
        if log_level > 0:
            print('Error neither have datafile nor datalist')
        return -1, None

    if data_csv and data_list:
        if log_level > 0:
            print('Error cannot have both datafile and datalist')
        return -1, None

    # get an iterable based on arguments passed
    market_data = data_list or open(data_csv, 'r')
    if log_level > 0:
        print('INFO opened data file/list ', market_data)

    # dump out trading parameters
    if log_level > 0:
        print('INFO trading params ', strategy_params)

    # pull out parameters, use defaults if missing
    ma_lookback_days = int(strategy_params.pop('ma_lookback_days', 10))
    o_loss_ticks = float(strategy_params.pop('loss_ticks', 5.0))
    o_net_change = float(strategy_params.pop('net_change', 5.0))
    risk_dollars = float(strategy_params.pop('risk_dollars', 1000.0))

    # define some model specific varibles
    lookback_prices = []  # maintain, update ma
    my_position, my_vwap, my_pnl = 0, 0, 0

    for line in reversed(list(market_data)):  # file is backwards
        try:
            # unpack list
            date, open_price, high_price, low_price, close_price = \
                fp.TokenizeToPriceInfo(contract, line)
        except ValueError or TypeError:
            continue

        lookback_prices.append([high_price, low_price, close_price])

        if len(lookback_prices) < ma_lookback_days + 1:
            # not initialized yet, push and contine
            continue

        # save and update list
        ma = statistics.mean(row[2] for row in lookback_prices)
        vol = statistics.mean((row[0] - row[1]) for row in lookback_prices)

        loss_ticks = o_loss_ticks * vol
        net_change = o_net_change * vol

        if log_level > 0:
            print('INFO vol:',
                  vol,
                  'adjusted params:',
                  'net_change:',
                  net_change,
                  'loss_ticks:',
                  loss_ticks,
                  sep=' ')

        lookback_prices.pop(0)
        dev_from_ma = close_price - ma
        if log_level > 0:
            print('INFO ma:',
                  ma,
                  'close_price:',
                  close_price,
                  'dev_from_ma:',
                  dev_from_ma,
                  sep=' ')

        traded_today = False

        if my_position == 0:  # flat, see if we want to get into a position
            # how much did today's close price deviate from moving average
            # +ve value means breaking out to the upside
            # -ve value means breaking out to the downside
            if abs(dev_from_ma) > net_change:  # trend starting
                trade_size = int((risk_dollars / contract.TickValue) /
                                 loss_ticks + 1)
                my_position = trade_size * (1 if dev_from_ma > 0 else -1)
                my_vwap = close_price
                trades.append([
                    date, ('B' if dev_from_ma > 0 else 'S'), trade_size,
                    close_price, my_position, my_pnl, vol, ma, dev_from_ma,
                    high_price, low_price
                ])
                traded_today = True

                if log_level > 0:
                    print('INFO initiating position ', trades[-1])
        else:  # have a position already, check for stop outs
            if ((my_position > 0 and my_vwap - low_price > loss_ticks) or
                (my_position < 0 and high_price - my_vwap > loss_ticks)):
                stopout_price = my_vwap + (loss_ticks *
                                           (1 if my_position < 0 else -1))
                trade_pnl = abs(my_position) * loss_ticks * contract.TickValue
                my_pnl -= trade_pnl
                buysell = ('S' if my_position > 0 else 'B')
                trades.append([
                    date, buysell,
                    abs(my_position), stopout_price, 0, my_pnl, vol, ma,
                    dev_from_ma, high_price, low_price
                ])
                traded_today = True
                my_position = 0
                if log_level > 0:
                    print('INFO stopped out ', trades[-1])
            elif abs(dev_from_ma) < 0.5 * net_change:  # trend dying out
                stopout_price = close_price
                trade_pnl = my_position * (stopout_price -
                                           my_vwap) * contract.TickValue
                my_pnl += trade_pnl
                buysell = ('S' if my_position > 0 else 'B')
                trades.append([
                    date, buysell,
                    abs(my_position), stopout_price, 0, my_pnl, vol, ma,
                    dev_from_ma, high_price, low_price
                ])
                traded_today = True
                my_position = 0

                if log_level > 0:
                    print('INFO took a win ', trades[-1])

        # add an empty line if no trades were made today otherwise you'll see gaps in plots
        if not traded_today:
            unreal_pnl = my_position * (close_price -
                                        my_vwap) * contract.TickValue
            trades.append([
                date, '-', 0, close_price, my_position, my_pnl + unreal_pnl,
                vol, ma, dev_from_ma, high_price, low_price
            ])
    return 0, trades
Exemple #2
0
    def OnMarketDataUpdate(self, shc, date, line, risk_dollars):
        Trader.OnMarketDataUpdate(self, shc, date, line, risk_dollars)

        contract_index = (1 if shc == self.contracts[1] else 0)
        self.market_data[contract_index] = line
        if not all(self.market_data):
            return

        contracts = [
            ci.ContractInfoDatabase[self.contracts[0]],
            ci.ContractInfoDatabase[self.contracts[1]]
        ]
        contracts.append(
            ci.ContractInfo(contracts[0].Name + ' VS. ' + contracts[1].Name,
                            0.01, 10))

        date, open_price, high_price, low_price, close_price = \
            [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]
        try:
            # unpack list
            for index in [0, 1]:
                date[index], open_price[index], high_price[index], low_price[index], close_price[index] = \
                    fp.TokenizeToPriceInfo(contracts[index], self.market_data[index])
        except ValueError or TypeError:
            return

        if dt.CompareDates(date[0], date[1]) != 0:
            return
        # print(str(self.market_data))

        for index in [0, 1]:
            self.lookback_prices[index].append(
                [high_price[index], low_price[index], close_price[index]])

        if len(self.lookback_prices[0]) < self.ma_lookback_days + 1:
            # not initialized yet, push and continue
            return

        # save ma and update list
        # print(list(row[2] for row in lookback_prices))
        ma, vol, dollar_vol, weight = [0, 0, 0], [0, 0, 0], [0, 0,
                                                             0], [1, 1, 1]
        for index in [0, 1]:
            ma[index] = statistics.mean(row[2]
                                        for row in self.lookback_prices[index])
            vol[index] = statistics.mean(
                abs(row[0] - row[1]) for row in self.lookback_prices[index])
            dollar_vol[index] = vol[index] * contracts[index].TickValue

        is_inverted = False  # maintain a flag if we invert ratio
        ratio = dollar_vol[0] / dollar_vol[1]
        if ratio < 1:
            ratio = 1 / ratio
            is_inverted = True

        high_price[2] = self.ComputeSpreadPrice(ratio, is_inverted,
                                                high_price[0], high_price[1])
        low_price[2] = self.ComputeSpreadPrice(ratio, is_inverted,
                                               low_price[0], low_price[1])
        close_price[2] = self.ComputeSpreadPrice(ratio, is_inverted,
                                                 close_price[0],
                                                 close_price[1])
        self.lookback_prices[2].append(
            [high_price[2], low_price[2], close_price[2]])
        ma[2] = statistics.mean(row[2] for row in self.lookback_prices[2])
        vol[2] = statistics.mean(
            abs(row[0] - row[1]) for row in self.lookback_prices[2])
        dev_from_ma = close_price[2] - ma[2]

        if self.log_level > 0:
            print('INFO vol ', vol, ' dollar vol ', dollar_vol, ' ratio ',
                  ratio)

        for index in [0, 1, 2]:
            if len(self.lookback_prices[index]) >= self.ma_lookback_days:
                self.lookback_prices[index].pop(0)

        loss_ticks = self.o_loss_ticks * vol[2]
        net_change = self.o_net_change * vol[2]

        # this is a tough one, and this solution is imperfect
        # but preferred for its simplicity
        spread_tick_value = min(contracts[0].TickValue,
                                contracts[1].TickValue * ratio)
        if is_inverted:
            spread_tick_value = min(contracts[0].TickValue * ratio,
                                    contracts[1].TickValue)

        if self.log_level > 0:
            print('INFO vol:',
                  vol,
                  'adjusted params:',
                  'net_change:',
                  net_change,
                  'loss_ticks:',
                  loss_ticks,
                  'spread_tick_value:',
                  spread_tick_value,
                  sep=' ')

        traded_today = False

        if self.my_position[
                2] == 0:  # flat, see if we want to get into a position
            # how much did today's close price deviate from moving average ?
            # +ve value means breaking out to the upside
            # -ve value means breaking out to the downside
            if abs(dev_from_ma) > net_change:  # blown out
                trade_size = int((risk_dollars / spread_tick_value) /
                                 loss_ticks + 1)
                self.my_position[0] = trade_size * (ratio
                                                    if is_inverted else 1)
                self.my_position[1] = trade_size * (1
                                                    if is_inverted else ratio)
                self.my_vwap = list(close_price)
                self.my_position[2] = trade_size * (1
                                                    if dev_from_ma < 0 else -1)
                self.my_vwap[2] = close_price[2]
                self.trades.append([
                    date[0], ('B' if dev_from_ma < 0 else 'S'), trade_size,
                    close_price[2], self.my_position[2], self.my_pnl[2],
                    vol[2], ma[2], dev_from_ma, high_price[2], low_price[2]
                ])
                traded_today = True

                if self.log_level > 0:
                    print('INFO initiating position ', self.trades[-1])
        else:  # have a position already, check for stop outs
            if ((self.my_position[2] > 0
                 and self.my_vwap[2] - low_price[2] > loss_ticks)
                    or (self.my_position[2] < 0
                        and high_price[2] - self.my_vwap[2] > loss_ticks)):
                stopout_price = self.my_vwap[2] + \
                                (loss_ticks * (1 if self.my_position[2] < 0 else -1))
                trade_pnl = abs(
                    self.my_position[2]) * loss_ticks * spread_tick_value
                self.my_pnl[2] -= trade_pnl
                buysell = ('S' if self.my_position[2] > 0 else 'B')

                self.trades.append([
                    date[0], buysell,
                    abs(self.my_position[2]), stopout_price, 0, self.my_pnl[2],
                    vol[2], ma[2], dev_from_ma, high_price[2], low_price[2]
                ])
                traded_today = True
                self.my_position = [0, 0, 0]

                if self.log_level > 0:
                    print('INFO stopped out ', self.trades[-1])
            elif abs(dev_from_ma) < 0.5 * net_change:  # trend dying out
                self.my_pnl[0] += self.my_position[0] * (
                    close_price[0] - self.my_vwap[0]) * contracts[0].TickValue
                self.my_pnl[1] += self.my_position[1] * (
                    close_price[1] - self.my_vwap[1]) * contracts[1].TickValue
                self.my_pnl[2] = self.my_pnl[0] + self.my_pnl[1]
                buysell = ('S' if self.my_position[2] > 0 else 'B')

                self.trades.append([
                    date[0], buysell,
                    abs(self.my_position[2]), close_price[2], 0,
                    self.my_pnl[2], vol[2], ma[2], dev_from_ma, high_price[2],
                    low_price[2]
                ])
                traded_today = True
                self.my_position = [0, 0, 0]

                if self.log_level > 0:
                    print('INFO took a win ', self.trades[-1])

        if not traded_today:
            unreal_pnl = self.my_position[0] * (close_price[0] - self.my_vwap[0]) * contracts[0].TickValue + \
                         self.my_position[1] * (close_price[1] - self.my_vwap[1]) * contracts[1].TickValue
            self.trades.append([
                date[0], '-', self.my_position[2], close_price[2], 0,
                self.my_pnl[2] + unreal_pnl, vol[2], ma[2], dev_from_ma,
                high_price[2], low_price[2]
            ])

        self.alloc.append(risk_dollars)
        if len(self.trades) >= 2:
            self.daily_pnl.append(self.trades[-1][5] - self.trades[-2][5])
            if self.trades[-2][5] != 0:
                self.pct_pnl_change.append(
                    100 * self.daily_pnl[-1] /
                    abs(self.trades[-2][5]))  # what % pnl increase?
        else:
            self.daily_pnl.append(self.trades[-1][5])
Exemple #3
0
    def OnMarketDataUpdate(self, shc, date, line, risk_dollars):
        Trader.OnMarketDataUpdate(self, shc, date, line, risk_dollars)

        contract_index = (1 if shc == self.contracts[1] else 0)
        self.market_data[contract_index] = line
        if not all(self.market_data):
            return

        contracts = [
            ci.ContractInfoDatabase[self.contracts[0]],
            ci.ContractInfoDatabase[self.contracts[1]]
        ]

        date, open_price, high_price, low_price, close_price = \
            [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]
        try:
            # unpack list
            for index in [0, 1]:
                date[index], open_price[index], high_price[index], low_price[index], close_price[index] = \
                    fp.TokenizeToPriceInfo(contracts[index], self.market_data[index])
        except ValueError or TypeError:
            return

        if dt.CompareDates(date[0], date[1]) != 0:
            return
        # print(str(self.market_data))

        for index in [0, 1]:
            self.lookback_prices[index].append(
                [high_price[index], low_price[index], close_price[index]])

        if len(self.lookback_prices[0]) < self.ma_lookback_days + 1:
            # not initialized yet, push and continue
            return

        # save ma and update list
        # print(list(row[2] for row in lookback_prices))
        ma, vol, dev_from_ma = [0, 0], [0, 0], [0, 0]
        for index in [0, 1]:
            ma[index] = statistics.mean(row[2]
                                        for row in self.lookback_prices[index])
            vol[index] = statistics.mean(
                abs(row[0] - row[1]) for row in self.lookback_prices[index])
            dev_from_ma[index] = close_price[index] - ma[index]
            self.lookback_dev_from_ma[index].append(dev_from_ma[index])

        # Need at least 2 points, not enough degrees of freedom
        if len(self.lookback_dev_from_ma[0]) < 2:
            return

        corr = numpy.corrcoef(self.lookback_dev_from_ma[0],
                              self.lookback_dev_from_ma[1])
        cov = numpy.cov(self.lookback_dev_from_ma[0],
                        self.lookback_dev_from_ma[1])
        corr_0_1 = corr[0, 1]  # get the correlation between the 2 series

        # get the strength of the moves
        # this holds the answer to 'for every 1 unit move in B, how much should A move'
        # we will use this to predict expected moves and then use the difference
        # with actual move to accumulate positions
        cov_0_1 = cov[0, 0] / cov[0, 1]

        # project what the price-change should be
        # this is designed so that for weaker correlations, projections are dampened
        projected_dev_from_ma = dev_from_ma[1] * cov_0_1
        projected_price = ma[0] + projected_dev_from_ma
        dev_from_projection = projected_dev_from_ma * abs(
            corr_0_1) - dev_from_ma[0]
        if math.isnan(dev_from_projection):
            if self.log_level > 0:
                print('Skipping because dev_from_projection:',
                      dev_from_projection, 'or correlation:', corr_0_1,
                      'less than', self.min_correlation)
            return

        # track it so we know how big an average deviation is
        self.lookback_dev_from_projection.append(
            dev_from_projection)  # this measure only cares about the magnitude
        dev_from_projection_vol = statistics.mean(
            abs(item) for item in self.lookback_dev_from_projection)

        if self.log_level > 0:
            print('dev_from_projection',
                  dev_from_projection,
                  'dev_from_projection_vol',
                  dev_from_projection_vol,
                  'entries',
                  self.lookback_dev_from_projection,
                  sep=' ')

        if len(self.lookback_dev_from_ma[0]) < self.ma_lookback_days + 1:
            # need to have a long enough history
            # of relative deviations to project in the future
            if self.log_level > 0:
                print('not enough lookback_dev_from_ma history',
                      len(self.lookback_dev_from_ma[0]),
                      self.ma_lookback_days,
                      sep=' ')
            return

        for index in [0, 1]:
            while len(self.lookback_prices[index]) > self.ma_lookback_days:
                self.lookback_prices[index].pop(0)
            while len(
                    self.lookback_dev_from_ma[index]) > self.ma_lookback_days:
                self.lookback_dev_from_ma[index].pop(0)
        while len(self.lookback_dev_from_projection) > self.ma_lookback_days:
            self.lookback_dev_from_projection.pop(0)

        if self.log_level > 0:
            print(contracts[0].Name, 'projected by', contracts[1].Name,
                  'correlation:', corr_0_1, 'coefficient:', cov_0_1)
            print('projected_dev_from_ma',
                  projected_dev_from_ma,
                  'actual',
                  dev_from_ma[0],
                  'dev_from_projection',
                  dev_from_projection,
                  sep=' ')

        loss_ticks = self.o_loss_ticks * vol[0]
        net_change = self.o_net_change * dev_from_projection_vol

        if self.log_level > 0:
            print('INFO vol:',
                  vol,
                  'adjusted params:',
                  'net_change:',
                  net_change,
                  'loss_ticks:',
                  loss_ticks,
                  sep=' ')

        traded_today = False

        if self.my_position == 0:  # flat, see if we want to get into a position
            # how much did today's close price deviate from moving average ?
            # +ve value means breaking out to the upside
            # -ve value means breaking out to the downside
            if abs(dev_from_projection) > net_change:  # blown out
                trade_size = int((risk_dollars / contracts[0].TickValue) /
                                 loss_ticks + 1)
                self.my_position = trade_size * (1 if dev_from_projection > 0
                                                 else -1)
                self.my_vwap = close_price[0]
                self.trades.append([
                    date[0], ('B' if dev_from_projection > 0 else 'S'),
                    trade_size, close_price[0], self.my_position, self.my_pnl,
                    dev_from_projection_vol, ma[0], dev_from_projection,
                    projected_price, corr_0_1
                ])
                traded_today = True

                if self.log_level > 0:
                    print('INFO initiating position ', self.trades[-1])
        else:  # have a position already, check for stop outs
            if ((self.my_position > 0
                 and self.my_vwap - low_price[0] > loss_ticks)
                    or (self.my_position < 0
                        and high_price[0] - self.my_vwap > loss_ticks)):
                stopout_price = self.my_vwap + \
                                (loss_ticks * (1 if self.my_position < 0 else -1))
                trade_pnl = abs(
                    self.my_position) * loss_ticks * contracts[0].TickValue
                self.my_pnl -= trade_pnl
                buysell = ('S' if self.my_position > 0 else 'B')

                self.trades.append([
                    date[0], buysell,
                    abs(self.my_position), stopout_price, 0, self.my_pnl,
                    dev_from_projection_vol, ma[0], dev_from_projection,
                    projected_price, corr_0_1
                ])
                traded_today = True
                self.my_position = 0

                if self.log_level > 0:
                    print('INFO stopped out ', self.trades[-1])
            elif abs(dev_from_projection
                     ) < 0.5 * net_change:  # deviation dying out
                stopout_price = close_price[0]
                trade_pnl = self.my_position * (
                    stopout_price - self.my_vwap) * contracts[0].TickValue
                self.my_pnl += trade_pnl
                buysell = ('S' if self.my_position > 0 else 'B')

                self.trades.append([
                    date[0], buysell,
                    abs(self.my_position), stopout_price, 0, self.my_pnl,
                    vol[0], ma[0], dev_from_projection_vol, projected_price,
                    corr_0_1
                ])
                traded_today = True
                self.my_position = 0

                if self.log_level > 0:
                    print('INFO took a win ', self.trades[-1])

        if not traded_today:
            unreal_pnl = self.my_position * (
                close_price[0] - self.my_vwap) * contracts[0].TickValue
            self.trades.append([
                date[0], '-', self.my_position, close_price[0], 0,
                self.my_pnl + unreal_pnl, dev_from_projection_vol, ma[0],
                dev_from_projection, projected_price, corr_0_1
            ])

        self.alloc.append(risk_dollars)
        if len(self.trades) >= 2:
            self.daily_pnl.append(self.trades[-1][5] - self.trades[-2][5])
            if self.trades[-2][5] != 0:
                self.pct_pnl_change.append(
                    100 * self.daily_pnl[-1] /
                    abs(self.trades[-2][5]))  # what % pnl increase?
        else:
            self.daily_pnl.append(self.trades[-1][5])

        if self.log_level > 0:
            print('TRADE ' + str(self.ShortName()) + ' ' +
                  str(self.trades[-1]))
            print('ALLOC ' + str(self.ShortName()) + ' ' + str(self.alloc[-1]))
Exemple #4
0
    def OnMarketDataUpdate(self, shc, date, line, risk_dollars):
        Trader.OnMarketDataUpdate(self, shc, date, line, risk_dollars)

        contract = Trader.ShcToContract(self, shc)
        try:
            # unpack list
            date, open_price, high_price, low_price, close_price = \
                fp.TokenizeToPriceInfo(contract, line)
        except ValueError or TypeError:
            return

        self.lookback_prices.append([high_price, low_price, close_price])

        if len(self.lookback_prices) < self.ma_lookback_days + 1:
            # not initialized yet, push and continue
            return

        # save ma and update list
        # print(list(row[2] for row in lookback_prices))
        ma = statistics.mean(row[2] for row in self.lookback_prices)
        vol = statistics.mean(
            (row[0] - row[1]) for row in self.lookback_prices)

        loss_ticks = self.o_loss_ticks * vol
        net_change = self.o_net_change * vol
        if self.log_level > 0:
            print('INFO vol:',
                  vol,
                  'adjusted params:',
                  'net_change:',
                  net_change,
                  'loss_ticks:',
                  loss_ticks,
                  sep=' ')

        self.lookback_prices.pop(0)
        dev_from_ma = close_price - ma
        if self.log_level > 0:
            print('INFO ma:',
                  ma,
                  'close_price:',
                  close_price,
                  'dev_from_ma:',
                  dev_from_ma,
                  sep=' ')

        traded_today = False

        if self.my_position == 0:  # flat, see if we want to get into a position
            # how much did today's close price deviate from moving average ?
            # +ve value means breaking out to the upside
            # -ve value means breaking out to the downside
            if abs(dev_from_ma) > net_change:  # blown out
                trade_size = int((risk_dollars / contract.TickValue) /
                                 loss_ticks + 1)
                self.my_position = trade_size * (1 if dev_from_ma < 0 else -1)
                self.my_vwap = close_price
                self.trades.append([
                    date, ('B' if dev_from_ma < 0 else 'S'), trade_size,
                    close_price, self.my_position, self.my_pnl, vol, ma,
                    dev_from_ma, high_price, low_price
                ])
                traded_today = True

                if self.log_level > 0:
                    print('INFO initiating position ', self.trades[-1])
        else:  # have a position already, check for stop outs
            if ((self.my_position > 0
                 and self.my_vwap - low_price > loss_ticks)
                    or (self.my_position < 0
                        and high_price - self.my_vwap > loss_ticks)):
                stopout_price = self.my_vwap + \
                                (loss_ticks * (1 if self.my_position < 0 else -1))
                trade_pnl = abs(
                    self.my_position) * loss_ticks * contract.TickValue
                self.my_pnl -= trade_pnl
                buysell = ('S' if self.my_position > 0 else 'B')

                self.trades.append([
                    date, buysell,
                    abs(self.my_position), stopout_price, 0, self.my_pnl, vol,
                    ma, dev_from_ma, high_price, low_price
                ])
                traded_today = True
                self.my_position = 0

                if self.log_level > 0:
                    print('INFO stopped out ', self.trades[-1])
            elif abs(dev_from_ma) < 0.5 * net_change:  # trend dying out
                stopout_price = close_price
                trade_pnl = self.my_position * (
                    stopout_price - self.my_vwap) * contract.TickValue
                self.my_pnl += trade_pnl
                buysell = ('S' if self.my_position > 0 else 'B')

                self.trades.append([
                    date, buysell,
                    abs(self.my_position), stopout_price, 0, self.my_pnl, vol,
                    ma, dev_from_ma, high_price, low_price
                ])
                traded_today = True
                self.my_position = 0

                if self.log_level > 0:
                    print('INFO took a win ', self.trades[-1])

        if not traded_today:
            unreal_pnl = self.my_position * (close_price -
                                             self.my_vwap) * contract.TickValue
            self.trades.append([
                date, '-', 0, close_price, self.my_position,
                self.my_pnl + unreal_pnl, vol, ma, dev_from_ma, high_price,
                low_price
            ])

        self.alloc.append(risk_dollars)
        if len(self.trades) >= 2:
            self.daily_pnl.append(self.trades[-1][5] - self.trades[-2][5])
            if self.trades[-2][5] != 0:
                self.pct_pnl_change.append(
                    100 * self.daily_pnl[-1] /
                    abs(self.trades[-2][5]))  # what % pnl increase?
        else:
            self.daily_pnl.append(self.trades[-1][5])
def PairsReversionStrategy(contracts, data_csv=[], data_list=[], **strategy_params):
    """
    Run a mean reversion strategy on either the data_csv file
    or the data_list list, if both are passed, it will return error

    Trading parameters:
    net_change:         how much does today's price have to deviate from
                        ma to consider trend to be starting
    ma_lookback_days:   how many days to build moving average over
    loss_ticks:         where to stop out on a losing position

    :param contracts:
    :param data_csv: csv filename to load data from
    :param data_list: list to load data from
    :param strategy_params: dictionary of trading parameters
    :return: (error/successs code, list of trade information)
    """
    trades = []
    log_level = strategy_params.pop('log_level', 0)

    if not data_csv and not data_list:
        if log_level > 0:
            print('Error neither have datafile nor datalist')
        return -1, None, None

    if data_csv and data_list:
        if log_level > 0:
            print("Error cannot have both datafile and datalist")
        return -1, None, None

    # get an iterable based on arguments passed
    market_data_1 = data_list or open(data_csv[0], 'r')
    market_data_2 = data_list or open(data_csv[1], 'r')
    if log_level > 0:
        print('INFO opened data file/list ', market_data_1, ' and ', market_data_2)

    # dump out trading parameters
    if log_level > 0:
        print('INFO trading params ', strategy_params)

    # pull out parameters, use defaults if missing
    ma_lookback_days = int(strategy_params.pop('ma_lookback_days', 10))
    o_loss_ticks = float(strategy_params.pop('loss_ticks', 5.0))
    o_net_change = float(strategy_params.pop('net_change', 5.0))
    risk_dollars = float(strategy_params.pop('risk_dollars', 1000.0))

    # define some model specific variables
    lookback_prices = [[], [], []] # maintain, update ma
    my_position, my_vwap, my_pnl = [0,0,0], [0,0,0], [0,0,0] # position, position vwap, pnl
    market_data = [list(reversed(list(market_data_1))), list(reversed(list(market_data_2)))]
    market_data_index = [0, 0]

    syn_contract = ci.ContractInfo(contracts[0].Name + 'VS. ' + contracts[1].Name, 0.01, 10)

    while market_data_index[0] < len(market_data[0]) and market_data_index[1] < len(market_data[1]):
        date, open_price, high_price, low_price, close_price = [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]
        try:
            if log_level > 0:
                print('INFO looking at index: ', market_data_index[0], '/',
                      len(market_data[0]), ' ', market_data_index[1], '/', len(market_data[1]))

            # unpack list
            for index in [0, 1]:
                date[index], open_price[index], high_price[index], low_price[index], close_price[index] =\
                    fp.TokenizeToPriceInfo(contracts[index], market_data[index][market_data_index[index]])
        except ValueError or TypeError:
            # update indices
            for index in [0,1]:
                market_data_index[index] += 1
            continue

        # sanity check to make sure we are looking at same day on both contracts
        if date[0] != date[1]:
            # need to figure out which contract is lagging and bring that upto speed
            if du.CompareDates(date[0], date[1]) < 0:
                market_data_index[0] += 1
            else:
                market_data_index[1] += 1
            continue

        for index in [0, 1]:
            market_data_index[index] += 1
            lookback_prices[index].append(high_price[index], low_price[index], close_price[index])

        if len(lookback_prices[0]) < ma_lookback_days + 1:
            # not initialized yet, push and continue
            continue

        # save ma and update list
        ma, vol , dollar_vol, weight = [0, 0, 0], [0, 0, 0], [0, 0, 0], [1, 1, 1]
        for index in [0, 1]:
            ma[index] = statistics.mean(row[2] for row in lookback_prices[index])
            vol[index] = statistics.mean(abs(row[0] - row[1]) for row in lookback_prices[index])
            dollar_vol[index] = vol[index] * contracts[index].TickValue

        is_inverted = False # maintain a flag if we invert ratio
        ratio = dollar_vol[0] / dollar_vol[1]

        if ratio < 1:
            ratio = 1 / ratio
            is_inverted = True

        high_price[2] = ComputeSpreadPrice(ratio, is_inverted, high_price[0], high_price[1])
        low_price[2] = ComputeSpreadPrice(ratio, is_inverted, low_price[0], low_price[1])
        close_price[2] = ComputeSpreadPrice(ratio, is_inverted, close_price[0], close_price[1])
        lookback_prices[2].append(high_price[2], low_price[2], close_price[2])
        ma[2] = statistics.mean(row[2] for row in lookback_prices[2])
        vol[2] = statistics.mean(abs(row[0] - row[1]) for row in lookback_prices[2])
        dev_from_ma = close_price[2] - ma[2]

        if log_level > 0:
            print('INFO vol ', vol, ' dollar vol ', dollar_vol, ' ratio ', ratio)

        for index in [0, 1, 2]:
            if len(lookback_prices[index]) >= ma_lookback_days:
                lookback_prices[index].pop(0)

        loss_ticks = o_loss_ticks * vol[2]
        net_change = o_net_change * vol[2]

        # this is a tough one and this solution is imperfect
        # but preferred for its simplicity
        spread_tick_value = min(contracts[0].TickValue, contracts[1].TickValue * ratio)
        if is_inverted:
            spread_tick_value = min(contracts[0].TickValue * ratio, contracts[1].TickValue)

        if log_level > 0:
            print('INFO vol:', vol, 'adjusted params:', 'net_change:', net_change, 'loss_ticks:', loss_ticks,
                  'spread_tick_value:', spread_tick_value, sep=' ')

        traded_today = False

        if my_position[2] == 0: # flat, see if we want to get into a position
            # how much did today's close price deviate from moving average
            # +ve value means breaking out to the upside
            # -ve value means breaking out to the downside
            if abs(dev_from_ma) > net_change: # blown out
                trade_size = int((risk_dollars / spread_tick_value) / loss_ticks + 1)
                my_position[0] = trade_size * (ratio if is_inverted else 1)
                my_position[1] = trade_size * (1 if is_inverted else ratio)
                my_vwap = list(close_price)
                my_position[2] = trade_size * (1 if dev_from_ma < 0 else -1)
                my_vwap[2] = close_price[2]
                trades.append([date[0], ('B' if dev_from_ma < 0 else 'S'), trade_size, close_price[2],
                               my_position[2], my_pnl[2], vol[2], ma[2], dev_from_ma, high_price[2], low_price[2]])
                traded_today = True

            if log_level > 0:
                print('INFO initiating position ', trades[-1])
        else: # have a position already, check for stop outs
            if ((my_position[2] > 0 and my_vwap[2] - low_price[2] > loss_ticks) or
                    (my_position[2] < 0 and high_price[2] - my_vwap[2] > loss_ticks)):
                stopout_price = my_vwap[2] + (loss_ticks * (1 if my_position[2] < 0 else -1))
                trade_pnl = abs(my_position[2] * loss_ticks * spread_tick_value)
                my_pnl[2] -= trade_pnl
                buysell = ('S' if my_position[2] > 0 else 'B')

                traded_today = True
                trades.append([date[0], buysell, abs(my_position[2]), stopout_price, 0, my_pnl[2], vol[2], ma[2],
                               dev_from_ma, high_price[2], low_price[2]])
                my_position = [0, 0, 0]

                if log_level > 0:
                    print('INFO stopped out ', trades[-1])
            elif abs(dev_from_ma) < 0.5 * net_change: # trend dying out
                my_pnl[0] += my_position[0] * (close_price[0] - my_vwap[0]) * contracts[0].TickValue
                my_pnl[1] += my_position[1] * (close_price[1] - my_vwap[1]) * contracts[1].TickValue
                my_pnl[2] = my_pnl[0] + my_pnl[1]
                buysell = ('S' if my_position[2] > 0 else 'B')

                trades.append(
                    [date[0], buysell, abs(my_position[2]), close_price[2], 0, my_pnl[2], vol[2], ma[2], dev_from_ma,
                     high_price[2], low_price[2]])
                traded_today = True
                my_position = [0, 0, 0]

                if log_level > 0:
                    print('INFO took a win ', trades[-1])

        if not traded_today:
            unreal_pnl = my_position[0] * (close_price[0] - my_vwap[0]) * contracts[0].TickValue + \
                         my_position[1] * (close_price[1] - my_vwap[1]) * contracts[1].TickValue

            trades.append(
                [date[0], '-', my_position[2], close_price[2], 0, my_pnl[2] + unreal_pnl, vol[2], ma[2], dev_from_ma,
                 high_price[2], low_price[2]])

    return 0, syn_contract, trades
def StatArbStrategy(contracts, data_csv=[], data_list=[], **strategy_params):
    """
    Trade contract[0] using contract[1] as leading indicator

    Trading parameters:
    net_change:         how much does today's price have to deviate from
                        ma to consider trend to be starting
    ma_lookback_days:   how many days to build moving average over
    loss_ticks:         where to stop out on a losing position
    :param contracts:
    :param data_csv: csv filename to load data from
    :param data_list: list to load data from
    :param strategy_params: dictionary of trading parameters
    :return: (error/successs code, list of trade information)
    """
    trades = []
    log_level = strategy_params.pop('log_level', 0)

    if not data_csv and not data_list:
        if log_level > 0:
            print('Error neither have datafile nor datalist')
        return -1, None, None

    if data_csv and data_list:
        if log_level > 0:
            print("Error cannot have both datafile and datalist")
        return -1, None, None

    # get an iterable based on arguments passed
    market_data_1 = data_list or open(data_csv[0], 'r')
    market_data_2 = data_list or open(data_csv[1], 'r')
    if log_level > 0:
        print('INFO opened data file/list ', market_data_1, ' and ', market_data_2)

    # dump out trading parameters
    if log_level > 0:
        print('INFO trading params ', strategy_params)

    # pull out parameters, use defaults if missing
    ma_lookback_days = int(strategy_params.pop('ma_lookback_days', 10))
    o_loss_ticks = float(strategy_params.pop('loss_ticks', 5.0))
    o_net_change = float(strategy_params.pop('net_change', 5.0))
    risk_dollars = float(strategy_params.pop('risk_dollars', 1000.0))
    min_correlation = float(strategy_params.pop('min_correlation', 0.75))

    # define some model specific variables
    lookback_prices = [[], []] # maintain, update ma
    lookback_dev_from_ma = [[], []]
    lookback_dev_from_projection = []
    my_position, my_vwap, my_pnl = 0, 0, 0 # position, position vwap, pnl

    market_data = [list(reversed(list(market_data_1))), list(reversed(list(market_data_2)))]
    market_data_index = [0, 0]

    while market_data_index[0] < len(market_data[0]) and market_data_index[1] < len(market_data[1]):
        date, open_price, high_price, low_price, close_price = [0, 0], [0, 0], [0, 0], [0, 0], [0, 0]
        try:
            if log_level > 0:
                print('INFO looking at index: ', market_data_index[0], '/',
                      len(market_data[0]), ' ', market_data_index[1], '/', len(market_data[1]))

            # unpack list
            for index in [0, 1]:
                date[index], open_price[index], high_price[index], low_price[index], close_price[index] = \
                    fp.TokenizeToPriceInfo(contracts[index], market_data[index][market_data_index[index]])
        except ValueError or TypeError:
            # update indices
            for index in [0, 1]:
                market_data_index[index] += 1
            continue

        # sanity check to make sure we are looking at same day on both contracts
        if date[0] != date[1]:
            # need to figure out which contract is lagging and bring that upto speed
            if du.CompareDates(date[0], date[1]) < 0:
                market_data_index[0] += 1
            else:
                market_data_index[1] += 1
            continue

        for index in [0, 1]:
            market_data_index[index] += 1
            lookback_prices[index].append(high_price[index], low_price[index], close_price[index])

        if len(lookback_prices[0]) < ma_lookback_days + 1:
            # not initialized yet, push and continue
            continue

        # save ma and update list
        ma, vol, dev_from_ma = [0, 0], [0, 0], [0, 0]
        for index in [0, 1]:
            ma[index] = statistics.mean(row[2] for row in lookback_prices[index])
            vol[index] = statistics.mean(abs(row[0] - row[1]) for row in lookback_prices[index])
            dev_from_ma[index] = close_price[index] - ma[index]
            lookback_dev_from_ma[index].append(dev_from_ma[index])

        # Need at least 2 points, not enough degrees of freedom
        if len(lookback_dev_from_ma[0]) < 2:
            continue

        corr = numpy.corrcoef(lookback_dev_from_ma[0], lookback_dev_from_ma[1])
        cov = numpy.cov(lookback_dev_from_ma[0], lookback_dev_from_ma[1])
        corr_0_1 = corr[0, 1] # get the correlation between the 2 series

        # get the strength of the moves
        # this holds the answer to 'for every 1 unit move in B, how much should A move'
        # we will use this to predict expected moves and then use the difference
        # with actual move to accumulate positions
        cov_0_1 = cov[0, 0] / cov[0, 1]

        # project what the price-change should be
        # this is designed so that for weaker correlations, projections are dampened
        projected_dev_from_ma = dev_from_ma[1] * cov_0_1
        projected_price = ma[0] + projected_dev_from_ma
        dev_from_projection = projected_dev_from_ma * abs(corr_0_1) - dev_from_ma[0]
        if math.isnan(dev_from_projection) or corr_0_1 < min_correlation:
            if log_level > 0:
                print('Skipping because dev_from_projection', dev_from_projection,
                      'or correlation:', corr_0_1, 'less than', min_correlation)
            continue

        # track it so we know how big an average deviation is
        lookback_dev_from_projection.append(dev_from_projection) # this measure only cares about the magnitude
        dev_from_projection_vol = statistics.mean(abs(item) for item in lookback_dev_from_projection)

        if log_level > 0:
            print('dev_from_projection', dev_from_projection, 'dev_from_projection_vol',
                  dev_from_projection_vol, 'entries', lookback_dev_from_projection, sep=' ')

        if len(lookback_dev_from_ma[0]) < ma_lookback_days + 1:
            # need to have a long enough history
            # or relative deviations to project in the future
            if log_level > 0:
                print('not enough lookback_dev_from_ma history', len(lookback_dev_from_ma[0]), ma_lookback_days,
                      sep=' ')
            continue

        for index in [0, 1]:
            while len(lookback_prices[index]) > ma_lookback_days:
                lookback_prices[index].pop(0)
            while len(lookback_dev_from_ma[index]) > ma_lookback_days:
                lookback_dev_from_ma[index].pop(0)
        while len(lookback_dev_from_projection) > ma_lookback_days:
            lookback_dev_from_projection.pop(0)

        if log_level > 0:
            print(contracts[0].Name, 'projected by', contracts[1].Name,
                  'correlation', corr_0_1, 'coefficient:', cov_0_1)
            print('projected_dev_from_ma', projected_dev_from_ma, 'actual',dev_from_ma[0],
                  'dev_from_projection', dev_from_projection, sep=' ')

        loss_ticks = o_loss_ticks * vol[0]
        net_change = o_net_change * dev_from_projection_vol

        if log_level > 0:
            print('INFO vol:', vol, 'adjusted params:', 'net_change:', net_change, 'loss_ticks:', loss_ticks,
                  sep=' ')

        traded_today = False

        if my_position == 0: # flat, see if we want to get into a position
            # how much did today's close price deviate from moving average ?
            # +ve value means breaking out to the upside
            # -ve value means breaking out to the downside
            if abs(dev_from_projection) > net_change: # blown out
                trade_size = int((risk_dollars / contracts[0].TickValue) / loss_ticks + 1)
                my_position = trade_size * (1 if dev_from_projection > 0 else -1)
                my_vwap = close_price[0]
                trades.append([date[0], ('B' if dev_from_projection > 0 else 'S'), trade_size, close_price[0],
                               my_position, my_pnl, dev_from_projection_vol, ma[0], dev_from_projection, projected_price, corr_0_1])
                traded_today = True

                if log_level > 0:
                    print('INFO initiating position ', trades[-1])

        else:  # have a position already, check for stop outs
            if ((my_position > 0 and my_vwap - low_price[0] > loss_ticks) or
                    (my_position < 0 and high_price[0] - my_vwap > loss_ticks)):
                stopout_price = my_vwap + loss_ticks * (1 if my_position < 0 else -1)
                trade_pnl = abs(my_position) * loss_ticks * contracts[0].TickValue
                my_pnl -= trade_pnl
                buysell = ('S' if my_position > 0 else 'B')

                trades.append(
                    [date[0], buysell, abs(my_position), stopout_price, 0, my_pnl, dev_from_projection_vol, ma[0],
                     dev_from_projection, projected_price, corr_0_1])
                traded_today = True
                my_position = 0

                traded_today = True
                my_position = 0

                if log_level > 0:
                    print('INFO stopped out ', trades[-1])

            elif abs(dev_from_projection) < 0.5 * net_change: # deviation dying out
                stopout_price = close_price[0]
                trade_pnl = my_position * (stopout_price - my_vwap) * contracts[0].TickValue
                my_pnl += trade_pnl
                buysell = ('S' if my_position > 0 else 'B')

                trades.append(
                    [date[0], buysell, abs(my_position), stopout_price, 0, my_pnl, vol[0], ma[0],
                     dev_from_projection_vol, projected_price, corr_0_1])
                traded_today = True
                my_position = 0

                if log_level > 0:
                    print('INFO took a win ', trades[-1])

        if not traded_today:
            unreal_pnl = my_position * (close_price[0] - my_vwap) * contracts[0].TickValue
            trades.append([date[0], '-', my_position, close_price[0], 0, my_pnl + unreal_pnl, dev_from_projection_vol, ma[0], dev_from_projection, projected_price, corr_0_1])

    return 0, trades