{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# PREDIX Quickstart Tutorial\n", "\n", "Willkommen zu PREDIX – deiner Plattform für algorithmisches EUR/USD Trading!\n", "\n", "In diesem Notebook lernst du:\n", "1. **Daten laden** – EUR/USD 1-Minute Daten vorbereiten\n", "2. **Faktoren generieren** – Einfache Trading-Faktoren berechnen\n", "3. **Strategie kombinieren** – Mehrere Faktoren zu einer Strategie verbinden\n", "4. **Backtest durchführen** – Historische Performance testen\n", "5. **Ergebnisse visualisieren** – Equity Curve und Metriken\n", "\n", "## Voraussetzungen\n", "\n", "```bash\n", "pip install -e \".[all]\"\n", "```" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 1. Setup & Daten laden\n", "\n", "Zuerst importieren wir die benötigten Bibliotheken und laden die EUR/USD Daten." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import warnings\n", "warnings.filterwarnings('ignore')\n", "\n", "# Plotly für interaktive Charts (optional)\n", "try:\n", " import plotly.graph_objects as go\n", " from plotly.subplots import make_subplots\n", " HAS_PLOTLY = True\n", "except ImportError:\n", " HAS_PLOTLY = False\n", "\n", "print(\"✓ Imports erfolgreich!\")\n", "print(f\" Pandas: {pd.__version__}\")\n", "print(f\" NumPy: {np.__version__}\")\n", "print(f\" Plotly: {'ja' if HAS_PLOTLY else 'nein (pip install plotly)'}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "### Daten-Simulation\n", "\n", "Für dieses Tutorial simulieren wir EUR/USD Daten (in Produktion: Echte Daten aus Qlib)." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Simuliere EUR/USD 1-Minute Daten (1 Jahr)\n", "np.random.seed(42)\n", "n_bars = 525600 # 525600 Minuten pro Jahr\n", "\n", "# Datetime-Index (24/7 Trading)\n", "dates = pd.date_range('2024-01-01', periods=n_bars, freq='min')\n", "\n", "# Simulierte Preise (Geometric Brownian Motion)\n", "dt = 1/525600\n", "mu = 0.00002 # Drift\n", "sigma = 0.0003 # Volatilität\n", "returns = np.random.normal(mu, sigma, n_bars)\n", "prices = 1.0850 * np.exp(np.cumsum(returns)) # Start bei 1.0850\n", "\n", # OHLCV erstellen\n", "df = pd.DataFrame({\n", " 'open': prices + np.random.normal(0, 0.0001, n_bars),\n", " 'high': prices + np.abs(np.random.normal(0, 0.0002, n_bars)),\n", " 'low': prices - np.abs(np.random.normal(0, 0.0002, n_bars)),\n", " 'close': prices,\n", " 'volume': np.random.exponential(100, n_bars).astype(int)\n", "}, index=dates)\n", "\n", "print(f\"✓ Daten generiert: {len(df)} Bars\")\n", "print(f\" Zeitraum: {df.index[0]} bis {df.index[-1]}\")\n", "print(f\"\\nErste 5 Zeilen:\")\n", "df.head()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 2. Trading-Faktoren berechnen\n", "\n", "Jetzt berechnen wir verschiedene Trading-Faktoren:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "def calculate_momentum(close: pd.Series, window: int) -> pd.Series:\n", " \"\"\"Momentum-Faktor: Prozentuale Veränderung über window Bars.\"\"\"\n", " return close.pct_change(window)\n", "\n", "def calculate_rsi(close: pd.Series, period: int = 14) -> pd.Series:\n", " \"\"\"RSI (Relative Strength Index).\"\"\"\n", " delta = close.diff()\n", " gain = delta.where(delta > 0, 0).rolling(period).mean()\n", " loss = (-delta.where(delta < 0, 0)).rolling(period).mean()\n", " rs = gain / (loss + 1e-8)\n", " return 100 - (100 / (1 + rs))\n", "\n", "def calculate_hl_range(high: pd.Series, low: pd.Series, close: pd.Series) -> pd.Series:\n", " \"\"\"High-Low Range als Volatilitäts-Proxy.\"\"\"\n", " return (high - low) / close\n", "\n", "def calculate_session_flag(index: pd.DatetimeIndex, session: str) -> pd.Series:\n", " \"\"\"Session-Filter (London, NY, Asian).\"\"\"\n", " hour = index.hour\n", " if session == 'london':\n", " return ((hour >= 8) & (hour < 16)).astype(float)\n", " elif session == 'ny':\n", " return ((hour >= 13) & (hour < 21)).astype(float)\n", " elif session == 'overlap':\n", " return ((hour >= 13) & (hour < 16)).astype(float)\n", " return pd.Series(1, index=index)\n", "\n", "# Faktoren berechnen\n", "factors = pd.DataFrame(index=df.index)\n", "factors['momentum_16'] = calculate_momentum(df['close'], 16)\n", "factors['momentum_96'] = calculate_momentum(df['close'], 96)\n", "factors['rsi_14'] = calculate_rsi(df['close'], 14)\n", "factors['hl_range'] = calculate_hl_range(df['high'], df['low'], df['close'])\n", "factors['is_london'] = calculate_session_flag(df.index, 'london')\n", "factors['is_ny'] = calculate_session_flag(df.index, 'ny')\n", "\n", "# NaN entfernen\n", "factors = factors.dropna()\n", "\n", "print(f\"✓ {len(factors.columns)} Faktoren berechnet:\")\n", "for col in factors.columns:\n", " print(f\" - {col:15s} | Mean: {factors[col].mean():+.4f} | Std: {factors[col].std():.4f}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 3. Strategie kombinieren\n", "\n", "Wir kombinieren die Faktoren zu einer IC-weighted Strategie:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Simulierte IC-Werte (Information Coefficient)\n", "ic_values = {\n", " 'momentum_16': 0.074, # Positiv: Trend-following\n", " 'momentum_96': 0.051, # Positiv: Langfristiger Trend\n", " 'rsi_14': -0.045, # Negativ: Mean-reversion\n", " 'hl_range': -0.032 # Negativ: Volatilitäts-Fade\n", "}\n", "\n", "# Z-Score Normalisierung\n", "z_scores = (factors[list(ic_values.keys())] - factors[list(ic_values.keys())].rolling(20).mean()) / (\n", " factors[list(ic_values.keys())].rolling(20).std() + 1e-8\n", ")\n", "\n", "# IC-Weights (normalisieren)\n", "total_abs_ic = sum(abs(ic) for ic in ic_values.values())\n", "weights = {k: v / total_abs_ic for k, v in ic_values.items()}\n", "\n", "# Composite Signal\n", "composite = pd.Series(0.0, index=z_scores.index)\n", "for factor_name, weight in weights.items():\n", " composite += weight * z_scores[factor_name]\n", "\n", "# Signale generieren (Thresholds)\n", "signal = pd.Series(0, index=composite.index)\n", "signal[composite > 0.5] = 1 # LONG\n", "signal[composite < -0.5] = -1 # SHORT\n", "\n", "print(f\"✓ Strategie generiert\")\n", "print(f\"\\nSignal-Verteilung:\")\n", "print(f\" LONG: {(signal == 1).sum():6d} ({(signal == 1).mean()*100:.1f}%)\")\n", "print(f\" SHORT: {(signal == -1).sum():6d} ({(signal == -1).mean()*100:.1f}%)\")\n", "print(f\" NEUTRAL: {(signal == 0).sum():6d} ({(signal == 0).mean()*100:.1f}%)\")\n", "\n", "# IC-Weights anzeigen\n", "print(f\"\\nIC-Weights:\")\n", "for factor_name, weight in weights.items():\n", " print(f\" {factor_name:15s}: {weight:+.4f} (IC: {ic_values[factor_name]:+.4f})\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 4. Backtest\n", "\n", "Simulieren wir einen einfachen Backtest mit Spread-Kosten:" ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "# Backtest-Parameter\n", "spread_cost = 0.00015 # 1.5 bps\n", "initial_capital = 100000\n", "position_size = 0.1 # 10% des Kapitals pro Trade\n", "\n", "# Nur London/NY Session handeln\n", "active_mask = (factors['is_london'] == 1) | (factors['is_ny'] == 1)\n", "\n", "# Returns berechnen\n", "close = df.loc[signal.index, 'close']\n", "returns = close.pct_change()\n", "\n", "# Strategie-Returns\n", "strategy_returns = signal.shift(1) * returns # Signal vom Vortag\n", "strategy_returns = strategy_returns[active_mask]\n", "\n", "# Spread-Kosten abziehen\n", "trade_costs = (signal.shift(1) != signal).astype(float) * spread_cost\n", "strategy_returns = strategy_returns - trade_costs\n", "\n", "# Kumulierte Returns\n", "equity = initial_capital * (1 + strategy_returns).cumprod()\n", "benchmark_equity = initial_capital * (1 + returns[active_mask]).cumprod()\n", "\n", "# Metriken berechnen\n", "total_return = (equity.iloc[-1] / initial_capital - 1) * 100\n", "years = len(strategy_returns) / 525600\n", "arr = ((equity.iloc[-1] / initial_capital) ** (1/max(years, 0.001)) - 1) * 100\n", "sharpe = strategy_returns.mean() / (strategy_returns.std() + 1e-8) * np.sqrt(525600)\n", "\n", "# Max Drawdown\n", "rolling_max = equity.cummax()\n", "drawdown = (equity - rolling_max) / rolling_max\n", "max_dd = drawdown.min() * 100\n", "\n", "print(f\"=\" * 50)\n", "print(f\"BACKTEST ERGEBNISSE\")\n", "print(f\"=\" * 50)\n", "print(f\" Initial Capital: ${initial_capital:,.0f}\")\n", "print(f\" Final Capital: ${equity.iloc[-1]:,.0f}\")\n", "print(f\" Total Return: {total_return:+.2f}%\")\n", "print(f\" ARR: {arr:+.2f}%\")\n", "print(f\" Sharpe Ratio: {sharpe:.2f}\")\n", "print(f\" Max Drawdown: {max_dd:.2f}%\")\n", "print(f\" Trades: {(signal.shift(1) != signal).sum()}\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 5. Visualisierung\n", "\n", "Jetzt visualisieren wir die Equity Curve und die Drawdowns." ] }, { "cell_type": "code", "execution_count": null, "metadata": {}, "outputs": [], "source": [ "if HAS_PLOTLY:\n", " # Subplots: Equity + Drawdown\n", " fig = make_subplots(\n", " rows=2, cols=1,\n", " shared_xaxes=True,\n", " vertical_spacing=0.05,\n", " row_heights=[0.7, 0.3],\n", " subplot_titles=('Equity Curve', 'Drawdown')\n", " )\n", " \n", " # Equity Curve\n", " fig.add_trace(\n", " go.Scatter(x=equity.index, y=equity.values, name='Strategy', line=dict(color='#2E86AB', width=2)),\n", " row=1, col=1\n", " )\n", " fig.add_trace(\n", " go.Scatter(x=benchmark_equity.index, y=benchmark_equity.values, name='Benchmark', line=dict(color='#A23B72', width=1, dash='dot')),\n", " row=1, col=1\n", " )\n", " \n", " # Drawdown\n", " fig.add_trace(\n", " go.Scatter(x=drawdown.index, y=drawdown.values*100, name='Drawdown',\n", " fill='tozeroy', line=dict(color='#F18F01', width=1)),\n", " row=2, col=1\n", " )\n", " \n", " fig.update_layout(\n", " title='PREDIX Backtest - EUR/USD 1-Minute',\n", " template='plotly_dark',\n", " height=700,\n", " showlegend=True\n", " )\n", " \n", " fig.show()\n", "else:\n", " # Matplotlib Fallback\n", " fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 8), sharex=True, gridspec_kw={'height_ratios': [3, 1]})\n", " \n", " ax1.plot(equity.index, equity.values, label='Strategy', color='#2E86AB', linewidth=2)\n", " ax1.plot(benchmark_equity.index, benchmark_equity.values, label='Benchmark', color='#A23B72', linewidth=1, linestyle='--')\n", " ax1.set_title('Equity Curve')\n", " ax1.legend()\n", " ax1.grid(True, alpha=0.3)\n", " \n", " ax2.fill_between(drawdown.index, drawdown.values*100, 0, color='#F18F01', alpha=0.5)\n", " ax2.set_title('Drawdown')\n", " ax2.grid(True, alpha=0.3)\n", " \n", " plt.tight_layout()\n", " plt.savefig('equity_curve.png', dpi=150)\n", " plt.show()\n", " print(\"✓ Chart gespeichert: equity_curve.png\")" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## 6. Nächste Schritte\n", "\n", "🎉 Glückwunsch! Du hast deinen ersten PREDIX-Backtest durchgeführt.\n", "\n", "### Weiterführende Beispiele:\n", "\n", "| Beispiel | Beschreibung |\n", "|----------|-------------|\n", "| `01_factor_discovery.py` | Automatische Faktor-Generierung mit LLM |\n", "| `02_factor_evolution.py` | Faktor-Optimierung mit Session/Regime Filters |\n", "| `05_model_training.py` | ML-Modelle (LSTM/XGBoost) trainieren |\n", "| `06_rl_trading_agent.py` | Reinforcement Learning Agent |\n", "\n", "### CLI Commands:\n", "\n", "```bash\n", "# Alle Commands anzeigen\n", "rdagent --help\n", "\n", "# Faktor-Generierung starten\n", "rdagent quant --loop-n 10\n", "\n", "# Faktoren evaluieren\n", "rdagent evaluate\n", "\n", "# Top-Faktoren anzeigen\n", "rdagent top --n 10\n", "```\n", "\n", "### Ressourcen:\n", "\n", "- 📚 [Dokumentation](../docs/)\n", "- 💬 [GitHub Discussions](https://github.com/nico/NexQuant/discussions)\n", "- 🐛 [Issues melden](https://github.com/nico/NexQuant/issues)" ] } ], "metadata": { "kernelspec": { "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.10.0" } }, "nbformat": 4, "nbformat_minor": 4 }