Files
NexQuant/examples/notebooks/quickstart.ipynb
T

412 lines
14 KiB
Plaintext
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
{
"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
}