| Overall Statistics |
|
Total Orders 2686 Average Win 1.22% Average Loss -0.68% Compounding Annual Return 15.445% Drawdown 22.400% Expectancy 0.127 Start Equity 100000 End Equity 205112.12 Net Profit 105.112% Sharpe Ratio 0.533 Sortino Ratio 0.652 Probabilistic Sharpe Ratio 25.031% Loss Rate 60% Win Rate 40% Profit-Loss Ratio 1.80 Alpha 0.041 Beta 0.512 Annual Standard Deviation 0.144 Annual Variance 0.021 Information Ratio 0.047 Tracking Error 0.143 Treynor Ratio 0.15 Total Fees $26815.06 Estimated Strategy Capacity $580000.00 Lowest Capacity Asset SVXY V0H08FY38ZFP Portfolio Turnover 70.59% Drawdown Recovery 455 |
from AlgorithmImports import *
class BasicTemplateAlgorithm(QCAlgorithm):
"""
Volatility trading strategy using SVXY and UVXY with trend detection.
Uses moving average crossovers on SVXY and SPY trend indicators for positioning.
"""
def initialize(self):
self.set_start_date(self.end_date - timedelta(5*365))
self.set_cash(100_000)
self.set_benchmark("SPY")
self._uvxy = self.add_equity("UVXY", Resolution.HOUR)
self._svxy = self.add_equity("SVXY", Resolution.HOUR)
spy = self.add_equity("SPY", Resolution.HOUR)
self.settings.automatic_indicator_warm_up = True
# Signals dictionary for EMA indicator crossovers.
self._signals = {
"svxy": IndicatorExtensions.minus(self.ema(self._svxy, 2), self.ema(self._svxy, 5)),
"market": IndicatorExtensions.minus(self.sma(spy, 100), self.sma(spy, 200)),
"spy_bull": IndicatorExtensions.minus(self.ema(spy, 25), self.ema(spy, 50))
}
# Track date when stop loss was hit to prevent trading rest of day.
self._stop_loss_date = None
def on_data(self, slice):
# Check if all signals are ready and stop loss has not been hit this day.
if (not all(ind.is_ready for ind in self._signals.values()) or
self._stop_loss_date == self.time.date()):
return
market_uptrend = self._signals["market"].current.value > 0
svxy_signal = self._signals["svxy"].current.value > 0
spy_bull = self._signals["spy_bull"].current.value > 0
svxy_held = self._svxy.holdings.invested
uvxy_held = self._uvxy.holdings.invested
# Compute target allocations based on signal and market regime.
svxy_target = 0.75 if svxy_signal else 0
uvxy_target = 0.375 if not market_uptrend and not spy_bull and not svxy_signal and svxy_held else 0
if svxy_target > 0 and not svxy_held:
self.set_holdings(self._svxy, svxy_target)
self.liquidate(self._uvxy)
elif svxy_target == 0 and svxy_held:
self.liquidate(self._svxy)
if uvxy_target > 0 and not uvxy_held:
self.set_holdings(self._uvxy, uvxy_target)
self.liquidate(self._svxy)
elif uvxy_target == 0 and uvxy_held:
self.liquidate(self._uvxy)
def on_order_event(self, order_event: OrderEvent) -> None:
# Set a 10% stop loss on SVXY and UVXY.
if order_event.status != OrderStatus.FILLED:
return
match order_event.ticket.order_type:
case OrderType.MARKET:
symbol = order_event.symbol
if self.securities[symbol].invested:
self.stop_market_order(symbol, -order_event.fill_quantity, order_event.fill_price*0.90)
case OrderType.STOP_MARKET:
# If stop loss is hit, do not trade until the next trading day.
self._stop_loss_date = self.time.date()