Overall Statistics |
Total Orders
49271
Average Win
0.04%
Average Loss
-0.04%
Compounding Annual Return
7.898%
Drawdown
48.500%
Expectancy
0.067
Start Equity
100000
End Equity
179756.05
Net Profit
79.756%
Sharpe Ratio
0.265
Sortino Ratio
0.284
Probabilistic Sharpe Ratio
1.966%
Loss Rate
44%
Win Rate
56%
Profit-Loss Ratio
0.91
Alpha
0.095
Beta
-0.534
Annual Standard Deviation
0.18
Annual Variance
0.032
Information Ratio
-0.143
Tracking Error
0.285
Treynor Ratio
-0.089
Total Fees
$396.36
Estimated Strategy Capacity
$0
Lowest Capacity Asset
AFAR XZ06GLQJ9LK5
Portfolio Turnover
2.28%
|
# https://quantpedia.com/strategies/short-interest-effect-long-short-version/ # # All stocks from NYSE, AMEX, and NASDAQ are part of the investment universe. Stocks are then sorted each month into short-interest deciles based on # the ratio of short interest to shares outstanding. The investor then goes long on the decile with the lowest short ratio and short on the decile # with the highest short ratio. The portfolio is rebalanced monthly, and stocks in the portfolio are weighted equally. from AlgorithmImports import * from io import StringIO from typing import List, Dict from numpy import isnan class ShortInterestEffect(QCAlgorithm): def Initialize(self) -> None: self.SetStartDate(2017, 1, 1) self.SetCash(100_000) self.quantile: int = 10 self.leverage: int = 5 self.weight: Dict[Symbol, float] = {} # source: https://www.finra.org/finra-data/browse-catalog/equity-short-interest/data text: str = self.Download('data.quantpedia.com/backtesting_data/economic/short_volume.csv') self.short_volume_df: DataFrame = pd.read_csv(StringIO(text), delimiter=';') self.short_volume_df['date'] = pd.to_datetime(self.short_volume_df['date']).dt.date self.short_volume_df.set_index('date', inplace=True) self.selection_flag: bool = False self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.FundamentalSelectionFunction) self.Settings.MinimumOrderMarginPortfolioPercentage = 0. self.settings.daily_precise_end_time = False self.recent_month = -1 def OnSecuritiesChanged(self, changes: SecurityChanges) -> None: for security in changes.AddedSecurities: security.SetFeeModel(CustomFeeModel()) security.SetLeverage(self.leverage) def FundamentalSelectionFunction(self, fundamental: List[Fundamental]) -> List[Symbol]: # monthly rebalance if self.recent_month == self.Time.month: return Universe.Unchanged self.recent_month = self.Time.month self.selection_flag = True # check last date on custom data if self.Time.date() > self.short_volume_df.index[-1] or self.Time.date() < self.short_volume_df.index[0]: self.Liquidate() return Universe.Unchanged selected: List[Fundamental] = [ x for x in fundamental if x.HasFundamentalData and x.Market == 'usa' and x.CompanyProfile.SharesOutstanding != 0 and x.Symbol.Value in self.short_volume_df.columns ] short_interest: Dict[Symbol, float] = {} # calculate short interest for stock in selected: symbol: Symbol = stock.Symbol ticker: str = symbol.Value if ticker in self.short_volume_df.columns: if isnan(self.short_volume_df[self.short_volume_df.index <= self.Time.date()][ticker][-1]): continue short_interest[symbol] = self.short_volume_df[self.short_volume_df.index <= self.Time.date()][ticker][-1] / stock.CompanyProfile.SharesOutstanding if len(short_interest) >= self.quantile: # sorting by short interest ratio sorted_short_interest: List[Fundamental] = sorted(short_interest, key = short_interest.get) quantile: int = int(len(sorted_short_interest) / self.quantile) long: List[Fundamental] = sorted_short_interest[:quantile] short: List[Fundamental] = sorted_short_interest[-quantile:] # equaly weighting for i, portfolio in enumerate([long, short]): for symbol in portfolio: self.weight[symbol] = ((-1) ** i) / len(portfolio) return list(self.weight.keys()) def OnData(self, data: Slice) -> None: if not self.selection_flag: return self.selection_flag = False # trade execution portfolio:List[PortfolioTarget] = [PortfolioTarget(symbol, w) for symbol, w in self.weight.items() if symbol in data and data[symbol]] self.SetHoldings(portfolio, True) self.weight.clear() # Custom fee model. class CustomFeeModel(FeeModel): def GetOrderFee(self, parameters: OrderFeeParameters) -> OrderFee: fee: float = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005 return OrderFee(CashAmount(fee, "USD"))