Overall Statistics
Total Trades
73
Average Win
6.96%
Average Loss
-3.75%
Compounding Annual Return
9.771%
Drawdown
21.700%
Expectancy
0.825
Net Profit
179.056%
Sharpe Ratio
0.647
Loss Rate
36%
Win Rate
64%
Profit-Loss Ratio
1.86
Alpha
0.085
Beta
0.97
Annual Standard Deviation
0.161
Annual Variance
0.026
Information Ratio
0.526
Tracking Error
0.161
Treynor Ratio
0.107
Total Fees
$1205.30
# 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(2008, 1, 1)  # Set Start Date
        self.SetEndDate(2019, 1, 1)    # Set Start Date       
        self.SetCash(100000)           # Set Strategy Cash
        self.us = self.AddEquity("SPY", Resolution.Daily).Symbol
        self.worldExUS =  self.AddEquity("VEU", Resolution.Daily).Symbol
        self.tbill = self.AddEquity("SHV", Resolution.Daily).Symbol #SHY 
        self.agg = self.AddEquity("BND", Resolution.Daily).Symbol # AGG 
        self.lookBackPeriod = 100
        self.symbolDataDict = {symbol:SymbolData(symbol,self.lookBackPeriod) for symbol in [self.us,self.worldExUS,self.tbill,self.agg]}
        self.Schedule.On(self.DateRules.MonthEnd("SPY"),self.TimeRules.AfterMarketOpen("SPY"), self.Rebalance)
        self.leverage = 1.3
        self.Portfolio.MarginCallModel = MarginCallModel.Null;
        
            
    def Rebalance(self):
        if self.symbolDataDict[self.us].Return < self.symbolDataDict[self.tbill].Return:
            self.currentLong =  self.agg
        elif self.symbolDataDict[self.us].Return > self.symbolDataDict[self.worldExUS].Return:
            self.currentLong=self.us
        else:
            self.currentLong = self.worldExUS
        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.Daily)
                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)