Created with Highcharts 12.1.2EquityJan 2019Jan…Feb 2019Mar 2019Apr 2019May 2019Jun 2019Jul 2019Aug 2019Sep 2019Oct 2019Nov 2019Dec 2019Jan 2020750k1,000k1,250k1,500k-10-5000.51-10120250M500M05M102030
Overall Statistics
Total Orders
299
Average Win
0.16%
Average Loss
-0.14%
Compounding Annual Return
23.826%
Drawdown
6.300%
Expectancy
0.576
Start Equity
1000000
End Equity
1238014.63
Net Profit
23.801%
Sharpe Ratio
1.123
Sortino Ratio
1.616
Probabilistic Sharpe Ratio
62.126%
Loss Rate
25%
Win Rate
75%
Profit-Loss Ratio
1.09
Alpha
0.003
Beta
0.741
Annual Standard Deviation
0.125
Annual Variance
0.016
Information Ratio
-0.444
Tracking Error
0.102
Treynor Ratio
0.19
Total Fees
$1100.72
Estimated Strategy Capacity
$89000000.00
Lowest Capacity Asset
JNJ R735QTJ8XC9X
Portfolio Turnover
2.14%
from AlgorithmImports import * 

class MomentumTrendStrategy(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2019, 1, 1)
        self.SetEndDate(2020, 1, 1)
        self.SetCash(1000000)

        # Add AMD and JNJ (daily resolution)
        self.symbols = []
        self.symbols.append(self.AddEquity("AMD", Resolution.Daily).Symbol)
        self.symbols.append(self.AddEquity("JNJ", Resolution.Daily).Symbol)

        # Dictionary to hold indicators for each stock
        self.indicators = {}
        for symbol in self.symbols:
            self.indicators[symbol] = {
                "sma50": self.SMA(symbol, 50, Resolution.Daily),
                "sma200": self.SMA(symbol, 200, Resolution.Daily),
                "rsi": self.RSI(symbol, 14, MovingAverageType.Simple, Resolution.Daily),
                "bb": self.BB(symbol, 20, 2, Resolution.Daily)
            }

        # Warm up indicators
        self.SetWarmUp(200)

    def OnData(self, data):
        if self.IsWarmingUp:
            return

        for symbol in self.symbols:
            # Ensure the indicators are ready
            ind = self.indicators[symbol]
            if not all([ind["sma50"].IsReady, ind["sma200"].IsReady, ind["rsi"].IsReady, ind["bb"].IsReady]):
                continue

            # Get the current price from the security object instead of the data slice
            price = self.Securities[symbol].Price
            sma50 = ind["sma50"].Current.Value
            sma200 = ind["sma200"].Current.Value
            rsi = ind["rsi"].Current.Value
            bb = ind["bb"]
            upperBB = bb.UpperBand.Current.Value
            middleBB = bb.MiddleBand.Current.Value
            lowerBB = bb.LowerBand.Current.Value

            # Define base position (trend-following component)
            baseWeight = 0.25 if sma50 > sma200 else -0.25

            # Adjust exposure based on momentum signals
            bullishSignal = price > upperBB and rsi > 50
            bearishSignal = price < lowerBB and rsi < 50

            targetWeight = baseWeight
            if bullishSignal and baseWeight > 0:
                targetWeight = 0.5
            elif bearishSignal and baseWeight < 0:
                targetWeight = -0.5

            # Scale back position if momentum weakens
            if self.Portfolio[symbol].IsLong and (rsi >= 70 or price < middleBB):
                targetWeight = 0.25
            if self.Portfolio[symbol].IsShort and (rsi <= 30 or price > middleBB):
                targetWeight = -0.25

            # Ensure minimum absolute exposure is 25%
            if abs(targetWeight) < 0.25:
                targetWeight = 0.25 if baseWeight > 0 else -0.25

            self.SetHoldings(symbol, targetWeight)

            # Debugging output
            self.Debug(f"{self.Time} {symbol.Value}: Price={price:.2f}, SMA50={sma50:.2f}, "
                       f"SMA200={sma200:.2f}, RSI={rsi:.2f}, UpperBB={upperBB:.2f}, "
                       f"MiddleBB={middleBB:.2f}, LowerBB={lowerBB:.2f}, Target Weight={targetWeight:.2f}")