| Overall Statistics |
|
Total Orders 1004 Average Win 0.73% Average Loss -0.73% Compounding Annual Return 7.859% Drawdown 26.700% Expectancy 0.113 Start Equity 1000000 End Equity 1459074.97 Net Profit 45.907% Sharpe Ratio 0.171 Sortino Ratio 0.198 Probabilistic Sharpe Ratio 5.116% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 0.99 Alpha -0.017 Beta 0.899 Annual Standard Deviation 0.167 Annual Variance 0.028 Information Ratio -0.197 Tracking Error 0.11 Treynor Ratio 0.032 Total Fees $6903.53 Estimated Strategy Capacity $31000000.00 Lowest Capacity Asset TMK R735QTJ8XC9X Portfolio Turnover 4.86% Drawdown Recovery 539 |
# Region imports.
from AlgorithmImports import *
# End region.
class LevermannFactorsAlphaModel(AlphaModel):
def __init__(self, algorithm, date_rule, benchmark):
self._benchmark = benchmark
self._universe = []
self._assets = 10
# Add a Scheduled Event to emit monthly insights matching the universe.
self._insights = []
algorithm.schedule.on(date_rule, algorithm.time_rules.at(8, 0), self._create_insights)
def on_securities_changed(self, algorithm, changes):
# Create indicators for added securities.
for security in changes.added_securities:
security.roc_1m = algorithm.roc(security, 21)
if security == self._benchmark:
continue
security.roc_6m = algorithm.roc(security, 6 * 21)
security.roc_1y = algorithm.roc(security, 252)
security.session.size = 252
for bar in algorithm.history[TradeBar](security, 253):
security.session.update(bar)
self._universe.append(security)
# Remove securities that leave the universe.
for security in changes.removed_securities:
self._universe.remove(security)
def _create_insights(self):
# Calculate the score of each security in the universe.
# Scoring according to https://letyourmoneygrow.com/2018/03/09/susan-levermann-approach-an-investment-strategy-that-works/.
score_by_security = {}
for security in self._universe:
if not (security.roc_1y.is_ready and self._benchmark.roc_1m.is_ready):
continue
f = security.fundamentals
debt_to_equity_ratio = f.operation_ratios.total_debt_equity_ratio.one_month
inverse_debt_to_equity_ratio = 1 / debt_to_equity_ratio if debt_to_equity_ratio else None
quarterly_report_price_change = None
filing_date = f.financial_statements.file_date.value
if filing_date is not None and security.price is not None and security.price == security.price:
prior_close_list = [bar.close for bar in security.session if bar.end_time <= filing_date]
if prior_close_list:
quarterly_report_price_change = (security.price / prior_close_list[0]) - 1
# Score the Levermann threshold factors.
factors = (
# Factor 1: One year RoE >20%: +1 ; <10%: -1.
Factor(f.operation_ratios.roe.one_year, 0.2, 0.1, True),
# Factor 2: EBIT One Year >12%: +1 ; <6%: -1.
Factor(f.operation_ratios.ebit_margin.one_year, 0.12, 0.06, True),
# Factor 3: Equity Ratio one year >25%: +1 ; <15%: -1.
Factor(inverse_debt_to_equity_ratio, 0.25, 0.15, True),
# Factor 4: P/E one Year <12: +1 ; >16: -1.
Factor(f.valuation_ratios.pe_ratio, 12, 16, False),
# Factor 5: P/E five years <13: +1 ; >17: -1.
Factor(f.valuation_ratios.pe_ratio_5_year_average, 13, 17, False),
# Factor 7: Real price reaction in % on quarterly EPS report >1%: +1 ; <-1%: -1.
Factor(quarterly_report_price_change, 0.05, -0.05, True),
# Factor 8: Current FQ Est EPS% change >5%: +1 ; <-5%: -1.
Factor(f.valuation_ratios.forward_earning_yield, 0.05, -0.05, True),
# Factor 9: 6 months price change >5%: +1 ; <-5%: -1.
Factor(security.roc_6m.current.value, 0.05, -0.05, True),
# Factor 10: 12 months price change >5%: +1 ; <-5%: -1.
Factor(security.roc_1y.current.value, 0.05, -0.05, True),
# Factor 11: EPS growth: Change of current to next FY Est EPS >5%: +1; <-5%: -1.
Factor(f.valuation_ratios.second_year_estimated_eps_growth, 0.05, -0.05, True),
)
security_score = sum(factor.calculate() for factor in factors)
# Factor 12: Momentum: if 6 months price change > 5% and 12 month price change < -5%: 1.
# If 6 months price change < -5% and 12 month price change > 5%: -1.
if security.roc_6m.current.value > 0.05 and security.roc_1y.current.value < -0.05:
security_score += 1
elif security.roc_6m.current.value < -0.05 and security.roc_1y.current.value > 0.05:
security_score -= 1
# Factor 13: Reversal: if better than benchmark: 1 ; if worse than benchmark -1.
security_score += 1 if security.roc_1m < self._benchmark.roc_1m else -1
score_by_security[security] = security_score
# Create insights for the assets with the greatest scores: Score >= 4.
self._insights = [
Insight.price(security, Expiry.END_OF_MONTH, InsightDirection.UP)
for security in sorted(score_by_security, key=lambda security: score_by_security[security])[-self._assets:]
if score_by_security[security] >= 4
]
def update(self, algorithm, data):
insights = self._insights.copy()
self._insights.clear()
return insights
class Factor:
def __init__(self, factor_value, bullish_threshold, bearish_threshold, higher_is_better=True):
self._factor_value = factor_value
self._bullish_threshold = bullish_threshold
self._bearish_threshold = bearish_threshold
self._higher_is_better = higher_is_better
def calculate(self):
if self._factor_value is None:
return 0
if self._higher_is_better:
if self._factor_value > self._bullish_threshold:
return 1
if self._factor_value < self._bearish_threshold:
return -1
return 0
if self._factor_value < self._bullish_threshold:
return 1
if self._factor_value > self._bearish_threshold:
return -1
return 0# region imports
from AlgorithmImports import *
from alpha import LevermannFactorsAlphaModel
# endregion
class TheRelaxedWayToWealthAlgorithm(QCAlgorithmFramework):
def initialize(self):
self.set_start_date(self.end_date - timedelta(5*365))
self.set_cash(1_000_000)
# Define some settings.
self.settings.automatic_indicator_warm_up = True
self.settings.rebalance_portfolio_on_security_changes = False
# Add the universe of SPY constituents.
benchmark = self.add_equity('SPY', Resolution.DAILY)
date_rule = self.date_rules.month_start(benchmark)
self.universe_settings.resolution = Resolution.DAILY
self.universe_settings.schedule.on(date_rule)
self.set_universe_selection(ETFConstituentsUniverseSelectionModel(benchmark))
# Add the Alpha, PCM, and Execution models.
self.set_alpha(LevermannFactorsAlphaModel(self, date_rule, benchmark))
self.set_portfolio_construction(EqualWeightingPortfolioConstructionModel(lambda _: None))
# Add a warm-up period so the algorithm trades right away.
self.set_warm_up(timedelta(45))