Overall Statistics
Total Trades
588
Average Win
0.07%
Average Loss
-0.15%
Compounding Annual Return
-89.991%
Drawdown
27.800%
Expectancy
-0.731
Net Profit
-27.806%
Sharpe Ratio
-15.122
Probabilistic Sharpe Ratio
0.000%
Loss Rate
82%
Win Rate
18%
Profit-Loss Ratio
0.50
Alpha
-1.051
Beta
0.057
Annual Standard Deviation
0.059
Annual Variance
0.003
Information Ratio
-11.883
Tracking Error
0.308
Treynor Ratio
-15.53
Total Fees
$1224.48
import numpy as np
import math

# T is short for either Type or Mr. T (your choice)
class PositionT(Enum):
    I = 1 # just so we can set our inital enum state to a value
    T1 = 2 # long ZNH short CEA
    T2 = 2 # short ZNH long CEA
    T3 = 3 # liquidated (no holdings)

stock1 = 'ZNH'
stock2 = 'CEA'

class TachyonCalibratedThrustAssembly(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2020, 4, 1)  # Set Start Date
        self.SetCash(100000)  # Set Strategy Cash
        
        
        self.UniverseSettings.Resolution = Resolution.Hour
        # fun fact: both of these companies are Chinese Airlines listed on NYSE!
        self.znh = self.AddEquity(stock1)
        self.cea = self.AddEquity(stock2)
        self.LogRatioMovingAverage = SimpleMovingAverage(50)
        self.LogRatioMovingStd = StandardDeviation(50)
        self.PreviousHour = -1
        self.T = PositionT.I
        history = self.History(self.Securities.Keys, 100)
        history = history['open'].unstack(level=0).dropna()
        history = history[(history!=0).all(1)]
    
        for tuple in history.itertuples():
            temp = math.log(tuple[2] / tuple[1])
            self.LogRatioMovingAverage.Update(tuple[0], math.log(tuple[1] / tuple[2]))
            self.LogRatioMovingStd.Update(tuple[0], math.log(tuple[1] / tuple[2]))

    def OnData(self, data):
        
        if data.ContainsKey(stock1) and data.ContainsKey(stock2) and self.PreviousHour != self.Time.hour:
            self.PreviousHour = self.Time.hour
            log_ratio = math.log(data[stock1].Open / data[stock2].Open)
            self.LogRatioMovingStd.Update(self.Time, log_ratio)
            self.LogRatioMovingAverage.Update(self.Time, log_ratio)
            if not self.LogRatioMovingStd.IsReady or not self.LogRatioMovingAverage.IsReady:
                return
            std = self.LogRatioMovingStd.Current.Value
            ma = self.LogRatioMovingAverage.Current.Value
        
            if log_ratio > ma + std and self.T != PositionT.T1:
                self.T = PositionT.T1
                self.SetHoldings(stock1, .1)
                self.SetHoldings(stock2, -.1)
            elif log_ratio < ma + std and self.T != PositionT.T2:
                self.T = PositionT.T2
                self.SetHoldings(stock1, -.1)
                self.SetHoldings(stock2, .1)
            elif self.T == PositionT.T1 and log_ratio < ma:
                self.Liquidate()
                self.T = PositionT.T3
            elif self.T == PositionT.T2 and log_ratio > ma:
                self.Liquidate()
                self.T = PositionT.T3