| Overall Statistics |
|
Total Orders 1524 Average Win 0.14% Average Loss -0.26% Compounding Annual Return -4.635% Drawdown 7.700% Expectancy -0.025 Start Equity 1000000 End Equity 964932.21 Net Profit -3.507% Sharpe Ratio -1.435 Sortino Ratio -1.52 Probabilistic Sharpe Ratio 5.305% Loss Rate 36% Win Rate 64% Profit-Loss Ratio 0.53 Alpha -0.093 Beta 0.103 Annual Standard Deviation 0.06 Annual Variance 0.004 Information Ratio -0.868 Tracking Error 0.172 Treynor Ratio -0.84 Total Fees $24866.61 Estimated Strategy Capacity $360000.00 Lowest Capacity Asset BND TRO5ZARLX6JP Portfolio Turnover 223.43% Drawdown Recovery 0 |
from AlgorithmImports import *
from datetime import timedelta
class DCA_SPY_Gold_With_TPSL(QCAlgorithm):
def initialize(self):
self.set_start_date(2019, 3, 25)
self.set_end_date(2019, 7, 1)
self.set_cash(1_000_000)
# Add SPY, GLD, and BND (bonds)
self.spy = self.add_equity("SPY", Resolution.MINUTE).symbol
self.gld = self.add_equity("GLD", Resolution.MINUTE).symbol
self.bnd = self.add_equity("BND", Resolution.DAILY).symbol
# Portfolio allocation:
self.spy_weight = 0.5
self.gld_weight = 0.5
# DCA stages for combined portfolio
self.stages = [.4, .9, 1.0]
self.stage_index = 0
self.dca_active = True
# Set TP/SL as percentage (e.g. 0.05 = 5%)
self.take_profit_pct = 0.0008 # 0.08% daily gain
self.stop_loss_pct = -0.010 # -10% daily loss
self.take_profit = 0.0
self.stop_loss = 0.0
self.daily_pnl = 0.0
self.daily_baseline_value = 0.0
self.made_tp_sl = False
self.schedule.on(
self.date_rules.every_day(),
self.time_rules.after_market_open(self.spy, 1),
self.reset_daily
)
self.schedule.on(
self.date_rules.every_day(),
self.time_rules.every(timedelta(minutes=5)),
self.dca_rebalance
)
def reset_daily(self):
# Reset daily PnL baseline to current portfolio value
self.daily_baseline_value = self.portfolio.total_portfolio_value
# Calculate daily targets based on current portfolio value
self.take_profit = self.daily_baseline_value * self.take_profit_pct
self.stop_loss = self.daily_baseline_value * self.stop_loss_pct
self.daily_pnl = 0.0
self.made_tp_sl = False
if not (self.portfolio[self.spy].invested or self.portfolio[self.gld].invested):
# Only start new DCA if not already invested
self.stage_index = 0
self.dca_active = True
self.dca_rebalance()
else:
# If we're carrying positions, just reset the daily tracking
self.debug(f"Carrying positions overnight, resetting daily PnL baseline to ${self.daily_baseline_value:.2f}")
def dca_rebalance(self):
if not self.dca_active or self.stage_index >= len(self.stages):
return
target_total = self.stages[self.stage_index]
# Calculate individual holdings based on weights
spy_target = target_total * self.spy_weight
gld_target = target_total * self.gld_weight
# First liquidate bonds if we're entering a new position
if self.portfolio[self.bnd].invested:
self.liquidate(self.bnd)
self.debug("Liquidating bonds to enter SPY/GLD positions")
self.set_holdings(self.spy, spy_target)
self.set_holdings(self.gld, gld_target)
self.debug(f"DCA stage {self.stage_index+1}: SPY {spy_target*100:.1f}%, GLD {gld_target*100:.1f}% (Total: {target_total*100:.0f}%)")
self.stage_index += 1
def invest_in_bonds(self):
"""Invest remaining cash in bonds when not actively trading SPY/GLD"""
if not (self.portfolio[self.spy].invested or self.portfolio[self.gld].invested):
# Only invest in bonds if we're not holding SPY or GLD
if not self.portfolio[self.bnd].invested:
self.set_holdings(self.bnd, 1.0) # Invest 100% in bonds
self.debug("Investing in bonds (BND) - not actively trading SPY/GLD")
def on_data(self, data: Slice):
if (self.portfolio[self.spy].invested or self.portfolio[self.gld].invested) and not self.made_tp_sl:
# Calculate daily PnL from the daily baseline
self.daily_pnl = self.portfolio.total_portfolio_value - self.daily_baseline_value
if self.daily_pnl >= self.take_profit:
self.liquidate(self.spy) # Liquidate SPY and GLD positions only
self.liquidate(self.gld)
self.debug(f"Take profit hit! Daily PnL: +${self.daily_pnl:.2f}")
self.dca_active = False
self.made_tp_sl = True
self.stage_index = 0
# Invest in bonds after liquidating
self.invest_in_bonds()
elif self.daily_pnl <= self.stop_loss:
self.liquidate(self.spy) # Liquidate SPY and GLD positions only
self.liquidate(self.gld)
self.debug(f"Stop loss hit! Daily PnL: ${self.daily_pnl:.2f}")
self.dca_active = False
self.made_tp_sl = True
self.stage_index = 0
# Invest in bonds after liquidating
self.invest_in_bonds()
# If we're not actively trading and not in bonds, invest in bonds
elif not (self.portfolio[self.spy].invested or self.portfolio[self.gld].invested or self.portfolio[self.bnd].invested):
self.invest_in_bonds()