Overall Statistics |
Total Orders 1580 Average Win 3.36% Average Loss -2.38% Compounding Annual Return 284.614% Drawdown 56.900% Expectancy 0.131 Start Equity 100000 End Equity 541442.65 Net Profit 441.443% Sharpe Ratio 2.988 Sortino Ratio 3.165 Probabilistic Sharpe Ratio 73.851% Loss Rate 53% Win Rate 47% Profit-Loss Ratio 1.41 Alpha 2.159 Beta 4.143 Annual Standard Deviation 0.929 Annual Variance 0.864 Information Ratio 2.969 Tracking Error 0.885 Treynor Ratio 0.67 Total Fees $42954.85 Estimated Strategy Capacity $320000000.00 Lowest Capacity Asset NQ YGT6HGVF2SQP Portfolio Turnover 5175.76% |
# region imports from AlgorithmImports import * from datetime import timedelta import numpy as np from sklearn.linear_model import LinearRegression # endregion class Volumeprofile(QCAlgorithm): def initialize(self): self.set_start_date(2023, 1, 1) self.set_end_date(2024, 6, 1) self.set_cash(100000) self.future_chains = None # Set the symbol of the asset we want to trade future = self.add_future(Futures.Indices.NASDAQ_100_E_MINI, Resolution.Minute) future.SetFilter(timedelta(0), timedelta(182)) self.symbol = future.Symbol # Volume Profile indicator settings self.profile_period = 120 # 2 hours self.value_area_percentage = 0.4 self.volume_profile = VolumeProfile("Volume Profile", self.profile_period, self.value_area_percentage) # Rolling window to store past prices self.past_prices_period = 20 self.past_prices = RollingWindow[TradeBar](self.past_prices_period) # Long or short position self.is_long = True # Consolidate data self.Consolidate(self.symbol, timedelta(minutes=1), self.on_data_consolidated) self.register_indicator(self.symbol, self.volume_profile, timedelta(hours = 2)) # Setting stoploss self.stop_loss_len = 100 self.stop_loss_indicator = self.MIN(self.symbol, self.stop_loss_len, Resolution.MINUTE) self.lowest_low = 0 self.stop_loss = 0 self.start_stop_loss = False # Warm up period self.set_warm_up(timedelta(hours = 2)) # Free portfolio setting self.settings.free_portfolio_value = 0.3 def on_data_consolidated(self, data: Slice): # Store the past prices of the future contract self.past_prices.Add(data) def on_data(self, data: Slice): # Check if the strategy warm up period is over and indicators are ready if self.is_warming_up and not self.volume_profile.is_ready: return # Find the future contract for chain in data.FutureChains: self.popular_contracts = [contract for contract in chain.value if contract.open_interest > 1000] if len(self.popular_contracts) == 0: continue sorted_bt_o_i_contracts = sorted(self.popular_contracts, key=lambda k: k.open_interest, reverse=True) self.future_contract = sorted_bt_o_i_contracts[0] if self.past_prices.is_ready and self.volume_profile.is_ready: past_prices = [x.Close for x in self.past_prices if x is not None] slope = self.compute_slope(past_prices) # Check if price is moving towards the value area current_price = self.past_prices[0].Close # Entry if not self.portfolio.invested: if (self.volume_profile.value_area_low <= current_price <= self.volume_profile.value_area_high): # Long condition if slope < -0.5: # self.log("Price is moving up towards the value area") self.set_holdings(self.future_contract.symbol, 1) self.stop_loss = self.stop_loss_indicator.Current.Value self.start_stop_loss = True # self.is_long = True # elif slope < 0: # self.log("Price is moving down towards the value area") # self.set_holdings(self.symbol, -1) # self.take_profit = self.volume_profile.profile_low # self.stop_loss = self.volume_profile.poc_price # self.is_long = False # Exit if self.portfolio.invested and self.start_stop_loss: # Register stop loss and take profit levels if self.past_prices.IsReady: if self.past_prices[0].Close > self.past_prices[1].Close: self.stop_loss += (self.past_prices[0].Close - self.past_prices[1].Close) # Stop loss # if data.Close < self.stop_loss or not self.Time.hour in range(9, 16): if current_price < self.stop_loss: self.log(f"Stop loss at {current_price}") self.liquidate(self.future_contract.symbol) self.start_stop_loss = False # elif self.portfolio.invested and not self.is_long: # if current_price <= self.take_profit or current_price >= self.stop_loss: # self.log("Closing short position") # self.Liquidate() # # Plotting the data # # self.plot("VolumeProfile","vp", self.volume_profile.current.value) # self.plot("VolumeProfile","profile_high", self.volume_profile.profile_high) # self.plot("VolumeProfile","profile_low", self.volume_profile.profile_low) # self.plot("VolumeProfile","poc_price", self.volume_profile.poc_price) # # self.plot("VolumeProfile","poc_volume", self.volume_profile.poc_volume) # # self.plot("VolumeProfile","value_area_volume", self.volume_profile.value_area_volume) # self.plot("VolumeProfile","value_area_high", self.volume_profile.value_area_high) # self.plot("VolumeProfile","value_area_low", self.volume_profile.value_area_low) # self.plot("VolumeProfile","current_price", self.past_prices[0].Close) def compute_slope(self, prices: list) -> float: # Convert list to numpy array and reshape to 2D for sklearn prices_array = np.array(prices).reshape(-1, 1) # Create an array of indices representing time times = np.array(range(len(prices))).reshape(-1, 1) # Fit a linear regression model model = LinearRegression().fit(times, prices_array) # Return the slope of the regression line return model.coef_[0][0]