Overall Statistics |
Total Trades
834
Average Win
0.51%
Average Loss
-0.58%
Compounding Annual Return
12.198%
Drawdown
19.700%
Expectancy
0.211
Net Profit
63.236%
Sharpe Ratio
0.806
Probabilistic Sharpe Ratio
33.160%
Loss Rate
36%
Win Rate
64%
Profit-Loss Ratio
0.89
Alpha
0.106
Beta
-0.033
Annual Standard Deviation
0.128
Annual Variance
0.016
Information Ratio
0.066
Tracking Error
0.181
Treynor Ratio
-3.119
Total Fees
$558.05
|
# https://quantpedia.com/strategies/short-interest-effect-long-only-version/ # # All stocks from NYSE, AMEX, and NASDAQ are part of the investment universe. The short-interest ratio is used as the predictor variable. # Stocks are sorted based on their short interest ratio, and the first percentile is held. The portfolio is equally weighted and rebalanced monthly. import fk_tools class Short_Interest_Effect(QCAlgorithm): def Initialize(self): self.SetStartDate(2015, 7, 1) self.SetEndDate(2019, 10, 1) self.SetCash(100000) # NOTE: We use only s&p 100 stocks so it's possible to fetch short interest data from quandl. self.symbols = fk_tools.sp100_stocks for symbol in self.symbols: data = self.AddEquity(symbol, Resolution.Daily) data.SetFeeModel(fk_tools.CustomFeeModel(self)) self.AddData(QuandlFINRA_ShortVolume, 'FINRA/FNSQ_' + symbol, Resolution.Daily) self.Schedule.On(self.DateRules.MonthStart(self.symbols[0]), self.TimeRules.AfterMarketOpen(self.symbols[0]), self.Rebalance) def Rebalance(self): short_interest = {} for symbol in self.symbols: if self.Securities.ContainsKey('FINRA/FNSQ_' + symbol): data = self.Securities['FINRA/FNSQ_' + symbol].GetLastData() if data != None: short_vol = data.GetProperty("SHORTVOLUME") total_vol = data.GetProperty("TOTALVOLUME") short_interest[symbol] = short_vol / total_vol if len(short_interest) == 0: return sorted_by_short_interest = sorted(short_interest.items(), key = lambda x: x[1], reverse = True) decile = int(len(sorted_by_short_interest)/10) long = [x[0] for x in sorted_by_short_interest[-decile:]] # Trade execution and rebalance count = len(long) if count == 0: return stocks_invested = [x.Key.Value for x in self.Portfolio if x.Value.Invested] for symbol in stocks_invested: if symbol not in long: self.Liquidate(symbol) for symbol in long: self.SetHoldings(symbol, 1/count) class QuandlFINRA_ShortVolume(PythonQuandl): def __init__(self): self.ValueColumnName = 'SHORTVOLUME' # also 'TOTALVOLUME' is accesible
import numpy as np sp100_stocks = ['AAPL','MSFT','AMZN','FB','BRK.B','GOOGL','GOOG','JPM','JNJ','V','PG','XOM','UNH','BAC','MA','T','DIS','INTC','HD','VZ','MRK','PFE','CVX','KO','CMCSA','CSCO','PEP','WFC','C','BA','ADBE','WMT','CRM','MCD','MDT','BMY','ABT','NVDA','NFLX','AMGN','PM','PYPL','TMO','COST','ABBV','ACN','HON','NKE','UNP','UTX','NEE','IBM','TXN','AVGO','LLY','ORCL','LIN','SBUX','AMT','LMT','GE','MMM','DHR','QCOM','CVS','MO','LOW','FIS','AXP','BKNG','UPS','GILD','CHTR','CAT','MDLZ','GS','USB','CI','ANTM','BDX','TJX','ADP','TFC','CME','SPGI','COP','INTU','ISRG','CB','SO','D','FISV','PNC','DUK','SYK','ZTS','MS','RTN','AGN','BLK'] def Return(values): return (values[-1] - values[0]) / values[0] def Volatility(values): values = np.array(values) returns = (values[1:]-values[:-1])/values[:-1] return np.std(returns) # Custom fee model class CustomFeeModel(FeeModel): def GetOrderFee(self, parameters): fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005 return OrderFee(CashAmount(fee, "USD")) # Quantpedia data # NOTE: IMPORTANT: Data order must be ascending (datewise) class QuantpediaFutures(PythonData): def GetSource(self, config, date, isLiveMode): return SubscriptionDataSource("https://quantpedia.com/backtesting_data/futures/{0}.csv".format(config.Symbol.Value), SubscriptionTransportMedium.RemoteFile, FileFormat.Csv) def Reader(self, config, line, date, isLiveMode): data = QuantpediaFutures() data.Symbol = config.Symbol try: if not line[0].isdigit(): return None split = line.split(';') data.Time = datetime.strptime(split[0], "%d.%m.%Y") + timedelta(days=1) data['settle'] = float(split[1]) data.Value = float(split[1]) except: return None return data