{ "cells": [ { "cell_type": "markdown", "metadata": { "vscode": { "languageId": "raw" } }, "source": [ "# Pairs Trading Backtest Notebook\n", "\n", "This comprehensive notebook supports both StaticFit and SlidingFit.\n", "It automatically adapts its analysis and visualization based on the strategy specified in the configuration file.\n", "\n", "## Key Features:\n", "\n", "1. **Configuration-Driven**: Loads strategy and parameters from HJSON configuration files\n", "2. **Dual Model Support**: Works with both StaticFit and SlidingFit\n", "3. **Adaptive Visualization**: Different visualizations based on selected strategy\n", "4. **Comprehensive Analysis**: Deep analysis of trading pairs and dis-equilibrium\n", "5. **Interactive Configuration**: Easy parameter adjustment and re-running\n", "\n", "## Usage:\n", "\n", "1. **Configure Parameters**: Set CONFIG_FILE, SYMBOL_A, SYMBOL_B, and TRADING_DATE\n", "2. **Run Analysis**: Execute cells step by step\n", "3. **View Results**: Comprehensive visualizations and trading signals\n", "4. **Experiment**: Modify parameters and re-run for different scenarios\n" ] }, { "cell_type": "markdown", "metadata": { "vscode": { "languageId": "raw" } }, "source": [ "\n", "# Settings" ] }, { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [], "source": [ "# Trading Parameters Configuration\n", "# Specify your configuration file, trading symbols and date here\n", "\n", "# Configuration file selection\n", "CONFIG_FILE = \"equity\" # Options: \"equity\", \"crypto\", or custom filename (without .cfg extension)\n", "\n", "# Trading pair symbols\n", "SYMBOL_A = \"COIN\" # Change this to your desired symbol A\n", "SYMBOL_B = \"MSTR\" # Change this to your desired symbol B\n", "\n", "# Date for data file selection (format: YYYYMMDD)\n", "TRADING_DATE = \"20250605\" # Change this to your desired date\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# Setup and Configuration" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Code Setup" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Setup complete!\n" ] } ], "source": [ "import sys\n", "import os\n", "sys.path.append('/home/oleg/develop/pairs_trading/lib')\n", "sys.path.append('/home/coder/pairs_trading/lib')\n", "\n", "import pandas as pd\n", "import numpy as np\n", "import matplotlib.pyplot as plt\n", "import seaborn as sns\n", "import importlib\n", "from typing import Dict, List, Optional\n", "from IPython.display import clear_output\n", "\n", "# Import our modules\n", "from pt_trading.fit_methods import StaticFit, SlidingFit, PairState\n", "from tools.data_loader import load_market_data\n", "from pt_trading.trading_pair import TradingPair\n", "from pt_trading.results import BacktestResult\n", "\n", "# Set plotting style\n", "plt.style.use('seaborn-v0_8')\n", "sns.set_palette(\"husl\")\n", "plt.rcParams['figure.figsize'] = (15, 10)\n", "\n", "print(\"Setup complete!\")\n" ] }, { "cell_type": "markdown", "metadata": { "vscode": { "languageId": "raw" } }, "source": [ "## Load Configuration\n" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [], "source": [ "# Load Configuration from Configuration Files using HJSON\n", "import hjson\n", "import os\n", "\n", "def load_config_from_file(config_type) -> Optional[Dict]:\n", " \"\"\"Load configuration from configuration files using HJSON\"\"\"\n", " config_file = f\"../../configuration/{config_type}.cfg\"\n", " \n", " try:\n", " with open(config_file, 'r') as f:\n", " # HJSON handles comments, trailing commas, and other human-friendly features\n", " config = hjson.load(f)\n", " \n", " # Convert relative paths to absolute paths from notebook perspective\n", " if 'data_directory' in config:\n", " data_dir = config['data_directory']\n", " if data_dir.startswith('./'):\n", " # Convert relative path to absolute path from notebook's perspective\n", " config['data_directory'] = os.path.abspath(f\"../../{data_dir[2:]}\")\n", " \n", " return config\n", " \n", " except FileNotFoundError:\n", " print(f\"Configuration file not found: {config_file}\")\n", " return None\n", " except hjson.HjsonDecodeError as e:\n", " print(f\"HJSON parsing error in {config_file}: {e}\")\n", " return None\n", " except Exception as e:\n", " print(f\"Unexpected error loading config from {config_file}: {e}\")\n", " return None\n", "\n", "def instantiate_fit_method_from_config(config: Dict):\n", " \"\"\"Dynamically instantiate strategy from config\"\"\"\n", " fit_method_class_name = config.get(\"fit_method_class\", None)\n", " assert fit_method_class_name is not None\n", " try:\n", " # Split module and class name\n", " if '.' in fit_method_class_name:\n", " module_name, class_name = fit_method_class_name.rsplit('.', 1)\n", " else:\n", " module_name = \"fit_methods\"\n", " class_name = fit_method_class_name\n", " \n", " # Import module and get class\n", " module = importlib.import_module(module_name)\n", " fit_method_class = getattr(module, class_name)\n", " \n", " # Instantiate strategy\n", " return fit_method_class()\n", " \n", " except Exception as e:\n", " print(f\"Error instantiating strategy {fit_method_class_name}: {e}\")\n", " raise Exception(f\"Error instantiating strategy {fit_method_class_name}: {e}\") from e\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Print Configuration" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Trading Parameters:\n", " Configuration: equity\n", " Symbol A: COIN\n", " Symbol B: MSTR\n", " Trading Date: 20250605\n", "\n", "Loading equity configuration using HJSON...\n", "✓ Successfully loaded EQUITY configuration\n", " Data directory: /home/oleg/develop/pairs_trading/data/equity\n", " Database table: md_1min_bars\n", " Exchange: ALPACA\n", " Training window: 120 minutes\n", " Open threshold: 2\n", " Close threshold: 1\n", " Strategy: SlidingFit\n", "\n", "Data Configuration:\n", " Data File: 20250605.mktdata.ohlcv.db\n", " Security Type: EQUITY\n", " ✓ Data file found: /home/oleg/develop/pairs_trading/data/equity/20250605.mktdata.ohlcv.db\n" ] } ], "source": [ "print(f\"Trading Parameters:\")\n", "print(f\" Configuration: {CONFIG_FILE}\")\n", "print(f\" Symbol A: {SYMBOL_A}\")\n", "print(f\" Symbol B: {SYMBOL_B}\")\n", "print(f\" Trading Date: {TRADING_DATE}\")\n", "\n", "# Load the specified configuration\n", "print(f\"\\nLoading {CONFIG_FILE} configuration using HJSON...\")\n", "\n", "CONFIG = load_config_from_file(CONFIG_FILE)\n", "assert CONFIG is not None\n", "pt_bt_config: Dict = dict(CONFIG)\n", "\n", "if pt_bt_config:\n", " print(f\"✓ Successfully loaded {pt_bt_config['security_type']} configuration\")\n", " print(f\" Data directory: {pt_bt_config['data_directory']}\")\n", " print(f\" Database table: {pt_bt_config['db_table_name']}\")\n", " print(f\" Exchange: {pt_bt_config['exchange_id']}\")\n", " print(f\" Training window: {pt_bt_config['training_minutes']} minutes\")\n", " print(f\" Open threshold: {pt_bt_config['dis-equilibrium_open_trshld']}\")\n", " print(f\" Close threshold: {pt_bt_config['dis-equilibrium_close_trshld']}\")\n", " \n", " # Instantiate strategy from config\n", " FIT_MODEL = instantiate_fit_method_from_config(pt_bt_config)\n", " print(f\" Strategy: {type(FIT_MODEL).__name__}\")\n", " \n", " # Automatically construct data file name based on date and config type\n", " DATA_FILE = f\"{TRADING_DATE}.mktdata.ohlcv.db\"\n", "\n", " # Update CONFIG with the specific data file and instruments\n", " pt_bt_config[\"datafiles\"] = [DATA_FILE]\n", " pt_bt_config[\"instruments\"] = [SYMBOL_A, SYMBOL_B]\n", " \n", " print(f\"\\nData Configuration:\")\n", " print(f\" Data File: {DATA_FILE}\")\n", " print(f\" Security Type: {pt_bt_config['security_type']}\")\n", " \n", " # Verify data file exists\n", " data_file_path = f\"{pt_bt_config['data_directory']}/{DATA_FILE}\"\n", " if os.path.exists(data_file_path):\n", " print(f\" ✓ Data file found: {data_file_path}\")\n", " else:\n", " print(f\" ⚠ Data file not found: {data_file_path}\")\n", " print(f\" Please check if the date and file exist in the data directory\")\n", " \n", " # List available files in the data directory\n", " try:\n", " data_dir = pt_bt_config['data_directory']\n", " if os.path.exists(data_dir):\n", " available_files = [f for f in os.listdir(data_dir) if f.endswith('.db')]\n", " print(f\" Available files in {data_dir}:\")\n", " for file in sorted(available_files)[:5]: # Show first 5 files\n", " print(f\" - {file}\")\n", " if len(available_files) > 5:\n", " print(f\" ... and {len(available_files)-5} more files\")\n", " except Exception as e:\n", " print(f\" Could not list files in data directory: {e}\")\n", "else:\n", " print(\"⚠ Failed to load configuration. Please check the configuration file.\")\n" ] }, { "cell_type": "markdown", "metadata": { "vscode": { "languageId": "raw" } }, "source": [ "## Load and Prepare Market Data\n" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Loading data from: /home/oleg/develop/pairs_trading/data/equity/20250605.mktdata.ohlcv.db\n", "Loaded 782 rows of market data\n", "Symbols in data: ['COIN' 'MSTR']\n", "Time range: 2025-06-05 13:30:00 to 2025-06-05 20:00:00\n", "\n", "Created trading pair: COIN & MSTR\n", "Market data shape: (391, 3)\n", "Column names: ['close_COIN', 'close_MSTR']\n", "\n", "Sample data:\n" ] }, { "data": { "text/html": [ "
| \n", " | tstamp | \n", "close_COIN | \n", "close_MSTR | \n", "
|---|---|---|---|
| 0 | \n", "2025-06-05 13:30:00 | \n", "263.380 | \n", "384.7700 | \n", "
| 1 | \n", "2025-06-05 13:31:00 | \n", "265.385 | \n", "382.7806 | \n", "
| 2 | \n", "2025-06-05 13:32:00 | \n", "263.735 | \n", "379.8300 | \n", "
| 3 | \n", "2025-06-05 13:33:00 | \n", "264.250 | \n", "380.0400 | \n", "
| 4 | \n", "2025-06-05 13:34:00 | \n", "262.230 | \n", "379.6400 | \n", "