| Overall Statistics |
|
Total Orders 19 Average Win 0.83% Average Loss -0.48% Compounding Annual Return 11.058% Drawdown 3.300% Expectancy 1.123 Start Equity 2000000 End Equity 2089665.73 Net Profit 4.483% Sharpe Ratio 0.392 Sortino Ratio 0.592 Probabilistic Sharpe Ratio 60.327% Loss Rate 22% Win Rate 78% Profit-Loss Ratio 1.73 Alpha -0.053 Beta 0.408 Annual Standard Deviation 0.056 Annual Variance 0.003 Information Ratio -2.358 Tracking Error 0.069 Treynor Ratio 0.054 Total Fees $181.98 Estimated Strategy Capacity $420000000.00 Lowest Capacity Asset MSFT R735QTJ8XC9X Portfolio Turnover 3.13% |
from AlgorithmImports import *
class CombinedTradingAlgorithm(QCAlgorithm):
def Initialize(self):
self.SetStartDate(2023, 12, 20)
self.SetEndDate(2024, 5, 20)
self.SetCash(2_000_000)
# S1: AMZN (Momentum Strategy)
self.s1 = self.AddEquity("AMZN", Resolution.Daily).Symbol
self.sma_s1 = self.SMA(self.s1, 20, Resolution.Daily)
# S2: MSFT (Mean Reversion Strategy)
self.s2 = self.AddEquity("MSFT", Resolution.Daily).Symbol
self.sma_s2 = self.SMA(self.s2, 20, Resolution.Daily)
self.weight = 0.25 ## each stock gets 25% allocation
self.max_daily_loss = 0
self.max_loss_date = None
self.previous_value = None
self.pnl_on_max_loss_day = {"realized": 0, "unrealized": 0}
def OnData(self, data):
if not self.sma_s1.IsReady or not self.sma_s2.IsReady:
return
#get latest price & Simple Moving Average (SMA) values
if self.s1 in data and data[self.s1] is not None:
price_s1 = data[self.s1].Close
sma_value_s1 = self.sma_s1.Current.Value
# Momentum Strategy (AMZN)
if price_s1 > sma_value_s1 and not self.Portfolio[self.s1].Invested:
self.SetHoldings(self.s1, self.weight) # Buy 25% of portfolio
elif price_s1 <= sma_value_s1 and self.Portfolio[self.s1].Invested:
self.Liquidate(self.s1)
if self.s2 in data and data[self.s2] is not None:
price_s2 = data[self.s2].Close
sma_value_s2 = self.sma_s2.Current.Value
# Mean Reversion Strategy (MSFT)
if price_s2 < sma_value_s2 and not self.Portfolio[self.s2].Invested:
self.SetHoldings(self.s2, self.weight) # Buy 25% of portfolio
elif price_s2 >= sma_value_s2 and self.Portfolio[self.s2].Invested:
self.Liquidate(self.s2)
def OnEndOfDay(self):
if self.previous_value is not None:
daily_loss = (self.Portfolio.TotalPortfolioValue / self.previous_value) - 1
if daily_loss < self.max_daily_loss: # Track the worst loss day
self.max_daily_loss = daily_loss
self.max_loss_date = self.Time.date()
self.pnl_on_max_loss_day["realized"] = self.Portfolio.TotalProfit # Realized PnL
self.pnl_on_max_loss_day["unrealized"] = (
self.Portfolio.TotalPortfolioValue - self.Portfolio.Cash - self.Portfolio.TotalProfit
) # Unrealized PnL
self.previous_value = self.Portfolio.TotalPortfolioValue
def OnEndOfAlgorithm(self):
self.Debug(f"Max Daily Loss: {self.max_daily_loss:.4%} on {self.max_loss_date}")
self.Debug(f"Realized PnL on Max Loss Day: ${self.pnl_on_max_loss_day['realized']:,.2f}")
self.Debug(f"Unrealized PnL on Max Loss Day: ${self.pnl_on_max_loss_day['unrealized']:,.2f}")