Overall Statistics |
Total Orders 15179 Average Win 0.47% Average Loss -0.23% Compounding Annual Return 9.897% Drawdown 26.000% Expectancy 0.131 Start Equity 100000 End Equity 999579.22 Net Profit 899.579% Sharpe Ratio 0.537 Sortino Ratio 0.844 Probabilistic Sharpe Ratio 5.119% Loss Rate 63% Win Rate 37% Profit-Loss Ratio 2.02 Alpha 0.052 Beta -0.03 Annual Standard Deviation 0.094 Annual Variance 0.009 Information Ratio 0.045 Tracking Error 0.189 Treynor Ratio -1.659 Total Fees $179142.34 Estimated Strategy Capacity $240000000.00 Lowest Capacity Asset QQQ RIWIV7K5Z9LX Portfolio Turnover 193.92% |
# region imports from AlgorithmImports import * import numpy as np from collections import deque # endregion class FormalRedPelican(QCAlgorithm): def initialize(self): self.set_start_date(2000, 1, 1) self.set_cash(100000) # self.set_brokerage_model(BrokerageName.BITFINEX, AccountType.Margin) # symbols = ['SPY', 'QQQ', 'EFA', 'TLT'] symbols = ['SPY', 'QQQ'] for symbol in symbols: sym = self.add_equity( symbol, Resolution.MINUTE, data_normalization_mode=DataNormalizationMode.TOTAL_RETURN ) sym.margin_model = PatternDayTradingMarginModel() sym._vwap = self.vwap(sym.symbol) sym._roc = self.rocp(sym.symbol, 1, Resolution.DAILY) sym._vol = IndicatorExtensions.of(StandardDeviation(14), sym._roc) sym._deviation = AbsoluteDeviation('deviation', 63) sym._previous_date = None sym._open_price = None sym._previous_close = None sym._last_trade_date = None sym._bars = deque(maxlen=2) self.consolidate(sym.symbol, timedelta(minutes=30), self.consolidate_handler) # spy = self.add_equity("SPY", Resolution.MINUTE, data_normalization_mode=DataNormalizationMode.TOTAL_RETURN) # self._symbol = spy.symbol # spy.margin_model = PatternDayTradingMarginModel() # self._vwap = self.vwap(spy.symbol) # self._roc = self.rocp(self._symbol, 1, Resolution.DAILY) # self._vol = IndicatorExtensions.of(StandardDeviation(14), self._roc) # self._deviation = AbsoluteDeviation('deviation', 63) # self._ema = self.ema(self._symbol, 200, Resolution.DAILY) # self.consolidator = TradeBarConsolidator(timedelta(minutes=30)) # self.consolidator.data_consolidated += (self.consolidate) # self.subscription_manager.add_consolidator(self._symbol, self.consolidator) # self.previous_date = None # self.open_price = None # self.previous_close = None # self.last_trade_date = None # self.bars = deque(maxlen=2) self.schedule.on( self.date_rules.every_day(symbols[0]), # self.time_rules.at(0,0), self.time_rules.before_market_close(symbols[0], 1), self.end_of_day ) def consolidate_handler(self, bar): symbol = bar.symbol security = self.securities[symbol] current_date = bar.end_time.date() security._bars.append(bar) if current_date != security._previous_date: security._previous_date = current_date security._open_price = bar.open security._previous_close = security._bars[-2].close if len(security._bars) == 2 else None security._deviation.update(bar) if not security._vol.is_ready or not security._previous_close or not security._deviation.ready: return upper_band = (max(security._open_price, security._previous_close) * (1 + security._deviation.value)) lower_band = (min(security._open_price, security._previous_close) * (1 - security._deviation.value)) vwap_price = security._vwap.current.value long_stop_price = np.max([vwap_price, upper_band]) short_stop_price = np.min([vwap_price, lower_band]) is_up_trend = bar.close > security._vwap.current.value is_down_trend = bar.close < security._vwap.current.value is_long = self.portfolio[symbol].is_long is_short = self.portfolio[symbol].is_short is_long_stopped_out = is_long and bar.close <= long_stop_price is_short_stopped_out = is_short and bar.close >= short_stop_price is_not_last_trade_date = security._last_trade_date != current_date vol_target = 0.02 spy_vol = security._vol.current.value / 100 leverage = np.min([4, vol_target / spy_vol]) * 1 / len(self.securities.keys()) if is_long_stopped_out or is_short_stopped_out: self.liquidate(symbol) if bar.close > upper_band and not is_long and is_up_trend and is_not_last_trade_date: self.set_holdings(symbol, 1 * leverage) security._last_trade_date = current_date elif bar.close < lower_band and not is_short and is_down_trend and is_not_last_trade_date: self.set_holdings(symbol, -1 * leverage) security._last_trade_date = current_date def end_of_day(self): self.liquidate() class AbsoluteDeviation(PythonIndicator): def __init__(self, name, period): super().__init__() self.name = name self.period = period self.data = {} self.ready = False self.previous_data = None self.open_price = None def update(self, data: BaseData): current_data = data.end_time.date() if current_data != self.previous_data: self.previous_data = current_data self.open_price = data.open current_time = data.end_time.time() if current_time not in self.data: self.data[current_time] = deque(maxlen=self.period) self.data[current_time].append( np.abs(data.close / self.open_price - 1) ) if len(self.data[current_time]) == self.period: self.ready = True self.value = np.mean(self.data[current_time]) return len(self.data[current_time]) == self.period