Overall Statistics |
Total Orders 403 Average Win 0.65% Average Loss -0.48% Compounding Annual Return 33.209% Drawdown 5.900% Expectancy 0.340 Start Equity 100000 End Equity 123689.81 Net Profit 23.690% Sharpe Ratio 1.939 Sortino Ratio 2.325 Probabilistic Sharpe Ratio 91.021% Loss Rate 43% Win Rate 57% Profit-Loss Ratio 1.35 Alpha 0.052 Beta 0.785 Annual Standard Deviation 0.087 Annual Variance 0.008 Information Ratio 0.539 Tracking Error 0.036 Treynor Ratio 0.215 Total Fees $281.20 Estimated Strategy Capacity $12000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 81.58% |
from datetime import timedelta import numpy as np from AlgorithmImports import * class MyFrameworkAlgorithm(QCAlgorithm): def Initialize(self): self.SetStartDate(2024, 1, 1) self.SetEndDate(datetime.now()) self.SetCash(100000) self.spy = self.AddEquity("SPY", Resolution.Minute).Symbol self.lookback = timedelta(minutes=5) self.position_count = 0 self.positions = [] self.halt_trades = True self.initial_price = None self.stop_loss_percent = 0.04 # Adjust stop-loss to 5% for tighter risk management self.Settings.MinimumOrderMarginPortfolioValue = 0 self.SetWarmUp(self.lookback) self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.AfterMarketOpen(self.spy, 1), self.InitialInvestment ) self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.At(10, 0), self.CheckInitialSlope ) self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.Every(timedelta(minutes=5)), self.EvaluateMarket ) self.Schedule.On( self.DateRules.EveryDay(), self.TimeRules.At(12, 15), self.SellAllPositions ) def InitialInvestment(self): if self.IsWarmingUp: return if not self.Portfolio[self.spy].Invested: self.SetHoldings(self.spy, 0.45) def CheckInitialSlope(self): history = self.History(self.spy, self.lookback, Resolution.Minute) if history.empty or self.spy not in history.index or 'close' not in history.columns: return closes = history.loc[self.spy]["close"].values if len(closes) < self.lookback.total_seconds() / 60: return x = np.arange(len(closes)) m, _ = np.polyfit(x, closes, 1) if abs(m) <= np.tan(np.radians(30)): # Slope less than 30 degrees self.halt_trades = False self.initial_price = closes[-1] else: self.halt_trades = True def EvaluateMarket(self): if self.IsWarmingUp or self.halt_trades: return history = self.History(self.spy, self.lookback, Resolution.Minute) if history.empty or self.spy not in history.index or 'close' not in history.columns: return closes = history.loc[self.spy]["close"].values if len(closes) < self.lookback.total_seconds() / 60: return current_price = closes[-1] allocation = (self.Portfolio.TotalPortfolioValue - self.Portfolio[self.spy].HoldingsValue) / current_price # Use remaining capital for trading quantity = int(allocation) if quantity > 0 and self.CanPlaceOrder(quantity, current_price): if not self.Portfolio[self.spy].Invested: self.MarketOrder(self.spy, quantity) self.positions.append(current_price) self.position_count += 1 self.SetStopLoss(self.spy, self.stop_loss_percent) elif len(self.positions) > 0 and self.position_count < 3: if current_price > self.positions[-1]: self.MarketOrder(self.spy, quantity) self.positions.append(current_price) self.position_count += 1 self.SetStopLoss(self.spy, self.stop_loss_percent) elif current_price < self.initial_price: self.halt_trades = True elif len(self.positions) == 3: if current_price > self.positions[-1]: self.MarketOrder(self.spy, quantity) sell_quantity = self.Portfolio[self.spy].Quantity / len(self.positions) if sell_quantity > 0: self.MarketOrder(self.spy, -sell_quantity) # Sell the oldest position self.positions.pop(0) self.positions.append(current_price) self.SetStopLoss(self.spy, self.stop_loss_percent) elif current_price < self.initial_price: self.halt_trades = True def CanPlaceOrder(self, quantity, price): cost = quantity * price margin_remaining = self.Portfolio.GetMarginRemaining(self.spy, OrderDirection.Buy) if self.Portfolio.Cash >= cost and margin_remaining >= cost: return True return False def SetStopLoss(self, symbol, stop_loss_percent): quantity = self.Portfolio[symbol].Quantity if quantity > 0: stop_price = self.Portfolio[symbol].AveragePrice * (1 - stop_loss_percent) self.StopMarketOrder(symbol, -quantity, stop_price) def SellAllPositions(self): if self.Portfolio.Invested: history = self.History(self.spy, self.lookback, Resolution.Minute) if history.empty or self.spy not in history.index or 'close' not in history.columns: self.Liquidate(self.spy) self.positions = [] self.position_count = 0 return closes = history.loc[self.spy]["close"].values if len(closes) < 2: self.Liquidate(self.spy) self.positions = [] self.position_count = 0 return m, _ = np.polyfit(np.arange(len(closes)), closes, 1) if m > 0: return self.Liquidate(self.spy) self.positions = [] self.position_count = 0