Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
229 changes: 229 additions & 0 deletions notebooks/optim_supertrend_vectorbt.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,229 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": null,
"id": "a7e5f72c",
"metadata": {},
"outputs": [],
"source": [
"# %%\n",
"# filename: optim_supertrend_vectorbt.ipynb\n",
"\n",
"# --- 1. Imports and Setup ---\n",
"import os\n",
"import pandas as pd\n",
"import numpy as np\n",
"import pandas_ta as ta\n",
"import vectorbt as vbt\n",
"\n",
"print(\"Libraries imported.\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "d38122d9",
"metadata": {},
"outputs": [],
"source": [
"# %%\n",
"# --- 2. Configuration ---\n",
"# This is the control panel for the optimization.\n",
"# Change the parameters here to analyze different datasets or strategy settings.\n",
"\n",
"# --- Data Input ---\n",
"# Assumes the file is in the project's 'data/parquet/' directory\n",
"DATA_FILENAME = \"EURUSD_M15_2024-09-14_to_2025-09-14.parquet\"\n",
"# The frequency of the data is crucial for correct performance calculation.\n",
"# Examples: '15min', '1H', '4H', 'D'\n",
"FREQ = '15min'\n",
"\n",
"# --- Optimization Parameters for Supertrend ---\n",
"# We define the ranges for the grid search here.\n",
"supert_period_range = np.arange(7, 22, 2) # Test periods from 7 to 21\n",
"supert_multiplier_range = np.arange(2.0, 5.1, 0.5) # Test multipliers from 2.0 to 5.0\n",
"\n",
"# --- Backtest Settings ---\n",
"INIT_CASH = 10000\n",
"FEES = 0.0001 # 0.01% fee per trade\n",
"SLIPPAGE = 0.0001 # 0.01% slippage per trade\n",
"\n",
"# --- Analysis Settings ---\n",
"# The metric to sort the results by\n",
"OPTIMIZE_FOR = 'Sharpe Ratio'\n",
"# How many top results to display\n",
"N_TOP_RESULTS = 10\n",
"\n",
"# --- Path Construction ---\n",
"project_root = os.path.abspath(os.path.join(os.getcwd(), \"..\"))\n",
"file_path = os.path.join(project_root, \"data\", \"parquet\", DATA_FILENAME)\n",
"\n",
"print(\"Configuration loaded.\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c17eeb09",
"metadata": {},
"outputs": [],
"source": [
"# %%\n",
"# --- 3. Data Loading and Indicator Calculation ---\n",
"print(f\"Loading data from: {file_path}\")\n",
"try:\n",
" price_data = pd.read_parquet(file_path)\n",
" price_data.set_index('Time', inplace=True)\n",
" print(f\"Data loaded successfully: {len(price_data)} bars.\")\n",
"except Exception as e:\n",
" print(f\"Error loading data: {e}\")\n",
" exit()\n",
"\n",
"# --- CORRECTED APPROACH: Create a custom wrapper for pandas-ta ---\n",
"# This wrapper function will call pandas-ta and return only the main supertrend line.\n",
"# This solves the problem of dynamically named output columns.\n",
"def pta_supertrend_wrapper(high, low, close, length, multiplier):\n",
" # Calculate the supertrend using pandas-ta\n",
" st = ta.supertrend(high=high, low=low, close=close, length=length, multiplier=multiplier)\n",
"\n",
" # pandas-ta returns a DataFrame. The first column is the main supertrend line.\n",
" # We return only this column (as a Series).\n",
" return st.iloc[:, 0]\n",
"\n",
"\n",
"# Now, create an indicator factory from our OWN wrapper function\n",
"Supertrend = vbt.IndicatorFactory(\n",
" input_names=['high', 'low', 'close'],\n",
" param_names=['length', 'multiplier'],\n",
" output_names=['supertrend'] # We can name the output whatever we want here\n",
").from_apply_func(\n",
" pta_supertrend_wrapper,\n",
" # Important settings for functions that expect pandas Series:\n",
" keep_pd=True,\n",
" to_2d=False\n",
")\n",
"\n",
"print(\"\\nCalculating Supertrend for all parameter combinations...\")\n",
"supertrend_indicator = Supertrend.run(\n",
" price_data['High'],\n",
" price_data['Low'],\n",
" price_data['Close'],\n",
" length=supert_period_range,\n",
" multiplier=supert_multiplier_range,\n",
" param_product=True\n",
")\n",
"print(\"Indicator calculation complete.\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aafe29ef",
"metadata": {},
"outputs": [],
"source": [
"# %%\n",
"# --- 4. Signal Generation and Backtest Execution ---\n",
"print(\"\\nGenerating signals and running backtest...\")\n",
"\n",
"# Now we can access the output via the name we defined: .supertrend\n",
"entries = price_data['Close'].vbt.crossed_above(supertrend_indicator.supertrend)\n",
"exits = price_data['Close'].vbt.crossed_below(supertrend_indicator.supertrend)\n",
"\n",
"# Run the portfolio simulation\n",
"portfolio = vbt.Portfolio.from_signals(\n",
" price_data['Close'],\n",
" entries,\n",
" exits,\n",
" freq=FREQ,\n",
" init_cash=INIT_CASH,\n",
" fees=FEES,\n",
" slippage=SLIPPAGE\n",
")\n",
"print(\"Backtest complete.\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "74f45bc0",
"metadata": {},
"outputs": [],
"source": [
"# %%\n",
"# --- 5. Numerical Analysis of Results (FINAL, DEFINITIVE VERSION 4) ---\n",
"print(f\"\\n--- Optimization Results ---\")\n",
"\n",
"if portfolio.trades.count().sum() == 0:\n",
" print(\"No trades were executed for any parameter combination.\")\n",
"else:\n",
" # --- Top N Results ---\n",
" print(f\"\\nTop {N_TOP_RESULTS} parameter combinations sorted by '{OPTIMIZE_FOR}':\")\n",
"\n",
" # Build the results DataFrame manually from individual metric Series.\n",
"\n",
" total_return = portfolio.total_return()\n",
" sharpe_ratio = portfolio.sharpe_ratio()\n",
" max_drawdown = portfolio.max_drawdown()\n",
" win_rate = portfolio.trades.win_rate()\n",
"\n",
" # CORRECTED: profit_factor is also an attribute of the `trades` accessor\n",
" profit_factor = portfolio.trades.profit_factor()\n",
"\n",
" total_trades = portfolio.trades.count()\n",
"\n",
" # Combine these Series into a single DataFrame.\n",
" results_df = pd.concat([\n",
" total_return,\n",
" sharpe_ratio,\n",
" max_drawdown,\n",
" win_rate,\n",
" profit_factor,\n",
" total_trades\n",
" ], axis=1, keys=[\n",
" 'Total Return [%]',\n",
" 'Sharpe Ratio',\n",
" 'Max Drawdown [%]',\n",
" 'Win Rate [%]',\n",
" 'Profit Factor',\n",
" 'Total Trades'\n",
" ])\n",
"\n",
" # Sort this new DataFrame by the desired metric.\n",
" top_stats = results_df.sort_values(by=OPTIMIZE_FOR, ascending=False).head(N_TOP_RESULTS)\n",
"\n",
" print(top_stats)\n",
"\n",
" # --- Best Result In-Depth ---\n",
" best_params_idx = top_stats.index[0]\n",
" best_portfolio_run = portfolio[best_params_idx]\n",
" best_stats = best_portfolio_run.stats()\n",
"\n",
" print(f\"\\n--- Detailed Stats for the Best Combination: {best_params_idx} ---\")\n",
" print(best_stats)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": "strategy-optimizer_env",
"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.12.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Loading