Overall Statistics |
Total Orders 286 Average Win 2.38% Average Loss -0.87% Compounding Annual Return 52.612% Drawdown 16.700% Expectancy 0.111 Start Equity 2000.00 End Equity 2941.18 Net Profit 47.059% Sharpe Ratio 1.647 Sortino Ratio 2.895 Probabilistic Sharpe Ratio 65.197% Loss Rate 70% Win Rate 30% Profit-Loss Ratio 2.75 Alpha 0.553 Beta 0.014 Annual Standard Deviation 0.35 Annual Variance 0.122 Information Ratio -1.805 Tracking Error 0.594 Treynor Ratio 42.298 Total Fees â‚®460.00 Estimated Strategy Capacity â‚®1300000.00 Lowest Capacity Asset ETHUSDT 2UZ Portfolio Turnover 61.94% |
from AlgorithmImports import * class TechnicalIndicatorsAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2024, 1, 1) # Set Start Date #self.SetEndDate(2024, 11, 25) # Optional: Set End Date #self.SetCash(2000) # Set Strategy Cash # Set the time zone to the Philippines (UTC+8) self.SetTimeZone("Asia/Manila") # Set Bybit as the brokerage self.SetBrokerageModel(BrokerageName.Bybit, AccountType.Margin) # Set account currency (e.g., USDT) and seeder for Bybit self.SetAccountCurrency("USDT", 2000) self.SetSecurityInitializer(BrokerageModelSecurityInitializer(self.BrokerageModel, FuncSecuritySeeder(self.GetLastKnownPrice))) # Initialize individual crypto symbols self.btcusdt = self.AddCrypto("BTCUSDT", Resolution.Minute, Market.BYBIT).Symbol self.ethusdt = self.AddCrypto("ETHUSDT", Resolution.Minute, Market.BYBIT).Symbol # Define the cryptocurrency pairs to trade self.crypto_pairs = [self.btcusdt, self.ethusdt] # Set up indicators self.indicators = {} for symbol in self.crypto_pairs: self.indicators[symbol] = { "rsi": self.RSI(symbol, 14, MovingAverageType.Wilders, Resolution.Hour), "ema10": self.EMA(symbol, 10, Resolution.Hour), "ema20": self.EMA(symbol, 20, Resolution.Hour), "ema50": self.EMA(symbol, 50, Resolution.Hour), "entry_price": None, "stop_price": None } self.Log(f"Trading pairs initialized: {[s.Value for s in self.crypto_pairs]}") # Warm up indicators with historical data self.WarmUpIndicators() def WarmUpIndicators(self): """Warm up all indicators using historical data.""" warm_up_period = 100 # Use the longest period (e.g., EMA 50) to fetch sufficient history for symbol in self.crypto_pairs: history = self.History(symbol, warm_up_period + 10, Resolution.Hour) # Fetch hourly data if history.empty: self.Log(f"No historical data found for {symbol}. Verify Bybit API data access and symbol support.") continue for time, row in history.loc[symbol].iterrows(): # Update all indicators with historical data indicators = self.indicators[symbol] if "close" in row: indicators["rsi"].Update(time, row["close"]) indicators["ema10"].Update(time, row["close"]) indicators["ema20"].Update(time, row["close"]) indicators["ema50"].Update(time, row["close"]) self.Log(f"Indicators for {symbol} warmed up successfully.") def OnData(self, data): long_candidates = [] short_candidates = [] for symbol in self.crypto_pairs: # Check if data for the symbol is available if not data.ContainsKey(symbol): self.Log(f"Missing real-time data for {symbol} at {self.Time}. Check connection to Bybit or symbol availability.") self.Debug(f"Missing real-time data for {symbol} at {self.Time}. Check connection to Bybit or symbol availability.") continue price = data[symbol].Close indicators = self.indicators[symbol] # Log indicator and price data every hour self.Log(f"{self.Time} - {symbol} Price: {price}") self.Log(f"RSI: {indicators['rsi'].Current.Value}, EMA10: {indicators['ema10'].Current.Value}, EMA20: {indicators['ema20'].Current.Value}, EMA50: {indicators['ema50'].Current.Value}") self.Debug(f"{self.Time} - {symbol} Price: {price}") self.Debug(f"RSI: {indicators['rsi'].Current.Value}, EMA10: {indicators['ema10'].Current.Value}, EMA20: {indicators['ema20'].Current.Value}, EMA50: {indicators['ema50'].Current.Value}") # Ensure all indicators are ready before using them if not (indicators["rsi"].IsReady and indicators["ema10"].IsReady and indicators["ema20"].IsReady and indicators["ema50"].IsReady): self.Log(f"Indicators not ready for {symbol} at {self.Time}.") self.Debug(f"Indicators not ready for {symbol} at {self.Time}.") continue # Long condition if (indicators["rsi"].Current.Value > 40 and indicators["ema10"].Current.Value > indicators["ema20"].Current.Value > indicators["ema50"].Current.Value): if symbol in short_candidates: # Prevent overlap self.Log(f"Symbol {symbol} qualifies for both long and short. Skipping conflicting conditions.") continue self.Log(f"Long condition met for {symbol} at {self.Time}.") long_candidates.append(symbol) # Short condition elif (indicators["rsi"].Current.Value < 70 and indicators["ema10"].Current.Value < indicators["ema20"].Current.Value < indicators["ema50"].Current.Value): if symbol in long_candidates: # Prevent overlap self.Log(f"Symbol {symbol} qualifies for both short and long. Skipping conflicting conditions.") continue self.Log(f"Short condition met for {symbol} at {self.Time}.") short_candidates.append(symbol) # Allocate funds equally across all long candidates if long_candidates: allocation_per_symbol = 0.5 / len(long_candidates) if long_candidates else 0 for symbol in long_candidates: if not self.Portfolio[symbol].Invested or self.Portfolio[symbol].Quantity < 0: # Not invested or currently short self.SetHoldings(symbol, allocation_per_symbol) # Open long position self.indicators[symbol]["entry_price"] = data[symbol].Close self.indicators[symbol]["stop_price"] = data[symbol].Close * 0.90 # Initial 10% stop-loss self.Log(f"Opened long position for {symbol} with allocation {allocation_per_symbol}.") # Allocate funds equally across all short candidates if short_candidates: allocation_per_symbol = -0.5 / len(short_candidates) if short_candidates else 0 for symbol in short_candidates: if not self.Portfolio[symbol].Invested or self.Portfolio[symbol].Quantity > 0: # Not invested or currently long self.SetHoldings(symbol, allocation_per_symbol) # Open short position self.indicators[symbol]["entry_price"] = data[symbol].Close self.indicators[symbol]["stop_price"] = data[symbol].Close * 1.10 # Initial 10% stop-loss above entry self.Log(f"Opened short position for {symbol} with allocation {allocation_per_symbol}.") # Update trailing stop loss for active positions for symbol in self.crypto_pairs: if self.Portfolio[symbol].Invested: price = data[symbol].Close entry_price = self.indicators[symbol]["entry_price"] stop_price = self.indicators[symbol]["stop_price"] if self.Portfolio[symbol].Quantity > 0: # Long position if stop_price is None: stop_price = price * 0.90 self.indicators[symbol]["stop_price"] = max(stop_price, price * 0.90) if price < self.indicators[symbol]["stop_price"]: self.Log(f"Trailing stop hit for long {symbol} at {self.Time}. Liquidating.") self.Liquidate(symbol) self.indicators[symbol]["entry_price"] = None self.indicators[symbol]["stop_price"] = None elif self.Portfolio[symbol].Quantity < 0: # Short position if stop_price is None: stop_price = price * 1.10 self.indicators[symbol]["stop_price"] = min(stop_price, price * 1.10) if price > self.indicators[symbol]["stop_price"]: self.Log(f"Trailing stop hit for short {symbol} at {self.Time}. Liquidating.") self.Liquidate(symbol) self.indicators[symbol]["entry_price"] = None self.indicators[symbol]["stop_price"] = None