Overall Statistics
Total Trades
124
Average Win
1.04%
Average Loss
-0.77%
Compounding Annual Return
-2.959%
Drawdown
7.400%
Expectancy
-0.127
Net Profit
-6.165%
Sharpe Ratio
-0.512
Loss Rate
63%
Win Rate
37%
Profit-Loss Ratio
1.35
Alpha
-0.084
Beta
2.838
Annual Standard Deviation
0.055
Annual Variance
0.003
Information Ratio
-0.871
Tracking Error
0.055
Treynor Ratio
-0.01
Total Fees
$124.00
import numpy as np
import decimal as d 
class PairsDualListedArbitrage(QCAlgorithm):

    def Initialize(self):
        
        self.SetStartDate(2016,1,1)  
        self.SetEndDate(2017,12,1)    
        self.SetCash(1000)
        
        self.TakeProfit = d.Decimal(1.025)
        self.StopLoss = d.Decimal(1.01)
        
        self.StringSymbols = ["DISCA", "DISCK"]
        self.symbols = [
                self.AddEquity(self.StringSymbols[0], Resolution.Minute).Symbol,
                self.AddEquity(self.StringSymbols[1], Resolution.Minute).Symbol
                ]
        for i in self.symbols:        
            i.fast = self.SMA(i, 20, Resolution.Daily)
            i.slow = self.SMA(i, 50, Resolution.Daily)
        
        self.AddEquity("SPY", Resolution.Minute)
        self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.AfterMarketOpen("SPY",0),
                Action(self.SetBenchmark))
                
        self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.AfterMarketOpen("SPY", 60),
                Action(self.Rebalance1))

        self.Schedule.On(self.DateRules.EveryDay("SPY"),
                self.TimeRules.AfterMarketOpen("SPY", 380),
                Action(self.Reset))
        self.SetWarmUp(int(6.5*60*50))

    def OnData(self, data):
        if self.IsWarmingUp: return
    
    def SetBenchmark(self):
        
        # Returns a spread for the premium of owning X stock over the 
        # past 100 days
        # The mean of those 100 spreads becomes the Benchmark Spread that we base our
        # predictions off of

        mainHistoricalClose = self.History([self.StringSymbols[1]], 100, Resolution.Minute).loc[self.StringSymbols[1]]["close"]
        dualHistoricalClose = self.History([self.StringSymbols[0]], 100, Resolution.Minute).loc[self.StringSymbols[0]]["close"]
        spread = dualHistoricalClose - mainHistoricalClose
        self.benchmarkSpread = spread.mean()
        self.benchmarkSpread = float(self.benchmarkSpread)
        
    def Rebalance1(self):
 
        mainPrice = self.Securities[self.StringSymbols[1]].Price
        dualPrice = self.Securities[self.StringSymbols[0]].Price
        
        currentSpread = float(dualPrice - mainPrice)
        self.PercentDiff = (currentSpread - self.benchmarkSpread)/self.benchmarkSpread

        for i in self.symbols: 
            i.IsUpTrend = i.fast.Current.Value > i.slow.Current.Value
        
        if self.symbols[1].IsUpTrend and self.PercentDiff > .070:
                if self.Portfolio[self.symbols[1]].Quantity <= 0:
                    self.MarketOrder(self.symbols[1], int(self.Portfolio.TotalPortfolioValue/mainPrice))
                    self.LimitOrder(self.symbols[1], -1*abs(self.Portfolio[self.symbols[1]].Quantity), d.Decimal(mainPrice)*self.TakeProfit, tag="Short")
                    self.StopMarketOrder(self.symbols[1], -1*abs((self.Portfolio[self.symbols[1]].Quantity)), d.Decimal(mainPrice)/self.StopLoss, tag="Short")
        
        elif self.symbols[0].IsUpTrend and self.PercentDiff < -.070:
            if self.Portfolio[self.symbols[0]].Quantity <= 0:
                self.MarketOrder(self.symbols[0], int(self.Portfolio.TotalPortfolioValue/dualPrice))
                self.LimitOrder(self.symbols[0], -1*abs((self.Portfolio[self.symbols[0]].Quantity)), d.Decimal(dualPrice)*self.TakeProfit, tag="Short")
                self.StopMarketOrder(self.symbols[0], -1*abs((self.Portfolio[self.symbols[0]].Quantity)), d.Decimal(dualPrice)/self.StopLoss, tag="Short")
                
    def Reset(self):
        self.Liquidate()

    def OnOrderEvent(self, orderEvent):
        order = self.Transactions.GetOrderById(orderEvent.OrderId)
        
        if order.Status == OrderStatus.Filled:
            if order.Type == OrderType.Limit or order.Type == OrderType.StopMarket:
                self.Transactions.CancelOpenOrders(order.Symbol)