| Overall Statistics |
|
Total Orders 151 Average Win 3.39% Average Loss -2.20% Compounding Annual Return 5.116% Drawdown 24.800% Expectancy 0.425 Start Equity 100000 End Equity 227959.26 Net Profit 127.959% Sharpe Ratio 0.19 Sortino Ratio 0.137 Probabilistic Sharpe Ratio 0.047% Loss Rate 44% Win Rate 56% Profit-Loss Ratio 1.55 Alpha 0.023 Beta 0.031 Annual Standard Deviation 0.135 Annual Variance 0.018 Information Ratio -0.343 Tracking Error 0.194 Treynor Ratio 0.825 Total Fees $348.27 Estimated Strategy Capacity $950000000.00 Lowest Capacity Asset GC YYP4UM3T47ML Portfolio Turnover 2.92% Drawdown Recovery 3107 |
from AlgorithmImports import *
from datetime import timedelta, datetime
import pandas as pd
import pandas_ta as ta
class MGC_Strategy:
def __init__(self, algorithm):
self.algorithm = algorithm
# ─── NQ Future pro obchodování ───────────────────────────────────── Futures.Metals.MICRO_GOLD
self._future = self.algorithm.add_future(Futures.Metals.GOLD, Resolution.MINUTE, extended_market_hours=True)
self._future.set_filter(90, 500)
self.future_canonical = self._future.symbol
# ─── QQQ pro signály (daily data) ───────────────────────────────────
self.gld = self.algorithm.add_equity("GLD", Resolution.DAILY)
self.gld_symbol = self.gld.symbol
self.tlt = self.algorithm.add_equity("TLT", Resolution.DAILY)
self.tlt_symbol = self.tlt.symbol
# ─── QC Indikátory pro QQQ ──────────────────────────────────────────
# Management proměnné
self._max_days = 10
self.diagnostical_loging = True
self.entry_time = None
self.active_future_contract = None
self.regime = None
self.trading = 0
self._atr_limit_order = 0.10
self._exposure = 1
self.strategy = None
self.trend_following = None
self.breakout = None
self._gld_breakout_qty = None
self.algorithm.debug("MGC strategy inicializována s QQQ signály")
# ─── Schedule po uzavření QQQ trhu ──────────────────────────────────
self.algorithm.schedule.on(
self.algorithm.date_rules.every_day("QQQ"),
self.algorithm.time_rules.after_market_close("QQQ", 0),
self._execute_trading_logic
)
self.algorithm.schedule.on(
self.algorithm.date_rules.every_day(),
self.algorithm.time_rules.before_market_open("QQQ", 60),
self._cancel_orders
)
def on_warm_up_finished(self):
self.algorithm.debug("MNQ MODULE - warm up finished")
self._recover_after_restart()
def _cancel_orders(self):
if not self.algorithm.is_warming_up:
self.algorithm.transactions.cancel_open_orders()
def _recover_after_restart(self):
for holding in self.algorithm.portfolio.values():
symbol = holding.symbol
if holding.invested and "GC" in symbol.value:
self.active_future_contract = symbol
holding_info = self.algorithm.portfolio[symbol]
self.regime = "long" if holding_info.quantity > 0 else "short"
key = f"entry_time_{symbol.value}"
try:
saved_time = self.algorithm.object_store.read(key)
self.entry_time = datetime.fromisoformat(saved_time)
except:
self.entry_time = self.algorithm.time
self.algorithm.debug(f"Recovered MGC position: {symbol} | Regime: {self.regime} | Entry: {self.entry_time} | Qty: {holding_info.quantity}")
return
self.algorithm.debug("Not invested in MGC, nothing to recover")
def on_data(self, slice: Slice):
# ─── Aktualizace mapovaného NQ kontraktu ────────────────────────────
if self.future_canonical in slice.future_chains:
future_chain = slice.future_chains[self.future_canonical]
if future_chain:
sorted_contracts = sorted(future_chain, key=lambda c: c.expiry)
new_contract = sorted_contracts[0].symbol
if new_contract != self.active_future_contract:
if self.active_future_contract and self.algorithm.portfolio[self.active_future_contract].invested:
return
self.active_future_contract = new_contract
if not self.algorithm.is_warming_up:
self.algorithm.debug(f"New NQ contract mapped: {new_contract.value} exp: {sorted_contracts[0].expiry.date()}")
def _execute_trading_logic(self):
if self.algorithm.is_warming_up or self.active_future_contract is None:
return
gld_history = self.algorithm.history(TradeBar, self.gld_symbol, 210, Resolution.DAILY)
tlt_history = self.algorithm.history(TradeBar, self.tlt_symbol, 210, Resolution.DAILY)
invested = self.algorithm.portfolio[self.active_future_contract].invested
atr_10 = self._atr(gld_history, 10)
ibs = self._ibs(gld_history)
momentum_gld = self._momentum(gld_history, 135)
momentum_tlt = self._momentum(tlt_history, 135)
contract = self.active_future_contract
three_day_high_close = self._high_breakout_close(gld_history , 4)
ten_day_high_high = self._high_breakout_high(gld_history, 11)
gld_today_close = gld_history.iloc[-1].close
gld_yesteday_high = gld_history.iloc[-2].high
if momentum_tlt is None or momentum_gld is None:
return
self.algorithm.debug(f"MGC MODULE -------- {self.algorithm.time} ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------")
self.algorithm.debug(f"Contract: {contract} | Invested: {invested} | Momentum_TLT : {momentum_tlt:.2f} | Momentum_GLD: {momentum_gld:.2f}| price: {gld_today_close:.2f} | yesterday high: {gld_yesteday_high:.2f}")
# ─── ENTRY: volatility_reversal ─────────────────────────────────────
gld_trend_following = ((momentum_tlt > 0) and (momentum_gld > 0) and not invested)
gld_breakout = ((tlt_history.iloc[-1].close > tlt_history.iloc[-2].close) and three_day_high_close == 1 and not invested and self.algorithm.time.weekday() != 4)
long_pullback = (ibs < 0.15 and
ten_day_high_high == 1 and
not invested)
if gld_trend_following:
current_price = self.algorithm.securities[self.active_future_contract].price
# Výpočet velikosti pozice
portfolio_value = self.algorithm.portfolio.total_portfolio_value
risk_amount = portfolio_value * (self._exposure / 2)
contract_multiplier = self.algorithm.securities[self.future_canonical].symbol_properties.contract_multiplier
contract_value = current_price * contract_multiplier
if contract_value <= 0:
return
qty_float = risk_amount / contract_value
qty = max(1, int(qty_float))
if qty <= 0:
return
order = self.algorithm.market_order(self.active_future_contract, int(qty))
self.entry_time = self.algorithm.time
self.regime = "long"
self.trend_following = 1
self._gld_trend_following = qty
self.algorithm.debug(f"Entry Long {qty} Current price: @ ~{current_price:.1f}")
return
# ─── EXIT: trendfollowing ──────────────────────────────
if invested:
exit_triggered = ((momentum_tlt < 0) or (momentum_gld < 0))
if exit_triggered:
self.algorithm.liquidate(self.active_future_contract)
self.algorithm.debug(f"EXIT LONG MGC @ GLD: {gld_today_close:.2f} | Včerejší high: {gld_yesteday_high:.2f}")
self.entry_time = None
self.regime = None
def _ibs(self, bar_series):
if len(bar_series) < 1: return None
if bar_series['high'].iloc[-1] == bar_series['low'].iloc[-1]:
return 0.5
ibs = (bar_series['close'].iloc[-1] - bar_series['low'].iloc[-1]) / (bar_series['high'].iloc[-1] - bar_series['low'].iloc[-1])
return ibs
def _momentum(self, bar_series, period):
if len(bar_series) < period: return None
momentum_series = ta.mom(bar_series['close'], length=period)
return momentum_series.iloc[-1]
def _atr(self, bar_series, period):
if len(bar_series) < period: return None
atr_series = ta.atr(bar_series['high'], bar_series['low'], bar_series['close'], length=period)
return atr_series.iloc[-1]
def _high_breakout_close(self, bar_series, period):
if len(bar_series) < period: return None
recent_max = bar_series['close'].iloc[-period:].max()
if bar_series['close'].iloc[-1] == recent_max:
breakout = 1
else:
breakout = 0
return breakout
def _high_breakout_high(self, bar_series, period):
if len(bar_series) < period: return None
recent_max = bar_series['high'].iloc[-period:].max()
if bar_series['high'].iloc[-1] == recent_max:
breakout = 1
else:
breakout = 0
return breakoutfrom AlgorithmImports import *
from datetime import timedelta
from datetime import time as dt_time
from MGC_module import MGC_Strategy
class AT_01(QCAlgorithm):
def initialize(self):
"""Settings of Alghoritm"""
self.set_start_date(2009, 6, 1)
self.set_end_date(2025, 12, 1)
self.set_cash(100000)
self.set_warm_up(timedelta(days=300))
self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.CASH)
self.set_security_initializer(lambda security: security.set_fee_model(InteractiveBrokersFeeModel()))
self.settings.seed_initial_prices = True
benchmark = self.add_equity("SPY").symbol
self.set_benchmark(benchmark)
self.diagnostical_loging = True
self._gold_strategy = 1
if self._gold_strategy == 1: self._mgc_strategy = MGC_Strategy(self)
self.schedule.on(
self.date_rules.every_day(),
self.time_rules.every(timedelta(minutes=60)),
self._hourly_sanity_check)
def on_data(self, slice):
if self._gold_strategy == 1:
self._mgc_strategy.on_data(slice)
def _hourly_sanity_check(self):
if not self.is_warming_up and self.diagnostical_loging:
self.debug(f"AT-01 - Main.py | Hourly Loging Sanity check - Successful | Current time: {self.time}")
def on_order_event(self, order_event):
"""Loging/Debuging of Orders"""
if order_event.status == OrderStatus.SUBMITTED:
self.debug(f"NEW ORDER: {order_event.symbol} - {order_event.fill_quantity} @ ${order_event.fill_price}")
# Save of entry time for MNQ ticker, so after restart it will recover data.
if self.live_mode:
if "MGC" in order_event.symbol.value:
key = f"entry_time_{order_event.symbol.value}"
self.object_store.save(key, str(self.time))
elif order_event.status == OrderStatus.CANCELED:
self.debug(f"Order Canceled: {order_event.symbol}")
elif order_event.status == OrderStatus.INVALID:
self.debug(f"Order Invalid: {order_event.symbol} - {order_event.message}")
def on_brokerage_message(self, message):
"""Will Debug/Log Brokers message"""
self.log(f"BROKERAGE MESSAGE: {message}")
self.debug(f"BROKERAGE MESSAGE: {message}")
def on_warmup_finished(self) -> None:
self.debug(f"on warm up finished in MAIN CALL")
if self._gold_strategy == 1:
self._mgc_strategy.on_warm_up_finished()