Overall Statistics
Total Orders
31
Average Win
2.55%
Average Loss
-2.21%
Compounding Annual Return
5.807%
Drawdown
7.300%
Expectancy
0.124
Start Equity
100000
End Equity
104328.26
Net Profit
4.328%
Sharpe Ratio
0.177
Sortino Ratio
0.262
Probabilistic Sharpe Ratio
30.252%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
1.15
Alpha
0.014
Beta
0.017
Annual Standard Deviation
0.082
Annual Variance
0.007
Information Ratio
-0.127
Tracking Error
0.157
Treynor Ratio
0.848
Total Fees
$0.00
Estimated Strategy Capacity
$0
Lowest Capacity Asset
XAGUSD 8I
Portfolio Turnover
15.47%
from AlgorithmImports import *
from QuantConnect.DataSource import *

class SMAPairsTrading(QCAlgorithm):

    def initialize(self) -> None:
        self.set_start_date(2018, 7, 1)   
        self.set_end_date(2019, 3, 31)
        self.set_cash(100000)
        
        self.add_cfd('XAUUSD', Resolution.HOUR);
        self.add_cfd('XAGUSD', Resolution.HOUR);

        self.pair = [ ]
        self.spread_mean = SimpleMovingAverage(500)
        self.spread_std = StandardDeviation(500)
        
    def on_data(self, slice: Slice) -> None:
        spread = self.pair[1].price - self.pair[0].price
        self.spread_mean.update(self.time, spread)
        self.spread_std.update(self.time, spread) 
        
        upperthreshold = self.spread_mean.current.value + self.spread_std.current.value
        lowerthreshold = self.spread_mean.current.value - self.spread_std.current.value

        if spread > upperthreshold:
            self.set_holdings(self.pair[0].symbol, 1)
            self.set_holdings(self.pair[1].symbol, -1)
        
        if spread < lowerthreshold:
            self.set_holdings(self.pair[0].symbol, -1)
            self.set_holdings(self.pair[1].symbol, 1)
    
    def on_securities_changed(self, changes: SecurityChanges) -> None:
        self.pair = [x for x in changes.added_securities]
        
        #1. Call for 500 bars of history data for each symbol in the pair and save to the variable history
        history = self.history([x.symbol for x in self.pair], 500)
        #2. Unstack the Pandas data frame to reduce it to the history close price
        history = history.close.unstack(level=0)
        #3. Iterate through the history tuple and update the mean and standard deviation with historical data 
        for tuple in history.itertuples():
            self.spread_mean.update(tuple[0], tuple[2]-tuple[1])
            self.spread_std.update(tuple[0], tuple[2]-tuple[1])