Overall Statistics
Total Orders
648
Average Win
1.51%
Average Loss
-0.40%
Compounding Annual Return
8.497%
Drawdown
11.600%
Expectancy
0.166
Start Equity
100000.00
End Equity
117767.59
Net Profit
17.768%
Sharpe Ratio
0.497
Sortino Ratio
0.984
Probabilistic Sharpe Ratio
18.324%
Loss Rate
76%
Win Rate
24%
Profit-Loss Ratio
3.80
Alpha
0.056
Beta
0.031
Annual Standard Deviation
0.123
Annual Variance
0.015
Information Ratio
-0.488
Tracking Error
0.238
Treynor Ratio
1.961
Total Fees
$0.00
Estimated Strategy Capacity
$480000.00
Lowest Capacity Asset
BTCUSD 2XR
Portfolio Turnover
76.28%
Drawdown Recovery
382
# region imports
from AlgorithmImports import *
# endregion

class MultiIndicatorStrategy(QCAlgorithm):

    def Initialize(self):
        
        self.set_start_date(2020,1,1)
        self.set_end_date(2022, 1, 1)

        self.set_cash(100000)

        self.btc = self.add_crypto("BTCUSD", Resolution.MINUTE, Market.COINBASE)

        fast_period = 50
        slow_period = 200
        rsi_period = 14
        atr_period = 14
        adx_period = 14
        volume_period = 25
        self.rsi_overbought = 80
        self.atr_multiplier = 3.0 
        self.adx_strength_threshold = 30

        self.fast_ema = self.ema(self.btc.Symbol, fast_period, Resolution.MINUTE)
        self.slow_ema = self.ema(self.btc.Symbol, slow_period, Resolution.MINUTE)
        self.RSI = self.rsi(self.btc.Symbol, rsi_period, MovingAverageType.EXPONENTIAL, Resolution.MINUTE)
        self.ATR = self.atr(self.btc.Symbol, atr_period, MovingAverageType.EXPONENTIAL, Resolution.MINUTE)
        self.ADX = self.adx(self.btc.symbol, adx_period, Resolution.MINUTE)
        self.MACD = self.macd(self.btc.symbol, 12, 26, 9, MovingAverageType.EXPONENTIAL, Resolution.MINUTE)
        self.volume_sma = self.sma(self.btc.symbol, volume_period, Resolution.MINUTE, Field.VOLUME)

        self.previous_direction = 0
        self.stop_loss_ticket = None

    def OnData(self, data: Slice):
        
        if not all([self.slow_ema.is_ready, self.RSI.is_ready, self.ATR.is_ready, self.ADX.is_ready, self.MACD.is_ready, self.volume_sma.is_ready]):
            return

        current_direction = 0
        if self.fast_ema.Current.Value > self.slow_ema.Current.Value:
            current_direction = 1  # Bullish
        elif self.fast_ema.Current.Value < self.slow_ema.Current.Value:
            current_direction = -1 # Bearish
        is_strong_trend = self.ADX.current.value > self.adx_strength_threshold
        is_momentum_confirmed = self.MACD.current.value > self.MACD.signal.current.value
        is_volume_confirmed = self.securities[self.btc.symbol].volume > self.volume_sma.current.value

        is_new_crossover = self.previous_direction != current_direction

        if is_new_crossover and current_direction == 1:
            if not self.portfolio.invested and self.RSI.Current.Value < self.rsi_overbought and is_strong_trend and is_momentum_confirmed and is_volume_confirmed:
                self.log(f"Entry Signal: Strong Trend (ADX>{self.adx_strength_threshold}), MACD Bullish. Buying BTC.")

                self.set_holdings(self.btc.Symbol, 1.0)

                stop_price = self.securities[self.btc.Symbol].Close - (self.ATR.Current.Value * self.atr_multiplier)
                self.stop_loss_ticket = self.stop_market_order(self.btc.Symbol, -self.portfolio[self.btc.Symbol].quantity, stop_price)
                self.log(f"Stop-loss order placed at {stop_price:.2f}")

        elif is_new_crossover and current_direction == -1:
            if self.portfolio.invested:
                self.log("Bearish Crossover: Liquidating BTC position.")
                self.liquidate(self.btc.Symbol)
                if self.stop_loss_ticket:
                    self.stop_loss_ticket.Cancel()
                    self.stop_loss_ticket = None

        self.previous_direction = current_direction

    def OnOrderEvent(self, orderEvent):
        if orderEvent.Status == OrderStatus.FILLED:
            self.log(f"Filled order: {orderEvent.Symbol} - Quantity: {orderEvent.FillQuantity} @ {orderEvent.FillPrice:.2f}")

            if self.stop_loss_ticket and self.stop_loss_ticket.OrderId == orderEvent.OrderId:
                self.log(f"Stop-loss triggered for {orderEvent.Symbol}.")
                self.stop_loss_ticket = None

        if orderEvent.Status == OrderStatus.CANCELED:
            if self.stop_loss_ticket and self.stop_loss_ticket.OrderId == orderEvent.OrderId:
                self.stop_loss_ticket = None