Overall Statistics |
Total Trades
963
Average Win
0.74%
Average Loss
-0.83%
Compounding Annual Return
-0.183%
Drawdown
32.600%
Expectancy
-0.016
Net Profit
-3.555%
Sharpe Ratio
-0.001
Probabilistic Sharpe Ratio
0.000%
Loss Rate
48%
Win Rate
52%
Profit-Loss Ratio
0.90
Alpha
-0.001
Beta
0.017
Annual Standard Deviation
0.058
Annual Variance
0.003
Information Ratio
-0.367
Tracking Error
0.192
Treynor Ratio
-0.002
Total Fees
$164.96
|
# https://quantpedia.com/strategies/advertising-effect-within-stocks/ # # The investment universe consists of firms from NYSE, Amex and Nasdaq with a market capitalization greater than $20 million in the prior year. # Investor defines year t as the advertising year, year t − 1 as the year prior to the advertising year, and year t + 1 as the year subsequent # to the advertising year. Change in advertising in year t (∆Advt) is measured as the change in the log values of advertising expenditures # from year t − 1 to year t. # Investor ranks stocks into ten deciles every year based on ∆Advt. He then forms a zero-investment portfolio that shorts the stocks in decile # 10 (high advertising stocks) and longs the stocks in decile 1 (low advertising stocks). Stocks are bought in the 7th month of the year # subsequent to the advertising year (to ensure that investor has all financial data from previous year) and are held for subsequent 6 months. # Stocks in the portfolio are equally weighted. from collections import deque import numpy as np import fk_tools class Advertising_Effect(QCAlgorithm): def Initialize(self): self.SetStartDate(2000, 1, 1) self.SetEndDate(2019, 10, 1) self.SetCash(100000) self.course_count = 1000 self.long = [] self.short = [] self.adv_expenses = {} self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.CoarseSelectionFunction, self.FineSelectionFunction) self.symbol = 'SPY' self.AddEquity(self.symbol, Resolution.Daily) self.record_adv_expenses_flag = False self.selection_flag = False self.Schedule.On(self.DateRules.MonthEnd(self.symbol), self.TimeRules.AfterMarketOpen(self.symbol), self.Rebalance) def OnSecuritiesChanged(self, changes): for security in changes.AddedSecurities: security.SetFeeModel(fk_tools.CustomFeeModel(self)) def CoarseSelectionFunction(self, coarse): if not self.selection_flag and not self.record_adv_expenses_flag: return Universe.Unchanged selected = sorted([x for x in coarse if x.HasFundamentalData and x.Price > 5 and x.Market == 'usa'], key=lambda x: x.DollarVolume, reverse=True) return [x.Symbol for x in selected[:self.course_count]] def FineSelectionFunction(self, fine): fine = [x for x in fine if x.FinancialStatements.IncomeStatement.SellingAndMarketingExpense.ThreeMonths > 0] if self.record_adv_expenses_flag: for stock in fine: symbol = stock.Symbol if symbol not in self.adv_expenses: self.adv_expenses[symbol] = deque(maxlen=2) adv_expenses = stock.FinancialStatements.IncomeStatement.SellingAndMarketingExpense.ThreeMonths self.adv_expenses[symbol].append(adv_expenses) # NOTE: Get rid of old advertisment records so we work with latest values. del_symbols = [] for symbol in self.adv_expenses: if symbol not in [x.Symbol for x in fine]: del_symbols.append(symbol) for symbol in del_symbols: self.adv_expenses.pop(symbol) self.record_adv_expenses_flag = False elif self.selection_flag: d_adv = {} for stock in fine: symbol = stock.Symbol if symbol in self.adv_expenses and len(self.adv_expenses[symbol]) == self.adv_expenses[symbol].maxlen: expanses_values = [x for x in self.adv_expenses[symbol]] d_adv[symbol] = fk_tools.Return(expanses_values) self.selection_flag = False if len(d_adv) == 0: return [] sorted_by_adv = sorted(d_adv.items(), key = lambda x: x[1], reverse = True) decile = int(len(sorted_by_adv)/10) self.long = [x[0] for x in sorted_by_adv[-decile:]] self.short = [x[0] for x in sorted_by_adv[:decile]] return self.long + self.short return [] def Rebalance(self): month = self.Time.month if month == 12: self.Liquidate() self.record_adv_expenses_flag = True elif month == 5: self.selection_flag = True # Trade execution and rebalance count = len(self.long + self.short) if count == 0: return for symbol in self.long: self.SetHoldings(symbol, 1/count) for symbol in self.short: self.SetHoldings(symbol, -1/count) self.long.clear() self.short.clear()
import numpy as np 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 # https://quantpedia.com/strategies/momentum-stock-picking-strategy-using-rsi-indicator/ # # The investment universe consists of stocks from the S&P 500. “N” is set to be 25 days. Firstly, we use 14 – day RSI to assess # if stocks are in a bull range (RSI fluctuates between 40 and 100 over 25 days). Then we use the momentum signal – we check the # highest high value of 14 – day RSI. It has to be greater than 70 over 25 days to select stocks into the trading portfolio. Stocks # are weighted equally, and the portfolio is rebalanced on a daily basis. import fk_tools from collections import deque class Momentum_Stock_Picking_RSI_Indicator(QCAlgorithm): def Initialize(self): self.SetStartDate(2000, 1, 1) self.SetEndDate(2019, 9, 1) self.SetCash(100000) self.UniverseSettings.Resolution = Resolution.Daily self.AddUniverse(self.Universe.Index.QC500) self.rsi = {} self.rsi_history = {} self.period = 25 self.SetWarmUp(self.period) symbol = 'SPY' self.AddEquity(symbol, Resolution.Daily) self.Schedule.On(self.DateRules.EveryDay(symbol), self.TimeRules.AfterMarketOpen(symbol), self.Rebalance) def OnSecuritiesChanged(self, changes): for security in changes.AddedSecurities: security.SetFeeModel(fk_tools.CustomFeeModel(self)) def Rebalance(self): long = [] for symbol in self.Securities.Keys: if not symbol in self.rsi: self.rsi[symbol] = self.RSI(symbol, 14, MovingAverageType.Simple, Resolution.Daily) self.rsi_history[symbol] = deque(maxlen = self.period) if not self.rsi[symbol].IsReady: continue self.rsi_history[symbol].append(self.rsi[symbol].Current.Value) if len(self.rsi_history[symbol]) == self.rsi_history[symbol].maxlen: rsi_values = [x for x in self.rsi_history[symbol]] max_rsi = max(rsi_values) bull_range = sum([x > 40 for x in rsi_values]) == self.period if max_rsi > 70 and bull_range: long.append(symbol) else: if self.Portfolio[symbol].Invested: self.Liquidate(symbol) # Open new trades count = len(long) if count == 0: return for symbol in long: self.SetHoldings(symbol, 1/count)