Overall Statistics |
Total Orders 13337 Average Win 0.20% Average Loss -0.08% Compounding Annual Return 6.789% Drawdown 21.900% Expectancy 0.201 Start Equity 100000000 End Equity 246925033.3 Net Profit 146.925% Sharpe Ratio 0.354 Sortino Ratio 0.473 Probabilistic Sharpe Ratio 0.619% Loss Rate 67% Win Rate 33% Profit-Loss Ratio 2.62 Alpha 0.04 Beta -0.074 Annual Standard Deviation 0.094 Annual Variance 0.009 Information Ratio -0.295 Tracking Error 0.179 Treynor Ratio -0.449 Total Fees $9958666.70 Estimated Strategy Capacity $650000000.00 Lowest Capacity Asset ES YLZ9Z50BJE2P Portfolio Turnover 69.60% |
# region imports from AlgorithmImports import * # endregion class FuturesIntradayTrendFollowingWithVolailityScalingAlgorithm(QCAlgorithm): def initialize(self): self.set_start_date(2011, 1, 1) self.set_end_date(2024, 10, 1) self.set_cash(100_000_000) self.settings.minimum_order_margin_portfolio_percentage = 0 self._future = self.add_future( Futures.Indices.SP_500_E_MINI, data_normalization_mode=DataNormalizationMode.BACKWARDS_RATIO, data_mapping_mode=DataMappingMode.OPEN_INTEREST, contract_depth_offset=0 ) self._future.set_filter(lambda universe: universe.front_month()) self._future.std_dev_by_time = {} self._future.yesterdays_close = None self._trading_interval = timedelta(minutes=60) self._lookback = 252 # 1 year self._volatility_scaler = 0.5 # 50% self._weight_scaler = 5 # To utilize more cash. date_rule = self.date_rules.every_day(self._future.symbol) self.schedule.on(date_rule, self.time_rules.midnight, self._record_close_price) self.schedule.on(date_rule, self.time_rules.every(self._trading_interval), self._rebalance) self.schedule.on(date_rule, self.time_rules.before_market_close(self._future.symbol, 1), lambda: self.liquidate(self._future.mapped)) self.set_warm_up(timedelta(int(1.5*self._lookback))) def _record_close_price(self): self._future.yesterdays_close = self._future.price def _rebalance(self): # Wait until the market is open. t = self.time if (not self._future.yesterdays_close or not self._future.exchange.hours.is_open(t - self._trading_interval, False) or not self._future.exchange.hours.is_open(t + self._trading_interval - timedelta(seconds=1), False)): return # Get the intraday standard deviation. time_period = (t.hour, t.minute) if time_period not in self._future.std_dev_by_time: self._future.std_dev_by_time[time_period] = StandardDeviation(self._lookback) intraday_roc = self._future.price / self._future.yesterdays_close - 1 if not self._future.std_dev_by_time[time_period].update(t, intraday_roc) or self.is_warming_up: return std_dev = self._future.std_dev_by_time[time_period].current.value self.plot('Annualized STD %', str(time_period), 100*(std_dev*np.sqrt(252))) self.plot('Intraday ROC %', str(time_period), 100*intraday_roc) # Calculate the target weight. scaled_volaility = self._volatility_scaler * std_dev if scaled_volaility < intraday_roc: weight = intraday_roc - scaled_volaility elif intraday_roc < -scaled_volaility: weight = intraday_roc + scaled_volaility else: weight = 0 self.plot('STD', str(time_period), 100*std_dev) self.plot('Weight %', str(time_period), 100*weight) # Place orders. self.set_holdings(self._future.mapped, self._weight_scaler * weight)