| Overall Statistics |
|
Total Orders 427 Average Win 0.27% Average Loss -0.25% Compounding Annual Return 1.573% Drawdown 7.900% Expectancy 0.147 Start Equity 100000 End Equity 108121.41 Net Profit 8.121% Sharpe Ratio -0.707 Sortino Ratio -0.757 Probabilistic Sharpe Ratio 3.045% Loss Rate 45% Win Rate 55% Profit-Loss Ratio 1.07 Alpha -0.028 Beta 0.023 Annual Standard Deviation 0.038 Annual Variance 0.001 Information Ratio -0.593 Tracking Error 0.143 Treynor Ratio -1.201 Total Fees $830.69 Estimated Strategy Capacity $100000.00 Lowest Capacity Asset CLY UIB7ED20G0V9 Portfolio Turnover 5.13% Drawdown Recovery 861 |
# region imports
from AlgorithmImports import *
from datetime import timedelta
import math
# endregion
class DeterminedLightBrownAlpaca(QCAlgorithm):
def initialize(self):
self.set_cash(100_000)
self.set_start_date(self.end_date - timedelta(5*365))
self.settings.automatic_indicator_warm_up = True
self.settings.seed_initial_prices = True
self._portfolio_size = 4
# Add the safety hedge asset.
self._safety_asset = self.add_equity("SHY", Resolution.DAILY)
self._ranked_assets = []
# Define the tickers for fixed income and credit-focused securities.
tickers = ["AGZ", "BIL", "HYG", "IEF", "IEI", "IGIB", "IGLB",
"JNK", "LQD", "MBB", "MINT", "TBF", "TBX", "TIP", "TLT"]
for ticker in tickers:
asset = self.add_equity(ticker, Resolution.DAILY)
# Attach indicators for each asset.
asset.return_a = self.momp(asset, 20)
asset.return_b = self.momp(asset, 63)
# Create a 6-day SMA updating monthly.
asset.trend_sma = SimpleMovingAverage(6)
self.register_indicator(asset, asset.trend_sma, TradeBarConsolidator(CalendarType.MONTHLY))
# Create a 20-day log return annualized volatility indicator.
annualized_volatility = IndicatorExtensions.of(StandardDeviation(20), self.logr(asset, 20))
asset.volatility = IndicatorExtensions.times(annualized_volatility, math.sqrt(252))
self._ranked_assets.append(asset)
self.set_warm_up(timedelta(365))
self._rebalance_counter = 0
def on_warmup_finished(self):
rebalance_time_rule = self.time_rules.at(8, 0)
# Add a Scheduled event to rebalance the portfolio bi-weekly.
self.schedule.on(
self.date_rules.week_start("SPY"),
rebalance_time_rule,
self._bi_weekly
)
# Rebalance the portfolio today too.
if self.live_mode:
self._rebalance()
else:
self.schedule.on(self.date_rules.today, rebalance_time_rule, self._rebalance)
def _bi_weekly(self):
self._rebalance_counter += 1
if self._rebalance_counter % 2 == 0:
self._rebalance()
def _rebalance(self):
ready_assets = [asset for asset in self._ranked_assets if asset.volatility.is_ready and asset.trend_sma.is_ready]
if not ready_assets:
return
# Rank securities by: short-term and medium term momentum as well as low volatility.
asset_count = len(ready_assets)
for asset in ready_assets:
asset.rank_score = 0.0
for rank, asset in enumerate(sorted(ready_assets, key=lambda security: security.return_a)):
asset.rank_score += (asset_count - rank - 1) * 0.2
for rank, asset in enumerate(sorted(ready_assets, key=lambda security: security.return_b)):
asset.rank_score += (asset_count - rank - 1) * 0.5
for rank, asset in enumerate(sorted(ready_assets, key=lambda security: security.volatility)):
asset.rank_score += rank * 0.3
top_assets = sorted(ready_assets, key=lambda security: security.rank_score)[:self._portfolio_size]
# Select the top-ranked assets with their price above monthly SMA.
trend_assets = [asset for asset in top_assets if asset.close > asset.trend_sma.current.value]
# Add the safety asset to portfolio if fewer assets pass the filter.
target_assets = trend_assets + ([self._safety_asset] if len(trend_assets) < len(top_assets) else [])
# Exit the safety asset when at least one top asset passes the trend filter.
if trend_assets and self._safety_asset.holdings.invested:
self.liquidate(self._safety_asset)
# Maintain the safety asset if no trend-filtered assets were selected.
if self._safety_asset.holdings.invested:
return
# Create a equal-weighted portfolio.
portfolio_targets = [PortfolioTarget(asset, 1 / self._portfolio_size) for asset in target_assets]
self.set_holdings(portfolio_targets, True)