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