Overall Statistics
Total Trades
65
Average Win
10.16%
Average Loss
-6.11%
Compounding Annual Return
13.039%
Drawdown
21.800%
Expectancy
0.914
Net Profit
500.817%
Sharpe Ratio
0.776
Loss Rate
28%
Win Rate
72%
Profit-Loss Ratio
1.66
Alpha
0.113
Beta
-0.025
Annual Standard Deviation
0.143
Annual Variance
0.021
Information Ratio
0.149
Tracking Error
0.222
Treynor Ratio
-4.392
Total Fees
$1045.13
# https://quantpedia.com/Screener/Details/14
from QuantConnect.Data.UniverseSelection import *
import math
import numpy as np
import pandas as pd
import scipy as sp
from datetime import datetime
from collections import *
from scipy import stats 

class DualMomentumGem(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2005, 1, 1)  # Set Start Date
        self.SetEndDate(2019, 8, 15)    # Set Start Date       
        self.SetCash(100000)           # Set Strategy Cash
        self.us = self.AddEquity("SPY", Resolution.Minute).Symbol
        self.worldExUS =  self.AddEquity("EFA", Resolution.Minute).Symbol
        self.tbill = self.AddEquity("TLT", Resolution.Minute).Symbol #TLT 
        self.lookBackPeriod = 43200
        self.symbolDataDict = {symbol:SymbolData(symbol,self.lookBackPeriod) for symbol in [self.us,self.worldExUS,self.tbill]}
        self.Schedule.On(self.DateRules.MonthEnd("SPY"),self.TimeRules.BeforeMarketClose("SPY",10), self.Rebalance)
        self.leverage = 1.0
        self.Portfolio.MarginCallModel = MarginCallModel.Null;
        
            
    def Rebalance(self):
        if self.symbolDataDict[self.us].Return < self.symbolDataDict[self.worldExUS].Return:
            self.currentLong =  self.worldExUS
        elif self.symbolDataDict[self.worldExUS].Return < self.symbolDataDict[self.tbill].Return:
            self.currentLong=self.tbill
        else:
            self.currentLong = self.us
        stocksInvested = [x.Key for x in self.Portfolio if x.Value.Invested]
        if not stocksInvested or self.currentLong not in stocksInvested:
            self.SetHoldings(self.currentLong, self.leverage, True)


    def OnData(self, data):
        for symbol, symbolData in self.symbolDataDict.items():
            if not symbolData.IsReady:
                history = self.History(symbol, self.lookBackPeriod, Resolution.Minute)
                if str(symbol) in history.index:             
                    symbolData.WarmUpIndicator(history.loc[str(symbol)])
            else:
                symbolData.Update(self.Securities[symbol].Close)

                    

class SymbolData:
    def __init__(self, symbol, lookBackPeriod):
        self.symbol = symbol
        self.Time = datetime.min
        self.IsReady = False
        self.Return = 0
        self.queue = deque(maxlen=lookBackPeriod)
        self.lookBackPeriod = lookBackPeriod
        
    def Update(self, value):
        self.queue.appendleft(value)
        self.Return = self.queue[0]/self.queue[-1]-1
        count = len(self.queue)
        self.IsReady = count == self.queue.maxlen

    def WarmUpIndicator(self, history):
        for tuple in history.itertuples(): self.Update(tuple.close)