| 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