| Overall Statistics |
|
Total Orders 160 Average Win 0.16% Average Loss -0.01% Compounding Annual Return 7.199% Drawdown 19.700% Expectancy 11.724 Start Equity 1000000 End Equity 1366852.85 Net Profit 36.685% Sharpe Ratio 0.408 Sortino Ratio 0.45 Probabilistic Sharpe Ratio 10.392% Loss Rate 3% Win Rate 97% Profit-Loss Ratio 12.11 Alpha -0.02 Beta 0.917 Annual Standard Deviation 0.107 Annual Variance 0.011 Information Ratio -0.545 Tracking Error 0.048 Treynor Ratio 0.048 Total Fees $241.59 Estimated Strategy Capacity $7100000.00 Lowest Capacity Asset CRHCY R735QTJ8XC9X Portfolio Turnover 0.07% |
#region imports
from AlgorithmImports import *
#endregion
# https://quantpedia.com/Screener/Details/26
class BooktoMarketAnomaly(QCAlgorithm):
def initialize(self):
self.set_start_date(2014, 1, 1)
self.set_end_date(2018, 7, 1)
self.set_cash(1000000)
self._sorted_by_bm = None
self.set_security_initializer(BrokerageModelSecurityInitializer(
self.brokerage_model, FuncSecuritySeeder(self.get_last_known_prices)))
self.add_universe(self.coarse_selection_function, self.fine_selection_function)
self.add_equity("SPY")
self.schedule.on(self.date_rules.month_start("SPY"), self.time_rules.after_market_open("SPY"), self._rebalance)
# Count the number of months that have passed since the algorithm starts
self._months = -1
self._yearly_rebalance = True
def coarse_selection_function(self, coarse):
if self._yearly_rebalance:
# drop stocks which have no fundamental data or have low price
return [x.symbol for x in coarse if (x.has_fundamental_data)]
else:
return Universe.UNCHANGED
def fine_selection_function(self, fine):
if self._yearly_rebalance:
# Filter stocks with positive PB Ratio
fine = [x for x in fine if (x.valuation_ratios.pb_ratio > 0)]
top_market_cap = sorted(fine, key=lambda x: x.market_cap, reverse=True)[:int(len(fine)*0.2)]
# sorted stocks in the top market-cap list by book-to-market ratio
top_bm = sorted(top_market_cap, key=lambda x: 1 / x.valuation_ratios.pb_ratio, reverse=True)[:int(len(top_market_cap)*0.2)]
self._sorted_by_bm = [i.symbol for i in top_bm]
total_market_cap = np.sum([i.market_cap for i in top_bm])
# calculate the weight with the market cap
self._weights = {}
for i in top_bm:
self._weights[str(i.symbol)] = i.market_cap/total_market_cap
return self._sorted_by_bm
else:
return Universe.UNCHANGED
def _rebalance(self):
# yearly rebalance
self._months += 1
if self._months%12 == 0:
self._yearly_rebalance = True
def on_data(self, data):
if not self._yearly_rebalance:
return
if self._sorted_by_bm:
stocks_invested = [x.key for x in self.portfolio if x.value.invested]
# liquidate stocks not in the trading list
for i in stocks_invested:
if i not in self._sorted_by_bm:
self.liquidate(i)
# goes long on stocks with the highest book-to-market ratio
for i in self._sorted_by_bm:
self.set_holdings(i, self._weights[str(i)])
self._yearly_rebalance = False