Overall Statistics
Total Trades
1106
Average Win
0.83%
Average Loss
-0.75%
Compounding Annual Return
1.052%
Drawdown
32.000%
Expectancy
0.038
Net Profit
5.924%
Sharpe Ratio
0.145
Loss Rate
51%
Win Rate
49%
Profit-Loss Ratio
1.11
Alpha
0.117
Beta
-4.917
Annual Standard Deviation
0.153
Annual Variance
0.023
Information Ratio
0.018
Tracking Error
0.153
Treynor Ratio
-0.004
Total Fees
$0.00
from QuantConnect.Python import PythonQuandl
from datetime import datetime, timedelta
from QuantConnect.Python import PythonData
from decimal import Decimal
from datetime import timedelta
from QuantConnect.Data import SubscriptionDataSource
from collections import deque

class ShortTermReversalWithFutures(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2013, 2, 1)                                 
        self.SetEndDate(2018, 8, 1)                               
        self.SetCash(10000000) 

        self.tickers = ["CME_SF1_EF", "CME_MP1_FF",  "CME_CD1_EF", "CME_ED8_FF",               # Currencies
                        "CME_NQ1_EF", "CME_MD1_EF", "CME_ES1_EF", "CME_YM1_EF", "CME_NK1_EF",  # Financial indices
                        "CME_C1_EF", "CME_SM1_EF",  "ICE_CC1_EF",  "CME_LC1_EF",               # Agricultural product
                        "CME_KW1_EF", "CME_S1_EF", "ICE_KC1_EF", "CME_LC1_EF",                
                        "CME_HG1_FF",  "CME_GC1_EF", "SHFE_AL1_EF",  "SHFE_CU1_EF",            # Commodities
                        "CME_CL1_EF", "ICE_T1_FR", "CME_HO1_EF"]
        self.length = len(self.tickers)
        for ticker in self.tickers:
            self.AddData(Futures, ticker)
        # create the deque list to save the weekly history dataframe 
        self.window = deque(maxlen=2)   
        # rebalance the portfolio every week on Wednesday
        self.Schedule.On(self.DateRules.Every(DayOfWeek.Wednesday, DayOfWeek.Wednesday), self.TimeRules.At(0, 0), self.WeeklyTrade)

    def WeeklyTrade(self):
        hist = self.History(self.tickers, self.Time-timedelta(days=7), self.Time,Resolution.Daily)
        self.window.append(hist)
        if len(self.window) == self.window.maxlen:
            hist_t2 = self.window[0] # the weekly history dataframe two week ago
            hist_t1 = self.window[1] # the weekly history dataframe a week ago
            top_vol, bottom_vol = self.CalculateChange("volume", hist_t2, hist_t1)
            top_OI, bottom_OI = self.CalculateChange("open_interest", hist_t2, hist_t1)
            # the intersection of top volume group and bottom open interest group
            trade_group = list(set(top_vol) & set(bottom_OI))
            returns = {}
            for ticker in trade_group:
                res = (hist_t1.loc[ticker]["settle"][-1] - hist_t2.loc[ticker]["settle"][-1]) / hist_t2.loc[ticker]["settle"][-1]
                returns[ticker] = res
    
            sortedByReturn = sorted(returns, key = lambda x: returns[x])
            
            if len(sortedByReturn) >= 2:
                if self.Portfolio.Invested:
                    self.Liquidate()
                self.SetHoldings(sortedByReturn[-1], -0.3)
                self.SetHoldings(sortedByReturn[0], 0.3)
    
    def CalculateChange(self, column_name, hist_t2, hist_t1):  
        # calculate the weekly change and sort by the colume value
        value_t2 = hist_t2[column_name].unstack(level=0).sum(axis=0)
        value_t1 = hist_t1[column_name].unstack(level=0).sum(axis=0)
        delta = (value_t1 - value_t2).sort_values(ascending=True)
        top = list(delta[:int(self.length*0.5)].index)
        bottom = list(delta[-int(self.length*0.5):].index)
        return top, bottom


class Futures(PythonData):
    # import the futures custom data from dropbox

    def GetSource(self, config, date, isLiveMode):
        key = { "CME_C1_EF": "ptqr9mlbwmrwmha",
                "CME_CL1_EF": "pjwn2f1ym3030ql",
                "CME_ED8_FF": "kqmfgzlp4mljrmx",
                "CME_ES1_EF": "g88576n8n3y8fn9",
                "CME_GC1_EF": "ub1bdrukz02v820",
                "CME_HG1_FF": "nlm5ws5zxucdjg2",
                "CME_HO1_EF": "fsu70u9b7oxcirm",
                "CME_CD1_EF": "23cn1u489voxwr0",
                "CME_KW1_EF": "c5omhcfkfzt9x6b",
                "CME_LC1_EF": "slslbez9yw3763w",
                "CME_MD1_EF": "afb2p8urj99hg5p",
                "CME_MP1_FF": "zm13mpkyphtts2p",
                "CME_NG1_EF": "7y6c4wfgxyhq9eg",
                "CME_NK1_EF": "ufer5ypdn9dfehj",
                "CME_NQ1_EF": "fzqkino0ao3bm7y",
                "ICE_KC1_EF": "nl7ax33p05x7esz",
                "CME_S1_EF": "w00ryar0c8mxfmz",
                "CME_SF1_EF": "s0lqt3j8edyhp9c",
                "CME_SM1_EF": "zdt4okvkk6hxojb",
                "ICE_CC1_EF": "3n51khfd6nzkcqx",
                "CME_YM1_EF": "tyh0csc8q236bv5",
                "SHFE_CU1_EF": "uhrezib2dx2z2xd",
                "SHFE_AL1_EF": "u9xbkh5seja5f69",
                "ICE_T1_FR": "lxkcewy0jh8qw5o"}
        source = "https://www.dropbox.com/s/"+ key[config.Symbol.Value] +"/" + config.Symbol.Value +".csv?dl=1"
        return SubscriptionDataSource(source, SubscriptionTransportMedium.RemoteFile)

    def Reader(self, config, line, date, isLiveMode):
        futures = Futures()
        futures.Symbol = config.Symbol
        data = line.split(',')
        if data[0] == "date": return None
        futures.Time = datetime.strptime(data[0], "%m/%d/%y")
        futures.Value = float(data[4])
        futures["settle"] = float(data[4])
        futures["volume"] = float(data[5])
        futures["open_interest"] = float(data[6])

        return futures