{ "cells": [ { "cell_type": "code", "execution_count": null, "id": "8d6338ad", "metadata": {}, "outputs": [], "source": [ "import pandas as pd\n", "import numpy as np\n", "import csv\n", "\n", "# ====================================\n", "# Load BTC and ETH close prices\n", "# ====================================\n", "btc = pd.read_csv(r'C:\\Users\\amirs\\OneDrive\\Desktop\\myAlgoCode\\detector\\BTCUSDT_1m_1000d.csv')\n", "eth = pd.read_csv(r'C:\\Users\\amirs\\OneDrive\\Desktop\\myAlgoCode\\detector\\ETHUSDT_1m_1000d.csv')\n", "\n", "btc_price = btc['close']\n", "eth_price = eth['close']\n", "\n", "initial_capital = 1000\n", "trade_size = 200 # maximum notional per trade (USD)\n", "\n", "\n", "def run_strategy(k: float, fee_rate: float, window_days: int) -> dict:\n", " \"\"\"\n", " Run a simple pair-trading strategy between BTC and ETH.\n", "\n", " The strategy:\n", " - Compute the spread = BTC - ETH\n", " - Calculate rolling mean and std over a given window\n", " - Define upper/lower thresholds = mean ± k * std\n", " - If spread > upper bound -> sell BTC (partial, $200), buy ETH\n", " - If spread < lower bound -> sell ETH (partial, $200), buy BTC\n", " - Each trade pays a fee (fee_rate)\n", "\n", " Parameters\n", " ----------\n", " k : float\n", " Number of standard deviations for threshold.\n", " fee_rate : float\n", " Transaction fee rate (e.g., 0.001 = 0.1%).\n", " window_days : int\n", " Rolling window size in days (assuming 1-min candles).\n", "\n", " Returns\n", " -------\n", " dict : containing performance metrics:\n", " - k, fee_rate, window_days\n", " - trades (number of trades executed)\n", " - final_strat (final portfolio value)\n", " - return_strat (% return of strategy)\n", " - return_bh_equal (% return of equal-weight BTC/ETH benchmark)\n", " - return_bh_btc (% return of buy&hold BTC)\n", " - return_bh_eth (% return of buy&hold ETH)\n", " - avg_interval (average minutes between trades)\n", " \"\"\"\n", " # Spread between BTC and ETH\n", " spread = btc_price - eth_price\n", " window = 60 * 24 * window_days # convert days to minutes\n", " rolling_mean = spread.rolling(window).mean()\n", " rolling_std = spread.rolling(window).std()\n", "\n", " # Upper / Lower thresholds\n", " upper_bound = rolling_mean + k * rolling_std\n", " lower_bound = rolling_mean - k * rolling_std\n", " zt = spread\n", "\n", " # Initial portfolio: $500 BTC + $500 ETH\n", " btc_units = 500 / btc_price.iloc[0]\n", " eth_units = 500 / eth_price.iloc[0]\n", "\n", " portfolio_values = []\n", " strategy_values = []\n", " trade_log = []\n", "\n", " for t in range(len(spread)):\n", " # Benchmark portfolio values\n", " bh_btc = (500 / btc_price.iloc[0]) * btc_price.iloc[t]\n", " bh_eth = (500 / eth_price.iloc[0]) * eth_price.iloc[t]\n", " portfolio_values.append((bh_btc, bh_eth))\n", "\n", " total_value = btc_units * btc_price.iloc[t] + eth_units * eth_price.iloc[t]\n", "\n", " if not np.isnan(upper_bound.iloc[t]):\n", " if zt.iloc[t] > upper_bound.iloc[t]:\n", " # BTC expensive -> sell BTC, buy ETH\n", " btc_value = btc_units * btc_price.iloc[t]\n", " if btc_value > 0:\n", " sell_value = min(trade_size, btc_value)\n", " fee = sell_value * fee_rate\n", " sell_value_after_fee = sell_value - fee\n", " btc_units -= sell_value / btc_price.iloc[t]\n", " eth_units += sell_value_after_fee / eth_price.iloc[t]\n", " trade_log.append(t)\n", "\n", " elif zt.iloc[t] < lower_bound.iloc[t]:\n", " # BTC cheap -> sell ETH, buy BTC\n", " eth_value = eth_units * eth_price.iloc[t]\n", " if eth_value > 0:\n", " sell_value = min(trade_size, eth_value)\n", " fee = sell_value * fee_rate\n", " sell_value_after_fee = sell_value - fee\n", " eth_units -= sell_value / eth_price.iloc[t]\n", " btc_units += sell_value_after_fee / btc_price.iloc[t]\n", " trade_log.append(t)\n", "\n", " strat_value = btc_units * btc_price.iloc[t] + eth_units * eth_price.iloc[t]\n", " strategy_values.append(strat_value)\n", "\n", " # Benchmark final values\n", " bh_btc_value = [x[0] for x in portfolio_values]\n", " bh_eth_value = [x[1] for x in portfolio_values]\n", " bh_equal = [x[0] + x[1] for x in portfolio_values]\n", "\n", " final_btc = bh_btc_value[-1]\n", " final_eth = bh_eth_value[-1]\n", " final_equal = bh_equal[-1]\n", " final_strat = strategy_values[-1]\n", "\n", " # Trade interval stats\n", " if len(trade_log) > 1:\n", " trade_intervals = np.diff(trade_log)\n", " avg_interval = np.mean(trade_intervals)\n", " else:\n", " avg_interval = None\n", "\n", " return {\n", " \"k\": k,\n", " \"fee_rate\": fee_rate,\n", " \"window_days\": window_days,\n", " \"trades\": len(trade_log),\n", " \"final_strat\": final_strat,\n", " \"return_strat\": (final_strat / initial_capital - 1) * 100,\n", " \"return_bh_equal\": (final_equal / initial_capital - 1) * 100,\n", " \"return_bh_btc\": (final_btc / 500 - 1) * 100,\n", " \"return_bh_eth\": (final_eth / 500 - 1) * 100,\n", " \"avg_interval\": avg_interval,\n", " }\n", "\n", "\n", "# ====================================\n", "# 3. Grid Search\n", "# ====================================\n", "k_list = [0.5, 1, 1.5, 2]\n", "fee_list = [0.0005, 0.001, 0.002] # 0.05% to 0.2%\n", "window_list = [5, 10, 20] # 5, 10, 20-day windows\n", "\n", "output_file = \"grid_results.csv\"\n", "\n", "with open(output_file, \"w\", newline=\"\") as f:\n", " writer = csv.DictWriter(\n", " f,\n", " fieldnames=[\n", " \"k\",\n", " \"fee_rate\",\n", " \"window_days\",\n", " \"trades\",\n", " \"final_strat\",\n", " \"return_strat\",\n", " \"return_bh_equal\",\n", " \"return_bh_btc\",\n", " \"return_bh_eth\",\n", " \"avg_interval\",\n", " ],\n", " )\n", " writer.writeheader()\n", "\n", " # Iterate over all parameter combinations\n", " for k in k_list:\n", " for fee in fee_list:\n", " for win in window_list:\n", " result = run_strategy(k, fee, win)\n", " writer.writerow(result)\n", " print(\"Done:\", result)\n" ] } ], "metadata": { "language_info": { "name": "python" } }, "nbformat": 4, "nbformat_minor": 5 }