Overall Statistics
Total Trades
71
Average Win
3.34%
Average Loss
-2.95%
Compounding Annual Return
-2.429%
Drawdown
18.100%
Expectancy
-0.025
Net Profit
-6.438%
Sharpe Ratio
-0.15
Probabilistic Sharpe Ratio
1.123%
Loss Rate
54%
Win Rate
46%
Profit-Loss Ratio
1.13
Alpha
-0.012
Beta
-0.01
Annual Standard Deviation
0.087
Annual Variance
0.008
Information Ratio
-0.629
Tracking Error
0.181
Treynor Ratio
1.366
Total Fees
$1753.70
Estimated Strategy Capacity
$190000000.00
Lowest Capacity Asset
ZM V9XOWSIHQ1ID
from AlgorithmImports import *

from datetime import timedelta
import numpy as np


class FuturesMomentumEffect(QCAlgorithm):
    '''Basic template algorithm simply initializes the date range and cash'''

    def Initialize(self):
        self.SetStartDate(2010, 1, 1)
        self.SetEndDate(2012, 9, 15)
        # self.SetEndDate(2020, 10, 1)
        self.SetCash(1000000)
        futures_universe = [
            Futures.Grains.Wheat,
            Futures.Grains.HRWWheat,
            Futures.Grains.Corn,
            Futures.Grains.Soybeans,
            Futures.Grains.SoybeanMeal,
            Futures.Grains.SoybeanOil
        ]

        indicator_period = 21
        # lookback_multiplier = 1
        # holding_multiplier = 6
        lookback_multiplier = int(self.GetParameter("lookback-period"))
        holding_multiplier = int(self.GetParameter("holding-period"))

        self.continuous_contracts = {}
        self.logr_indicators = {}
        for i in futures_universe:
            future = self.AddFuture(i, dataNormalizationMode=DataNormalizationMode.BackwardsRatio,
                                    dataMappingMode=DataMappingMode.FirstDayMonth, contractDepthOffset=0,
                                    resolution=Resolution.Daily)
            future.SetFilter(0, 182)
            self.continuous_contracts[future.Symbol] = future
            self.logr_indicators[future.Symbol] = self.LOGR(
                future.Symbol, indicator_period * lookback_multiplier, Resolution.Daily)

        self.nextRebalance = self.Time
        self.Rebalancetime = timedelta(30) * (holding_multiplier-1)

        # set warmup
        self.SetWarmUp(timedelta(indicator_period * lookback_multiplier))

        self.test_tag = f"({lookback_multiplier}-{holding_multiplier})"

        self.most_liquid_contracts = {}
        self.long_holdings = {}
        self.short_holdings = {}

        self.quantity = 10
        self.SetSecurityInitializer(BrokerageModelSecurityInitializer(self.BrokerageModel,
                                                                      FuncSecuritySeeder(self.GetLastKnownPrices)))

    def OnData(self, slice: Slice) -> None:
        if self.IsWarmingUp:
            return

        for changed_event in slice.SymbolChangedEvents.Values:
            old_contract = self.Symbol(changed_event.OldSymbol)
            new_contract = self.Symbol(changed_event.NewSymbol)
            old_month = old_contract.ID.Date.month
            new_month = new_contract.ID.Date.month
            rollover_tag = f"Rollover: {old_contract.Value} -> {new_contract.Value}, ({old_month}) -> ({new_month})"
            liquidate_tag = rollover_tag + \
                f', [Liquidate {old_contract.Value}]'
            new_tag = rollover_tag + f', [Open {new_contract.Value}]'

            if self.Portfolio[old_contract].Invested:
                if self.Portfolio[old_contract].Quantity > 0:
                    self.Liquidate(old_contract, tag=liquidate_tag)
                    self.MarketOrder(new_contract,
                                     self.quantity, tag=new_tag)
                elif self.Portfolio[old_contract].Quantity < 0:
                    self.Liquidate(old_contract, tag=liquidate_tag)
                    self.MarketOrder(new_contract, -
                                     self.quantity, tag=new_tag)


        # if first day of month
        if self.Time < (self.nextRebalance) or slice.FuturesChains.Count == 0:
            return

        invested = [x.Symbol.Value for x in self.Portfolio.Values if x.Invested]
        # get portfolio value
        portfolio_value = self.Portfolio.TotalPortfolioValue
        # get portfolio cash
        portfolio_cash = self.Portfolio.Cash
        # get portfolio pnl
        portfolio_pnl = self.Portfolio.TotalProfit

        self.Liquidate(tag=f'Rebalance - Liquidate ALL - {self.test_tag}')

        highest_logr, lowest_logr = self.Momentum_Signal(self.logr_indicators)



        for continuous_contract_symbol, chain in slice.FuturesChains.items():
            continuous_contract = self.continuous_contracts[continuous_contract_symbol]
            mapped_contract = continuous_contract.Mapped
            mapped_contract_debug = self.Symbol(mapped_contract).Value
            mapping_debug = f'{continuous_contract_symbol} Mapped: {mapped_contract_debug} ({mapped_contract})'

            if highest_logr == continuous_contract_symbol:
                self.MarketOrder(mapped_contract, self.quantity,
                                 tag=f'LONG {mapped_contract_debug} - {self.test_tag}')
            elif lowest_logr == continuous_contract_symbol:
                self.MarketOrder(mapped_contract, -self.quantity,
                                 tag=f'SHORT {mapped_contract_debug} - {self.test_tag}')

        # set next rebalance time
        self.nextRebalance = Expiry.EndOfMonth(self.Time) + self.Rebalancetime

    def Momentum_Signal(self, mom_indicator_dict):
        # get current value for logr indicators
        logr_values = {}
        for i in mom_indicator_dict:
            # check if indicator is ready
            if mom_indicator_dict[i].IsReady:
                logr_values[i] = round(mom_indicator_dict[i].Current.Value, 4)

        # sort logr values
        sorted_logr_values = dict(
            sorted(logr_values.items(), key=lambda x: x[1]))

        # get highest and lowest logr values
        if len(sorted_logr_values) < 2:
            return None, None

        highest_logr = list(sorted_logr_values.keys())[-1]
        lowest_logr = list(sorted_logr_values.keys())[0]

        return highest_logr, lowest_logr