| Overall Statistics |
|
Total Orders 62 Average Win 1.46% Average Loss -1.67% Compounding Annual Return -1.613% Drawdown 7.000% Expectancy 0.004 Start Equity 100000 End Equity 98786.38 Net Profit -1.214% Sharpe Ratio -0.535 Sortino Ratio -0.657 Probabilistic Sharpe Ratio 11.741% Loss Rate 46% Win Rate 54% Profit-Loss Ratio 0.87 Alpha -0.039 Beta 0.033 Annual Standard Deviation 0.07 Annual Variance 0.005 Information Ratio -0.483 Tracking Error 0.149 Treynor Ratio -1.127 Total Fees $0.00 Estimated Strategy Capacity $0 Lowest Capacity Asset XAGUSD 8I Portfolio Turnover 19.83% |
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)
# Request gold and sliver spot CFDs for trading their spread difference, assuming their spread series is cointegrated
self.add_cfd('XAUUSD', Resolution.HOUR)
self.add_cfd('XAGUSD', Resolution.HOUR)
# Use 500-step mean and SD indicator on determine the spread relative difference for trading signal generation
self.pair = [ ]
self.spread_mean = SimpleMovingAverage(500)
self.spread_std = StandardDeviation(500)
def on_data(self, slice: Slice) -> None:
# Update the indicator with updated spread difference, such that the an updated cointegration threshold is calculated for trade inception
spread = self.pair[1].price - self.pair[0].price
self.spread_mean.update(self.time, spread)
self.spread_std.update(self.time, spread)
spread_mean = self.spread_mean.current.value
upperthreshold = spread_mean + self.spread_std.current.value
lowerthreshold = spread_mean - self.spread_std.current.value
# If the spread is higher than upper threshold, bet theie spread series will revert to mean
if spread > upperthreshold:
self.set_holdings(self.pair[0].symbol, 1)
self.set_holdings(self.pair[1].symbol, -1)
elif spread < lowerthreshold:
self.set_holdings(self.pair[0].symbol, -1)
self.set_holdings(self.pair[1].symbol, 1)
# Close positions if mean reverted
elif (self.portfolio[self.pair[0].symbol].quantity > 0 and spread < spread_mean)\
or (self.portfolio[self.pair[0].symbol].quantity < 0 and spread > spread_mean):
self.liquidate()
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])