Overall Statistics
Total Orders
398
Average Win
1.36%
Average Loss
-0.60%
Compounding Annual Return
3.031%
Drawdown
21.700%
Expectancy
0.071
Start Equity
100000
End Equity
125253.25
Net Profit
25.253%
Sharpe Ratio
-0.026
Sortino Ratio
-0.028
Probabilistic Sharpe Ratio
0.707%
Loss Rate
67%
Win Rate
33%
Profit-Loss Ratio
2.26
Alpha
0.01
Beta
-0.154
Annual Standard Deviation
0.101
Annual Variance
0.01
Information Ratio
-0.381
Tracking Error
0.214
Treynor Ratio
0.017
Total Fees
$841.92
Estimated Strategy Capacity
$1000000000.00
Lowest Capacity Asset
MSFT R735QTJ8XC9X
Portfolio Turnover
7.20%
Drawdown Recovery
392
# region imports
from AlgorithmImports import *
# endregion

class MALongShortStrategy(QCAlgorithm):
    """Long-Short strategy on AAPL and MSFT using 20-day MA crossover"""

    def initialize(self):
        # Set start date, end date, and cash
        self.set_start_date(2018, 1, 1)
        self.set_end_date(2025, 10, 13)
        self.set_cash(100000)
        
        # Add equity data
        self.aapl = self.add_equity("AAPL", Resolution.DAILY).symbol
        self.msft = self.add_equity("MSFT", Resolution.DAILY).symbol
        
        # Create 20-day Simple Moving Average indicators
        self.aapl_sma = self.sma(self.aapl, 20, Resolution.DAILY)
        self.msft_sma = self.sma(self.msft, 20, Resolution.DAILY)
        
        # Warm up the indicators
        self.set_warm_up(20)
        
        # Track positions
        self.aapl_position = 0
        self.msft_position = 0
        
    def on_data(self, data: Slice):
        # Skip if we're still warming up
        if self.is_warming_up:
            return
            
        # Check if indicators are ready and we have data
        if not (self.aapl_sma.is_ready and self.msft_sma.is_ready):
            return
            
        if not (data.contains_key(self.aapl) and data.contains_key(self.msft)):
            return
        
        # Get current prices
        aapl_price = data[self.aapl].price
        msft_price = data[self.msft].price
        
        # Get MA values
        aapl_ma = self.aapl_sma.current.value
        msft_ma = self.msft_sma.current.value
        
        # AAPL Trading Logic
        # Long when price > MA, Short when price < MA
        if aapl_price > aapl_ma and self.aapl_position <= 0:
            # Go long AAPL (25% of portfolio)
            self.set_holdings(self.aapl, 0.25)
            self.aapl_position = 1
            self.debug(f"AAPL Long: Price {aapl_price:.2f} > MA {aapl_ma:.2f}")
            
        elif aapl_price < aapl_ma and self.aapl_position >= 0:
            # Go short AAPL (25% of portfolio)
            self.set_holdings(self.aapl, -0.25)
            self.aapl_position = -1
            self.debug(f"AAPL Short: Price {aapl_price:.2f} < MA {aapl_ma:.2f}")
        
        # MSFT Trading Logic
        # Long when price > MA, Short when price < MA
        if msft_price > msft_ma and self.msft_position <= 0:
            # Go long MSFT (25% of portfolio)
            self.set_holdings(self.msft, 0.25)
            self.msft_position = 1
            self.debug(f"MSFT Long: Price {msft_price:.2f} > MA {msft_ma:.2f}")
            
        elif msft_price < msft_ma and self.msft_position >= 0:
            # Go short MSFT (25% of portfolio)
            self.set_holdings(self.msft, -0.25)
            self.msft_position = -1
            self.debug(f"MSFT Short: Price {msft_price:.2f} < MA {msft_ma:.2f}")