Overall Statistics
Total Trades
3046
Average Win
0.52%
Average Loss
-0.53%
Compounding Annual Return
-0.709%
Drawdown
27.400%
Expectancy
-0.005
Net Profit
-7.917%
Sharpe Ratio
-0.049
Probabilistic Sharpe Ratio
0.004%
Loss Rate
50%
Win Rate
50%
Profit-Loss Ratio
0.99
Alpha
-0.003
Beta
-0.001
Annual Standard Deviation
0.07
Annual Variance
0.005
Information Ratio
-0.804
Tracking Error
0.17
Treynor Ratio
2.732
Total Fees
$1530.48
Estimated Strategy Capacity
$66000000.00
Lowest Capacity Asset
D R735QTJ8XC9X
# 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.

class ShortInterestEffect(QCAlgorithm):

    def Initialize(self):
        self.SetStartDate(2010, 1, 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 = [
            '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'
            ]
        
        for symbol in self.symbols:
            data = self.AddEquity(symbol, Resolution.Daily)
            data.SetFeeModel(CustomFeeModel(self))
            data.SetLeverage(5)
            
            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:
                    short_vol = data.GetProperty("SHORTVOLUME")
                    total_vol = data.GetProperty("TOTALVOLUME")
                    
                    short_interest[symbol] = short_vol / total_vol
            
        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:]]
        short = [x[0] for x in sorted_by_short_interest[:decile]]
                
        # Trade execution.
        stocks_invested = [x.Key.Value for x in self.Portfolio if x.Value.Invested]
        for symbol in stocks_invested:
            if symbol not in long + short:
                self.Liquidate(symbol)

        for symbol in long:
            if self.Securities[symbol].Price != 0 and self.Securities[symbol].IsTradable:
                self.SetHoldings(symbol, 1 / len(long))
        for symbol in short:
            if self.Securities[symbol].Price != 0 and self.Securities[symbol].IsTradable:
                self.SetHoldings(symbol, -1 / len(short))
            
class QuandlFINRA_ShortVolume(PythonQuandl):
    def __init__(self):
        self.ValueColumnName = 'SHORTVOLUME'    # also 'TOTALVOLUME' is accesible

# Custom fee model.
class CustomFeeModel(FeeModel):
    def GetOrderFee(self, parameters):
        fee = parameters.Security.Price * parameters.Order.AbsoluteQuantity * 0.00005
        return OrderFee(CashAmount(fee, "USD"))