Created with Highcharts 12.1.2EquityJan 2019Jan…Feb 2019Mar 2019Apr 2019May 2019Jun 2019Jul 2019Aug 2019Sep 2019Oct 2019Nov 2019Dec 2019Jan 2020500k1,000k1,500k2,000k-20-100012-2020100M200M010M20M02040
Overall Statistics
Total Orders
382
Average Win
0.34%
Average Loss
-0.19%
Compounding Annual Return
54.956%
Drawdown
12.000%
Expectancy
0.950
Start Equity
1000000
End Equity
1548945.06
Net Profit
54.895%
Sharpe Ratio
1.669
Sortino Ratio
2.654
Probabilistic Sharpe Ratio
75.465%
Loss Rate
31%
Win Rate
69%
Profit-Loss Ratio
1.81
Alpha
0.129
Beta
1.226
Annual Standard Deviation
0.214
Annual Variance
0.046
Information Ratio
0.989
Tracking Error
0.173
Treynor Ratio
0.291
Total Fees
$3348.95
Estimated Strategy Capacity
$130000000.00
Lowest Capacity Asset
JNJ R735QTJ8XC9X
Portfolio Turnover
6.20%
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, 15 if symbol == "AMD" else 25, 2.5 if symbol == "AMD" else 2.0, 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.35 if sma50 > sma200 else -0.35

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

            targetWeight = baseWeight
            if bullishSignal and baseWeight > 0:
                targetWeight = 0.75  # Increased from 0.5
            elif bearishSignal and baseWeight < 0:
                targetWeight = -0.75  # Increased from -0.5

            # Scale back position if momentum weakens but allow more flexibility
            if self.Portfolio[symbol].IsLong and (rsi >= 80 or price < middleBB):
                targetWeight = 0.5  # Increased from 0.35
            if self.Portfolio[symbol].IsShort and (rsi <= 20 or price > middleBB):
                targetWeight = -0.5  # Increased from -0.35

            # 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}")