Overall Statistics |
Total Orders 688 Average Win 1.16% Average Loss -1.13% Compounding Annual Return 5.956% Drawdown 18.800% Expectancy 0.255 Start Equity 100000 End Equity 270241.37 Net Profit 170.241% Sharpe Ratio 0.338 Sortino Ratio 0.408 Probabilistic Sharpe Ratio 1.004% Loss Rate 38% Win Rate 62% Profit-Loss Ratio 1.03 Alpha 0 Beta 0 Annual Standard Deviation 0.079 Annual Variance 0.006 Information Ratio 0.555 Tracking Error 0.079 Treynor Ratio 0 Total Fees $4136.75 Estimated Strategy Capacity $8400000.00 Lowest Capacity Asset UUP TQBX2PUC67OL Portfolio Turnover 4.45% |
# https://quantpedia.com/strategies/seasonality-patterns-in-the-crisis-hedge-portfolios/ # # The investment universe for this strategy consists of six ETFs that are considered to be crisis hedges. These include Invesco CurrencyShares Swiss Franc Trust (FXF), # Invesco CurrencyShares Japanese Yen Trust (FXY), Invesco DB US Dollar Index Bullish Fund (UUP), SPDR Gold Trust (GLD), iShares 7-10 Year Treasury Bond ETF (IEF), # and iShares 20+ Year Treasury Bond ETF (TLT). (As highlighted in the research paper, these instruments are selected based on their historical performance as effective # hedges during market downturns. The selection is rooted in the Black Swan Hedging Model and the Antifragile Asset Allocation strategy, emphasizing resilience in # volatile markets.) (Data can be obtained from Yahoo Finance.) # Variant Selection: This approach is based on the research paper’s findings that front-running seasonal signals outperform static strategies. The strategy utilizes # a front-running approach to seasonality, focusing on the performance of ETFs in the previous T-11 month as a predictor for the upcoming month. # Advanced Instructions: The methodology involves calculating the returns for each ETF and ranking them based on their T-11 performance. Perform an inter-asset comparison # within the portfolio, identifying the top and bottom performers within groups. Trading Rules: The buy rule is to go long on the top 2 ETFs with the highest returns # from the considered front-running period. Rebalancing & Weighting: The strategy involves monthly rebalancing to adjust positions based on the updated rankings of ETF # performance. The portfolio holds long positions in the top 2 ETFs, with equal capital allocated to each. # region imports from AlgorithmImports import * import pandas as pd from dateutil.relativedelta import relativedelta from pandas.core.frame import DataFrame from typing import List # endregion class SeasonalityPatternsintheCrisisHedgePortfolios(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2008, 1, 1) self.set_cash(100_000) self._period: int = 12 self._offset_months: int = 10 self._top_count: int = 2 self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN) tickers: List[str] = ['FXF', 'FXY', 'UUP', 'GLD', 'IEF', 'TLT'] self._traded_assets: List[Symbol] = [ self.add_equity(ticker, Resolution.DAILY).symbol for ticker in tickers ] self._selection_flag: bool = False self.settings.minimum_order_margin_portfolio_percentage = 0. self.schedule.on( self.date_rules.month_end(self._traded_assets[0]), self.time_rules.before_market_close(self._traded_assets[0]), self._selection ) def on_data(self, slice: Slice) -> None: # Monthly rebalance. if not self._selection_flag: return self._selection_flag = False long: List[str] = [] short: List[str] = [] history: DataFrame = self.history( self._traded_assets, start=self.time - relativedelta(months=self._period), end=self.time ) prices: DataFrame = history.close.unstack(level=0) monthly_returns: DataFrame = prices.groupby(pd.Grouper(freq='M')).last().pct_change().dropna() if len(monthly_returns) >= self._period - 1 and len(prices.columns) == len(self._traded_assets): # Historical performance. observed_performance: DataFrame = monthly_returns[monthly_returns.index.month == (self.time - pd.DateOffset(months=self._offset_months)).month].iloc[0] top_sorted_performance: DataFrame = observed_performance.sort_values(ascending=False).iloc[:self._top_count] # Order execution. targets: List[PortfolioTarget] = [] for symbol in top_sorted_performance.index: if slice.contains_key(str(symbol)) and slice[str(symbol)]: targets.append(PortfolioTarget(str(symbol), 1 / self._top_count)) self.set_holdings(targets, True) def _selection(self) -> None: self._selection_flag = True