Overall Statistics |
Total Orders 126 Average Win 0.53% Average Loss -0.32% Compounding Annual Return 8.171% Drawdown 2.400% Expectancy 0.294 Start Equity 100000 End Equity 105384.2 Net Profit 5.384% Sharpe Ratio 0.043 Sortino Ratio 0.054 Probabilistic Sharpe Ratio 58.263% Loss Rate 51% Win Rate 49% Profit-Loss Ratio 1.64 Alpha -0.013 Beta 0.076 Annual Standard Deviation 0.047 Annual Variance 0.002 Information Ratio -1.997 Tracking Error 0.101 Treynor Ratio 0.027 Total Fees $238.65 Estimated Strategy Capacity $3100000000.00 Lowest Capacity Asset ES YJHOAMPYKQGX Portfolio Turnover 106.41% |
# region imports from AlgorithmImports import * # endregion class SwimmingYellowGreenBaboon(QCAlgorithm): def initialize(self): self.set_start_date(2023, 10, 1) self.set_end_date(2024, 6, 1) self.set_cash(100000) # Adds the ES chain # The default mapping mode for the continous contract is OPEN_INTEREST, but we will set it explicitly # The default data normalization mode for the continous contract is RAW, but we will set it explicitly # For other options, see https://www.quantconnect.com/docs/v2/writing-algorithms/universes/futures#12-Continous-Contracts self.future = self.add_future(Futures.Indices.SP_500_E_MINI, extended_market_hours=True, data_mapping_mode=DataMappingMode.OPEN_INTEREST, data_normalization_mode=DataNormalizationMode.RAW) self.schedule.on(self.date_rules.every_day(self.future.symbol), self.time_rules.midnight, self.reset) self.window = RollingWindow[TradeBar](60) self.selloff_window = RollingWindow[bool](3) self.selloff_range = 10 * self.future.symbol_properties.minimum_price_variation * 10 self.trailing_amount = 5 * self.future.symbol_properties.minimum_price_variation * 10 def on_data(self, data: Slice): # Get the trade bar information from the Slice object, and update the following window bar = data.bars.get(self.future.symbol) if bar: self.window.add(bar) # Do not open new positions if we hold the contract if self.portfolio[self.future.mapped].invested: return movement = self.get_market_movement() self.selloff_window.add(movement > self.selloff_range) # IF we're remained below the 1 hour range for the entire 2 minutes. if self.selloff_window.is_ready and all(self.selloff_window): self.selloff_window.reset() # If less than 1 hour close price; continues to sell off; enter buy order self.market_order(self.future.mapped, 1) # Then create a trailing stop 5 ticks below, trail up. self.trailing_stop_order(self.future.mapped, -1, self.trailing_amount, trailing_as_percentage=False) def reset(self): # Exit on end of day self.liquidate() self.selloff_window.reset() def get_market_movement(self): # Wait until we have all the elements to calculate the min and max if not self.window.is_ready: return 0 # ADD End - Start start_value = self.window[self.window.count-1].close end_value = self.window[0].close return end_value - start_value # RANGE WITHIN WINDOW #min_value = min(self.window, key=lambda bar: bar.low).low #max_value = max(self.window, key=lambda bar: bar.high).high #return max_value - min_value