| Overall Statistics |
|
Total Orders 35 Average Win 1.10% Average Loss -0.01% Compounding Annual Return 205.059% Drawdown 18.200% Expectancy 71.010 Start Equity 4000 End Equity 7996.87 Net Profit 99.922% Sharpe Ratio 3.785 Sortino Ratio 4.461 Probabilistic Sharpe Ratio 92.647% Loss Rate 14% Win Rate 86% Profit-Loss Ratio 83.01 Alpha 0 Beta 0 Annual Standard Deviation 0.324 Annual Variance 0.105 Information Ratio 3.955 Tracking Error 0.324 Treynor Ratio 0 Total Fees $35.00 Estimated Strategy Capacity $6200000.00 Lowest Capacity Asset GLD T3SKPOF94JFP Portfolio Turnover 2.94% Drawdown Recovery 62 |
# region imports
from AlgorithmImports import *
import numpy as np
import math
# endregion
class CfdSimulationBuyingPowerModel(SecurityMarginModel):
def __init__(self, initial_margin):
# Initial Margin (z.B. 0.1313) -> Leverage = 1 / 0.1313 = ca. 7.6
super().__init__(1.0 / initial_margin)
class Kelly9SigTQQQ_UUP_MarginOptimized(QCAlgorithm):
def initialize(self):
self.set_start_date(2025, 6, 1)
self.set_cash(4000)
self._is_live = self.live_mode
self.set_brokerage_model(BrokerageName.INTERACTIVE_BROKERS_BROKERAGE, AccountType.MARGIN)
# --- IBKR ZINS-PARAMETER ---
self.bm_rate = 0.0364 # Benchmark 3.64%
self.borrow_rate = 0.0514 # BM + 1.5% = 5.14% (Sollzins/Financing)
self.credit_rate = 0.0314 # BM - 0.5% = 3.14% (Habenzins)
# 1. MARGIN-PARAMETER
self.qqq_margin_req = 0.1313
self.uup_margin_req = 0.20
if self._is_live:
self.qqq = self.add_cfd("QQQ", Resolution.MINUTE, Market.INTERACTIVE_BROKERS).symbol
self.safe = self.add_cfd("GLD", Resolution.MINUTE, Market.INTERACTIVE_BROKERS).symbol
else:
self.qqq_obj = self.add_equity("QQQ", Resolution.MINUTE)
self.safe_obj = self.add_equity("GLD", Resolution.MINUTE)
self.qqq = self.qqq_obj.symbol
self.safe = self.safe_obj.symbol
self.qqq_obj.set_buying_power_model(CfdSimulationBuyingPowerModel(self.qqq_margin_req))
self.safe_obj.set_buying_power_model(CfdSimulationBuyingPowerModel(self.uup_margin_req))
# Neue akkurate Zins-Logik statt der alten apply_financing
self.schedule.on(self.date_rules.every_day(self.qqq),
self.time_rules.before_market_close(self.qqq, 1),
self.apply_ibkr_interest_model)
# 2. STRATEGIE-LIMITS
self.initial_leverage = 3
self.max_leverage = 6
self.max_bp_utilization = 0.5
self.daily_growth_target = 0.03/4
self.qqq_target_value = 0
self.invested_once = False
self.total_financing_costs = 0
self.total_interest_earned = 0
#self.schedule.on(self.date_rules.every(DayOfWeek.MONDAY, DayOfWeek.FRIDAY),
# self.time_rules.after_market_open(self.qqq, 60),
# self.rebalance)
self.schedule.on(self.date_rules.week_start(self.qqq),
self.time_rules.after_market_open(self.qqq, 60),
self.rebalance)
def apply_ibkr_interest_model(self):
"""Akkurate Simulation der IBKR Kosten für CFDs und Cash"""
if self._is_live: return
# 1. Schritt: NAV und Skalierung (Schritt 2 der IBKR Anleitung)
nav = self.portfolio.total_portfolio_value
nav_factor = min(1.0, max(0, nav / 100000.0))
# 2. Schritt: Exposure bestimmen (Bruttowert der CFDs)
total_cfd_exposure = 0
for kvp in self.portfolio:
if kvp.value.invested:
total_cfd_exposure += abs(kvp.value.holdings_value)
# 3. Schritt: Zinsberechnung
# A) CFD FINANCING (KOSTEN)
# IBKR verlangt BM + 1.5% auf das gesamte Long-Exposure
daily_cfd_cost = (total_cfd_exposure * self.borrow_rate) / 360
# B) GUTHABENZINSEN (ERTRAG)
# Da wir CFDs handeln, bleibt unser Kapital (NAV) als Guthaben stehen.
# IBKR zahlt BM - 0.5% auf Guthaben über 10.000 USD, skaliert mit dem nav_factor.
daily_credit_earned = 0
if nav > 10000:
taxable_cash = nav - 10000
daily_credit_earned = (taxable_cash * self.credit_rate * nav_factor) / 360
# C) MARGIN-SCHULDEN (Nur falls NAV negativ - extrem selten/Margin Call Risiko)
daily_margin_debt_cost = 0
if nav < 0:
daily_margin_debt_cost = (abs(nav) * self.borrow_rate) / 360
# 4. Schritt: Buchung
net_daily_interest = daily_credit_earned - daily_cfd_cost - daily_margin_debt_cost
self.portfolio.cash_book["USD"].add_amount(net_daily_interest)
# Statistik & Zielwertanpassung
self.total_financing_costs += daily_cfd_cost
self.total_interest_earned += daily_credit_earned
if self.invested_once:
# Wir ziehen nur die Kosten vom Zielwert ab, nicht die Zinserträge,
# um konservativ zu bleiben, oder das Netto-Ergebnis:
self.qqq_target_value += net_daily_interest
if self.time.day % 10 == 0:
self.debug(f"NAV: {nav:.0f} | Exposure: {total_cfd_exposure:.0f} | Net Int: {net_daily_interest:.2f}")
def initial_entry(self):
self.qqq_target_value = self.portfolio.total_portfolio_value * self.initial_leverage
self.rebalance()
self.invested_once = True
def on_data(self, data):
if not self.invested_once and not self.is_warming_up:
self.initial_entry()
def rebalance(self):
if not self.invested_once or self.is_warming_up: return
port_val = self.portfolio.total_portfolio_value
if port_val <= 0: return
# --- TEIL 1: QQQ (Leader) ---
target_qqq_weight = self.qqq_target_value / port_val
actual_qqq_weight = min(target_qqq_weight, self.max_leverage)
used_bp_qqq = actual_qqq_weight * self.qqq_margin_req
# --- TEIL 2: UUP (Filler) ---
available_bp_for_safe = max(0, self.max_bp_utilization - used_bp_qqq)
actual_safe_weight = available_bp_for_safe / self.uup_margin_req
# --- TEIL 3: AUSFÜHRUNG ---
self.set_holdings(self.qqq, actual_qqq_weight)
self.set_holdings(self.safe, actual_safe_weight)
# Zielwert-Fortschreibung
self.qqq_target_value *= (1 + self.daily_growth_target)
def on_end_of_algorithm(self):
if not self._is_live:
self.log(f"Financing Paid: {self.total_financing_costs:.2f}")
self.log(f"Interest Earned: {self.total_interest_earned:.2f}")