Overall Statistics |
Total Orders 49 Average Win 19.17% Average Loss -2.18% Compounding Annual Return 10.936% Drawdown 19.700% Expectancy 5.924 Start Equity 100000 End Equity 1331827.89 Net Profit 1231.828% Sharpe Ratio 0.517 Sortino Ratio 0.536 Probabilistic Sharpe Ratio 3.003% Loss Rate 29% Win Rate 71% Profit-Loss Ratio 8.78 Alpha 0 Beta 0 Annual Standard Deviation 0.114 Annual Variance 0.013 Information Ratio 0.712 Tracking Error 0.114 Treynor Ratio 0 Total Fees $1017.61 Estimated Strategy Capacity $840000000.00 Lowest Capacity Asset SPY R735QTJ8XC9X Portfolio Turnover 0.54% |
# region imports from AlgorithmImports import * from dateutil.relativedelta import relativedelta # endregion class RecessionSignal(QCAlgorithm): def initialize(self) -> None: self.set_start_date(2000, 1, 1) self.set_cash(100_000) self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN) self.settings.minimum_order_margin_portfolio_percentage = 0 self.settings.daily_precise_end_time = False period: int = 10 * 21 self._market: Symbol = self.add_equity("SPY", Resolution.MINUTE).symbol self._sma: SimpleMovingAverage = self.SMA(self._market, period, Resolution.DAILY) # safe asset self._safe_asset: Symbol = self.add_equity("IEF", Resolution.MINUTE).symbol self.set_warm_up(period, Resolution.DAILY) self._indpro: Symbol = self.add_data(YOYData, 'INDPRO_YOY', Resolution.DAILY).symbol self._rrsfs: Symbol = self.add_data(YOYData, 'RRSFS_YOY', Resolution.DAILY).symbol self._rebalance_flag: bool = False self.schedule.on(self.date_rules.month_end(self._market), self.time_rules.before_market_close(self._market, 1), self._selection) def on_data(self, slice: Slice) -> None: if not self._rebalance_flag: return self._rebalance_flag = False if self.is_warming_up: return if not self._sma.is_ready: return if not slice.contains_key(self._market) or not slice.contains_key(self._safe_asset): return traded_asset: Symbol = self._market # check end of custom datasets last_update_date: datetime.date = YOYData.get_last_update_date() if not all(self.securities[symbol].get_last_data() and symbol.value in last_update_date and self.time.date() < last_update_date[symbol.value] \ for symbol in [self._indpro, self._rrsfs]): # signal no recession in case there's an end of custom data pass else: # signal recession if any(self.securities[symbol].price < 0. for symbol in [self._indpro, self._rrsfs]): if slice[self._market].value <= self._sma.current.value: traded_asset = self._safe_asset if not self.portfolio[traded_asset].invested: self.set_holdings(traded_asset, 1., True) def _selection(self) -> None: self._rebalance_flag = True class YOYData(PythonData): _last_update_date: Dict[str, datetime.date] = {} def GetSource(self, config: SubscriptionDataConfig, date: datetime, isLiveMode: bool) -> SubscriptionDataSource: return SubscriptionDataSource(f'data.quantpedia.com/backtesting_data/economic/{config.symbol.value}.csv', SubscriptionTransportMedium.REMOTE_FILE, FileFormat.CSV) @staticmethod def get_last_update_date() -> Dict[Symbol, datetime.date]: return YOYData._last_update_date def Reader(self, config: SubscriptionDataConfig, line: str, date: datetime, is_live_mode: bool) -> BaseData: data: YOYData = YOYData() data.symbol = config.symbol if not line[0].isdigit(): return None split = line.split(';') # Parse the CSV file's columns into the custom data class data.time = datetime.strptime(split[0], "%Y-%m-%d") + relativedelta(months=1) data.value = float(split[1]) if config.symbol.value not in YOYData._last_update_date: YOYData._last_update_date[config.symbol.value] = datetime(1,1,1).date() if data.time.date() > YOYData._last_update_date[config.symbol.value]: YOYData._last_update_date[config.symbol.value] = data.time.date() return data