| Overall Statistics |
|
Total Orders 8293 Average Win 0.37% Average Loss -0.38% Compounding Annual Return 9.122% Drawdown 31.000% Expectancy 0.093 Start Equity 30000 End Equity 112112.21 Net Profit 273.707% Sharpe Ratio 0.481 Sortino Ratio 0.391 Probabilistic Sharpe Ratio 2.561% Loss Rate 45% Win Rate 55% Profit-Loss Ratio 0.98 Alpha 0 Beta 0 Annual Standard Deviation 0.114 Annual Variance 0.013 Information Ratio 0.605 Tracking Error 0.114 Treynor Ratio 0 Total Fees $20867.87 Estimated Strategy Capacity $14000000.00 Lowest Capacity Asset CDRD R735QTJ8XC9X Portfolio Turnover 28.66% |
# region imports
from AlgorithmImports import *
from alpha_1637 import Strategy001AlphaModel
from porfolio_1737 import Stragety001PortfolioConstructionModel
# endregion
class MeanReversionAlgorithm(QCAlgorithm):
def initialize(self):
self.set_start_date(2009, 1, 10)
self.set_end_date(2024, 5, 12)
self.set_cash(30000) # Set Strategy Cash
self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)
self.add_universe_selection(ETFConstituentsUniverseSelectionModel("QQQ"))
self.universe_settings.resolution = Resolution.DAILY
self.add_alpha(Strategy001AlphaModel())
self.set_portfolio_construction(Stragety001PortfolioConstructionModel())
self.set_execution(ImmediateExecutionModel())
#region imports
from AlgorithmImports import *
#endregion
class Strategy001AlphaModel(AlphaModel):
def __init__(self):
self.entry_rolling_window_dict = {}
self.exit_rolling_window_dict = {}
def update(self, algorithm, data):
entry_insights_list = self.entry_insights_update(algorithm, data)
exit_insights_list = self.exit_insights_update(algorithm, data)
insight_group = Insight.group(entry_insights_list + exit_insights_list)
return insight_group
def exit_insights_update(self, algorithm, data):
exit_insights_list = []
for symbol, trade_bar in data.items():
self.exit_rolling_window_dict[symbol].add(trade_bar)
if (self.exit_rolling_window_dict[symbol].count == 2) and algorithm.portfolio[symbol].invested:
if (self.exit_rolling_window_dict[symbol][0] is None) or (self.exit_rolling_window_dict[symbol][1] is None):
algorithm.log(f"system error:{symbol.value}:self.exit_rolling_window_dict[symbol][0] or [1] is None: trade_bar is None? {trade_bar is None}")
return []
if self.exit_rolling_window_dict[symbol][0].close > self.exit_rolling_window_dict[symbol][1].close:
exit_insights_list.append(
Insight(
symbol=symbol,
period=timedelta(days=1),
type=InsightType.PRICE,
direction=InsightDirection.FLAT,
magnitude= self.exit_rolling_window_dict[symbol][0].volume,
confidence=self.exit_rolling_window_dict[symbol][0].volume))
# algorithm.log(f"exit insight:{symbol.value}")
return exit_insights_list
def entry_insights_update(self, algorithm, data):
entry_insights_list = []
if data.time.weekday() in [4, 5, 1]:
for symbol, trade_bar in data.items():
self.entry_rolling_window_dict[symbol].add(trade_bar)
if (data.time.weekday() == 1) and (self.entry_rolling_window_dict[symbol].count == 3):
if (self.entry_rolling_window_dict[symbol][0] is None) or (self.entry_rolling_window_dict[symbol][1] is None) or (self.entry_rolling_window_dict[symbol][2] is None):
algorithm.log(f"system error:{symbol.value}:self.entry_rolling_window_dict[symbol][0] or [1] or [2] is None: trade_bar is None? {trade_bar is None}")
return []
flag = True
flag = flag and (self.entry_rolling_window_dict[symbol][0].close < self.entry_rolling_window_dict[symbol][1].close)
flag = flag and (self.entry_rolling_window_dict[symbol][1].close < self.entry_rolling_window_dict[symbol][2].close)
if flag:
entry_insights_list.append(
Insight(
symbol=symbol,
period=timedelta(days=1),
type=InsightType.PRICE,
direction=InsightDirection.UP,
magnitude=self.entry_rolling_window_dict[symbol][0].volume,
confidence=self.entry_rolling_window_dict[symbol][0].volume))
# algorithm.log(f"entry insight:{symbol.value}")
return entry_insights_list
def on_securities_changed(self, algorithm, changes):
for security in changes.added_securities:
self.entry_rolling_window_dict.setdefault(security.symbol, RollingWindow[TradeBar](3))
self.exit_rolling_window_dict.setdefault(security.symbol, RollingWindow[TradeBar](2))
#region imports
from AlgorithmImports import *
#endregion
# Portfolio construction scaffolding class; basic method args.
class Stragety001PortfolioConstructionModel(PortfolioConstructionModel):
# Create list of PortfolioTarget objects from Insights
def create_targets(self, algorithm: QCAlgorithm, insights: List[Insight]) -> List[PortfolioTarget]:
return super().create_targets(algorithm, insights)
# Determines the target percent for each insight
def determine_target_percent(self, activeInsights: List[Insight]) -> Dict[Insight, float]:
targets = {}
securities_count = 0
for security_holding in self.algorithm.portfolio.values():
if security_holding.invested:
securities_count += 1
self.algorithm.log(f"securities_count:{securities_count}")
sorted_acive_insights = sorted(activeInsights, key=lambda x: x.confidence, reverse=True)
# exit
for insight in sorted_acive_insights:
if insight.direction == InsightDirection.FLAT:
targets[insight] = 0.0
securities_count -= 1
if securities_count > 5 or securities_count < 0:
self.algorithm.log(f"securities_count:{securities_count}:exit")
continue
assert (securities_count >= 0) and (securities_count <= 5)
# # entry
for insight in sorted_acive_insights:
if (insight.direction == InsightDirection.UP) and (securities_count < 5):
targets[insight] = 0.2
securities_count += 1
if securities_count > 5 or securities_count < 0:
self.algorithm.log(f"securities_count:{securities_count}:entry")
continue
assert (securities_count >= 0) and (securities_count <= 5)
log_string = ""
for t in targets:
log_string += t.to_string() + "; "
self.algorithm.log(log_string)
return targets