Overall Statistics |
Total Trades 448 Average Win 0.40% Average Loss -0.33% Compounding Annual Return 0.012% Drawdown 8.400% Expectancy 0.010 Net Profit 0.058% Sharpe Ratio 0.02 Probabilistic Sharpe Ratio 0.399% Loss Rate 54% Win Rate 46% Profit-Loss Ratio 1.20 Alpha 0.001 Beta -0.004 Annual Standard Deviation 0.036 Annual Variance 0.001 Information Ratio -0.847 Tracking Error 0.161 Treynor Ratio -0.168 Total Fees $8583.04 Estimated Strategy Capacity $1500000.00 Lowest Capacity Asset DAL TSAT9G6HOJ8L |
#region imports from AlgorithmImports import * from statsmodels.discrete.discrete_model import Logit #endregion class AirlineBuybacksDemo(QCAlgorithm): def Initialize(self): #1. Required: Five years of backtest history self.SetStartDate(2017, 1, 1) self.SetEndDate(2022, 1, 1) #2. Required: Alpha Streams Models: self.SetBrokerageModel(BrokerageName.AlphaStreams) #3. Required: Significant AUM Capacity self.SetCash(1000000) #4. Required: Benchmark to SPY self.SetBenchmark("SPY") self.SetPortfolioConstruction(EqualWeightingPortfolioConstructionModel()) self.SetExecution(ImmediateExecutionModel()) # Set our strategy to be take 5% profit and 5% stop loss. self.AddRiskManagement(MaximumUnrealizedProfitPercentPerSecurity(0.05)) self.AddRiskManagement(MaximumDrawdownPercentPerSecurity(0.05)) # Select the airline tickers for research. self.symbols = {} assets = ["LUV", # Southwest Airlines "DAL", # Delta Airlines "UAL", # United Airlines Holdings "AAL", # American Airlines Group "SKYW", # SkyWest Inc. "ALGT", # Allegiant Travel Co. "ALK" # Alaska Air Group Inc. ] # Call the AddEquity method with the tickers, and its corresponding resolution. Then call AddData with SmartInsiderTransaction to subscribe to their buyback transaction data. for ticker in assets: symbol = self.AddEquity(ticker, Resolution.Minute).Symbol self.symbols[symbol] = self.AddData(SmartInsiderTransaction, symbol).Symbol self.AddEquity("SPY") # Initialize the model self.BuildModel() # Set Scheduled Event Method For Our Model Recalibration every month self.Schedule.On(self.DateRules.MonthStart(), self.TimeRules.At(0, 0), self.BuildModel) # Set Scheduled Event Method For Trading self.Schedule.On(self.DateRules.EveryDay(), self.TimeRules.BeforeMarketClose("SPY", 5), self.EveryDayBeforeMarketClose) def BuildModel(self): qb = self # Call the History method with list of tickers, time argument(s), and resolution to request historical data for the symbol. history = qb.History(list(self.symbols.keys()), datetime(2015, 1, 1), datetime.now(), Resolution.Daily) # Call SPY history as reference spy = qb.History(["SPY"], datetime(2015, 1, 1), datetime.now(), Resolution.Daily) # Call the History method with list of buyback tickers, time argument(s), and resolution to request buyback data for the symbol. history_buybacks = qb.History(list(self.symbols.values()), datetime(2015, 1, 1), datetime.now(), Resolution.Daily) # Select the close column and then call the unstack method to get the close price dataframe. df = history['close'].unstack(level=0) spy_close = spy['close'].unstack(level=0) # Call pct_change to get the daily return of close price, then shift 1-step backward as prediction. ret = df.pct_change().shift(-1).iloc[:-1] ret_spy = spy_close.pct_change().shift(-1).iloc[:-1] # Get the active return active_ret = ret.sub(ret_spy.values, axis=0) # Select the ExecutionPrice column and then call the unstack method to get the dataframe. df_buybacks = history_buybacks['executionprice'].unstack(level=0) # Convert buyback history into daily mean data df_buybacks = df_buybacks.groupby(df_buybacks.index.date).mean() df_buybacks.columns = df.columns # Get the buyback premium/discount df_close = df.reindex(df_buybacks.index)[~df_buybacks.isna()] df_buybacks = (df_buybacks - df_close)/df_close # Create a dataframe to hold the buyback and 1-day forward return data data = pd.DataFrame(columns=["Buybacks", "Return"]) # Append the data into the dataframe for row, row_buyback in zip(active_ret.reindex(df_buybacks.index).itertuples(), df_buybacks.itertuples()): index = row[0] for i in range(1, df_buybacks.shape[1]+1): if row_buyback[i] != 0: data = pd.concat([data, pd.DataFrame({"Buybacks": row_buyback[i], "Return":row[i]}, index=[index])]) # Call dropna to drop NaNs data.dropna(inplace=True) # Get binary return (+/-) binary_ret = data["Return"].copy() binary_ret[binary_ret < 0] = 0 binary_ret[binary_ret > 0] = 1 # Construct a logistic regression model self.model = Logit(binary_ret.values, data["Buybacks"].values).fit() def EveryDayBeforeMarketClose(self): qb = self # Get any buyback event today history_buybacks = qb.History(list(self.symbols.values()), timedelta(days=1), Resolution.Daily) if history_buybacks.empty or "executionprice" not in history_buybacks.columns: return # Select the ExecutionPrice column and then call the unstack method to get the dataframe. df_buybacks = history_buybacks['executionprice'].unstack(level=0) # Convert buyback history into daily mean data df_buybacks = df_buybacks.groupby(df_buybacks.index.date).mean() # ============================== insights = [] # Iterate the buyback data, thne pass to the model for prediction row = df_buybacks.iloc[-1] for i in range(len(row)): prediction = self.model.predict(row[i]) # Long if the prediction predict price goes up, short otherwise. Do opposite for SPY (active return) if prediction > 0.5: insights.append( Insight.Price(row.index[i].split(".")[0], timedelta(days=1), InsightDirection.Up) ) insights.append( Insight.Price("SPY", timedelta(days=1), InsightDirection.Down) ) else: insights.append( Insight.Price(row.index[i].split(".")[0], timedelta(days=1), InsightDirection.Down) ) insights.append( Insight.Price("SPY", timedelta(days=1), InsightDirection.Up) ) self.EmitInsights(insights)