| Overall Statistics |
|
Total Orders 7563 Average Win 0.50% Average Loss -0.30% Compounding Annual Return 5.212% Drawdown 24.000% Expectancy 0.062 Start Equity 100000 End Equity 173982.72 Net Profit 73.983% Sharpe Ratio 0.183 Sortino Ratio 0.201 Probabilistic Sharpe Ratio 0.366% Loss Rate 60% Win Rate 40% Profit-Loss Ratio 1.67 Alpha -0.007 Beta 0.392 Annual Standard Deviation 0.137 Annual Variance 0.019 Information Ratio -0.366 Tracking Error 0.152 Treynor Ratio 0.064 Total Fees $0.00 Estimated Strategy Capacity $1100000.00 Lowest Capacity Asset SHY SGNKIKYGE9NP Portfolio Turnover 189.50% |
# region imports
from AlgorithmImports import *
# endregion
class KellyCriterionSMACrossoverAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2014, 1, 1)
# Remove fees to focus the research on the portfolio weighting, not the signal.
self.set_security_initializer(lambda s: s.set_fee_model(ConstantFeeModel(0)))
# Add the risky and risk-free assets.
self._risk_asset = self.add_equity('IBM', Resolution.HOUR, leverage=6)
self._rf_asset = self.add_equity('SHY', Resolution.HOUR, leverage=6)
# Add some strategy-specific indicators/variables.
self._risk_asset.short_sma = self.sma(self._risk_asset.symbol, 1)
self._risk_asset.long_sma = self.sma(self._risk_asset.symbol, 6)
# Add a warm-up period so we some historical performance of the strategy once we start trading.
self.set_warm_up(timedelta(365))
# Add a list and Scheduled Event to track the average exposure to the risky asset.
self._risky_weights = []
self.schedule.on(self.date_rules.every_day(self._risk_asset.symbol), self.time_rules.at(23, 59), self._sample_weight)
def on_data(self, data: Slice):
# Wait until the market is open.
if not data.bars or not self.is_market_open(self._risk_asset.symbol) or self.is_warming_up:
return
if not self._risk_asset.holdings.is_long and self._risk_asset.short_sma > self._risk_asset.long_sma:
self.set_holdings([PortfolioTarget(self._risk_asset.symbol, 1)], True)
elif self._risk_asset.holdings.is_long and self._risk_asset.short_sma < self._risk_asset.long_sma:
self.set_holdings([PortfolioTarget(self._rf_asset.symbol, 1)], True)
def _sample_weight(self):
self._risky_weights.append(self._risk_asset.holdings.holdings_value / self.portfolio.total_portfolio_value)
def on_end_of_algorithm(self):
self.log(f"Average weight: {sum(self._risky_weights) / len(self._risky_weights)}")