Overall Statistics
Total Orders
1249
Average Win
0.49%
Average Loss
-0.47%
Compounding Annual Return
10.295%
Drawdown
13.100%
Expectancy
0.218
Start Equity
100000
End Equity
148051.33
Net Profit
48.051%
Sharpe Ratio
0.568
Sortino Ratio
0.838
Probabilistic Sharpe Ratio
35.982%
Loss Rate
41%
Win Rate
59%
Profit-Loss Ratio
1.05
Alpha
0.001
Beta
0.522
Annual Standard Deviation
0.084
Annual Variance
0.007
Information Ratio
-0.531
Tracking Error
0.078
Treynor Ratio
0.091
Total Fees
$660.58
Estimated Strategy Capacity
$1100000000.00
Lowest Capacity Asset
SPY R735QTJ8XC9X
Portfolio Turnover
15.78%
# region imports
from AlgorithmImports import *
import numpy as np
import pandas as pd
import statsmodels.api as sm
from datetime import date
from sklearn.linear_model import LinearRegression
# endregion

class KANYEWEST(QCAlgorithm):
    def Initialize(self):
        self.SetStartDate(2020, 7, 25)
        self.SetEndDate(2024, 7, 25)
        self.SetCash(100000)
        self.symbol = self.AddEquity("SPY", Resolution.Daily).Symbol # symbol has to be SPY
        self.set_benchmark(self.symbol)

        self.cap = 100000
        self.benchmark_chart = []
        
        # Initialize model with past data
        history = self.History(self.symbol, 1800, Resolution.Daily)
        self.model_inter, self.model_intra = self.train(history)

        # Track open stop market orders
        self.stopMarketTicket = None

    def OnData(self, data: Slice):
        self.plot_market()
        if not data.ContainsKey(self.symbol):
            return

        if self.model_inter is None or self.model_intra is None:
            self.Log("Models are not trained. Skipping prediction.")
            return
        
        history = self.History(self.symbol, 35, Resolution.Daily)
        formatted_data = self.prepare_data(history)
        
        if formatted_data.empty:
            self.Log("Formatted data is empty. Skipping this OnData call.")
            return

        # Get latest features
        latest_data = formatted_data.iloc[[-1]][['0_inter', '0_intra', '1_inter', '1_intra', '2_inter', '2_intra', 'SMA_10', 'SMA_20']]

        # if date.day == 15:
        #     self.liquidate()
        
        # Get predictions
        inter_pred = self.model_inter.predict(latest_data)[0]
        intra_pred = self.model_intra.predict(latest_data)[0]
        
        self.Log(f"Date: {self.Time.date()}, Interday Prediction: {inter_pred:.4f}, Intraday Prediction: {intra_pred:.4f}")

        # Cancel any existing stop market orders before making a new trade
        if self.stopMarketTicket is not None and self.stopMarketTicket.Status == OrderStatus.Submitted:
            self.Transactions.CancelOrder(self.stopMarketTicket.OrderId)
            self.stopMarketTicket = None

        # paramseters to tune
        buy_threshold = 0.5
        sell_threshold = 0.4
        short_threshold = 0.35
        portfolio_weight = 0.7
        short_weight = 0.7

        # trading logic
        if inter_pred > buy_threshold and intra_pred > buy_threshold:  # Strong bullish signal
            self.SetHoldings(self.symbol, portfolio_weight)
            purchase_price = self.Portfolio[self.symbol].Price
            self.RecordPurchase(self.Time, 0.5 * purchase_price)
            self.PlaceStopMarketOrder(purchase_price, direction="long")
        # elif inter_pred < sell_threshold and intra_pred < sell_threshold: 
        elif inter_pred < sell_threshold and inter_pred > short_threshold and intra_pred < buy_threshold and intra_pred > short_threshold: 
            pass
        elif inter_pred < short_threshold and intra_pred < short_threshold: # Strong bearish signal
            self.SetHoldings(self.symbol, -short_weight) #portfolio_weight)
            purchase_price = self.Portfolio[self.symbol].Price
            self.RecordPurchase(self.Time, 0.5 * purchase_price)
            self.PlaceStopMarketOrder(purchase_price, direction="short")

        

    def PlaceStopMarketOrder(self, purchase_price, direction):
        if direction == "long":
            stop_price = purchase_price * 0.99  # 1% below purchase price
            self.stopMarketTicket = self.StopMarketOrder(self.symbol, -self.Portfolio[self.symbol].Quantity, stop_price)
            self.Log(f"Placed stop market order to sell at ${stop_price:.2f} (0.5% below purchase price).")
        elif direction == "short":
            stop_price = purchase_price * 1.01  # 1% above purchase price
            self.stopMarketTicket = self.StopMarketOrder(self.symbol, -self.Portfolio[self.symbol].Quantity, stop_price)
            self.Log(f"Placed stop market order to cover at ${stop_price:.2f} (0.5% above purchase price).")

    def RecordPurchase(self, date, amount):
        self.Log(f"Purchase recorded: Date: {date}, Amount: ${amount:.2f}")

    def OnOrderEvent(self, orderEvent):
        # Check if the stop market order was filled
        if self.stopMarketTicket is not None and orderEvent.OrderId == self.stopMarketTicket.OrderId:
            if orderEvent.Status == OrderStatus.Filled:
                execution_price = orderEvent.FillPrice
                execution_date = self.Time
                self.Log(f"Stop market order executed: Date: {execution_date}, Price: ${execution_price:.2f}")
                # Clear the stop market ticket after execution
                self.stopMarketTicket = None


    def train(self, history):
        df = pd.DataFrame(history)
        grads = self.prepare_data(df)
        if grads.empty:
            self.Log("Training data is empty. Cannot train model.")
            return None, None

        """


        Implement your own model and training here.
    
        
        """
        
        # Prepare features and targets
        X = grads[['0_inter', '0_intra', '1_inter', '1_intra', '2_inter', '2_intra', 'SMA_10', 'SMA_20']]
        y_inter = grads['0_inter'].shift(1)[grads.index]
        y_intra = grads['0_intra'].shift(1)[grads.index]
        
        # Remove first row since it won't have target values
        X = X[20:]
        y_inter = y_inter[20:]
        y_intra = y_intra[20:]
        
        # Train models
        self.model_inter = LinearRegression()
        self.model_intra = LinearRegression()
        
        self.model_inter.fit(X, y_inter)
        self.model_intra.fit(X, y_intra)
        
        return (self.model_inter, self.model_intra)


    def prepare_data(self, df):
        df = df.reset_index()
        if 'index' in df.columns:
            df.rename(columns={'index': 'time'}, inplace=True)
        elif 'time' not in df.columns:
            df.rename(columns={'time': 'time'}, inplace=True)

        """

        Implement your own feature engineering and data preparation here. 


        """

        df['Date'] = pd.to_datetime(df['time'])
        df.set_index('Date', inplace=True)
        
        df['intraday_grads'] = (df['close'] / df['open'] - 1).dropna()
        df['interday_grads'] = (df['open'] / df['close'].shift(1) - 1).dropna()
        df['intraday_grads_norm'] = (df['intraday_grads'] - df['intraday_grads'].min()) / (df['intraday_grads'].max() - df['intraday_grads'].min())
        df['interday_grads_norm'] = (df['interday_grads'] - df['interday_grads'].min()) / (df['interday_grads'].max() - df['interday_grads'].min())
        
        grads = df[['interday_grads_norm', 'intraday_grads_norm']]
        grads.columns = ['0_inter', '0_intra']
        grads['1_inter'] = df['interday_grads_norm'].shift(-1)
        grads['1_intra'] = df['intraday_grads_norm'].shift(-1)
        grads['2_inter'] = df['interday_grads_norm'].shift(-2)
        grads['2_intra'] = df['intraday_grads_norm'].shift(-2)
        grads['SMA_10'] = df['close'].rolling(window=10).mean()  # 10-day Simple Moving Average
        grads['SMA_20'] = df['close'].rolling(window=20).mean()
        
        return grads.dropna()

    def plot_market(self): #plot the market on the Startegy Equity Chart with your portfolio
        hist = self.History([self.symbol], 252, Resolution.Daily)['close'].unstack(level=0).dropna()
        self.benchmark_chart.append(hist[self.symbol].iloc[-1])
        benchmark_perf = self.benchmark_chart[-1] / self.benchmark_chart[0] * self.cap
        self.Plot("Strategy Equity", "Buy & Hold", benchmark_perf)