Prediction and model training work
This commit is contained in:
1 parent
fddcc9190a
commit
465e09fc82
13 files changed
+1689
-403
No files matched your search
@@ -0,0 +1,164 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 11,
|
||||
"id": "c8a08105",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import os\n",
|
||||
"from dotenv import load_dotenv\n",
|
||||
"from alpaca.trading.client import TradingClient\n",
|
||||
"\n",
|
||||
"load_dotenv()\n",
|
||||
"\n",
|
||||
"# Set paper=True for paper trading (sandbox), paper=False for live trading\n",
|
||||
"trading_client = TradingClient(\n",
|
||||
" api_key=os.getenv(\"ALPACA_API_KEY\"),\n",
|
||||
" secret_key=os.getenv(\"ALPACA_SECRET_KEY\"),\n",
|
||||
" paper=True,\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 13,
|
||||
"id": "49b14380",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"No open positions.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"positions = trading_client.get_all_positions()\n",
|
||||
"\n",
|
||||
"if not positions:\n",
|
||||
" print(\"No open positions.\")\n",
|
||||
"else:\n",
|
||||
" print(\"Current Portfolio Positions:\")\n",
|
||||
" for pos in positions:\n",
|
||||
" print(\n",
|
||||
" f\"Symbol: {pos.symbol:<5} | \"\n",
|
||||
" f\"Qty: {pos.qty:<5} | \"\n",
|
||||
" f\"Avg Entry Price: ${float(pos.avg_entry_price):.2f} | \"\n",
|
||||
" f\"Current Price: ${float(pos.current_price):.2f} | \"\n",
|
||||
" f\"Unrealized P/L: ${float(pos.unrealized_pl):.2f}\"\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 16,
|
||||
"id": "c032239c",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"99997.95 99997.95\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"account = trading_client.get_account()\n",
|
||||
"print(\n",
|
||||
" account.cash,\n",
|
||||
" account.equity\n",
|
||||
")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 17,
|
||||
"id": "c759e40f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"No open orders found.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from alpaca.trading.requests import GetOrdersRequest\n",
|
||||
"from alpaca.trading.enums import QueryOrderStatus\n",
|
||||
"\n",
|
||||
"# Request only open orders\n",
|
||||
"request_params = GetOrdersRequest(status=QueryOrderStatus.OPEN)\n",
|
||||
"open_orders = trading_client.get_orders(filter=request_params)\n",
|
||||
"\n",
|
||||
"if not open_orders:\n",
|
||||
" print(\"No open orders found.\")\n",
|
||||
"else:\n",
|
||||
" print(f\"Found {len(open_orders)} open order(s):\")\n",
|
||||
" for order in open_orders:\n",
|
||||
" print(\n",
|
||||
" f\"ID: {order.id} | Symbol: {order.symbol} | \"\n",
|
||||
" f\"Side: {order.side} | Qty: {order.qty} | Status: {order.status}\"\n",
|
||||
" )"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 18,
|
||||
"id": "091cf061",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Submitted Order ID: 4838c16d-7a12-4dc3-80cb-7c5d1dbecda3 | Status: OrderStatus.ACCEPTED\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from alpaca.trading.requests import MarketOrderRequest\n",
|
||||
"from alpaca.trading.enums import OrderSide, TimeInForce\n",
|
||||
"\n",
|
||||
"# Define a market buy order for 10 shares of SPY\n",
|
||||
"market_order_data = MarketOrderRequest(\n",
|
||||
" symbol=\"SPY\",\n",
|
||||
" notional=100.0,\n",
|
||||
" side=OrderSide.BUY,\n",
|
||||
" time_in_force=TimeInForce.DAY,\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# Submit the order\n",
|
||||
"order = trading_client.submit_order(order_data=market_order_data)\n",
|
||||
"\n",
|
||||
"print(f\"Submitted Order ID: {order.id} | Status: {order.status}\")"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
@@ -0,0 +1,308 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "038bf2ce",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import warnings\n",
|
||||
"from pathlib import Path\n",
|
||||
"import numpy as np\n",
|
||||
"import pandas as pd\n",
|
||||
"\n",
|
||||
"# Source of truth for model inputs (must match training order exactly)\n",
|
||||
"FEATURE_COLUMNS = [\n",
|
||||
" \"SPY_ret_5\",\n",
|
||||
" \"SPY_ret_20\",\n",
|
||||
" \"SPY_dist_sma50\",\n",
|
||||
" \"VIX_change_5\",\n",
|
||||
" \"VIX_rank_20\",\n",
|
||||
" \"TLT_ret_10\",\n",
|
||||
" \"USO_ret_5\",\n",
|
||||
" \"SPY_TLT_ratio_ret\",\n",
|
||||
"]\n",
|
||||
"\n",
|
||||
"DEFAULT_SYMBOLS = {\n",
|
||||
" \"SPY\": \"SPY\",\n",
|
||||
" \"VIXY\": \"VIX\", # Change to \"VIX\": \"VIX\" if using raw VIX Parquet\n",
|
||||
" \"TLT\": \"TLT\",\n",
|
||||
" \"USO\": \"USO\",\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def load_raw_close_prices(\n",
|
||||
" raw_data_dir: Path, symbols: dict[str, str]\n",
|
||||
") -> pd.DataFrame:\n",
|
||||
" \"\"\"Reads raw Parquet files and merges close prices into a single inner-joined DataFrame.\"\"\"\n",
|
||||
" frames = []\n",
|
||||
" for symbol, alias in symbols.items():\n",
|
||||
" path = raw_data_dir / f\"{symbol}.parquet\"\n",
|
||||
" if not path.exists():\n",
|
||||
" raise FileNotFoundError(f\"Missing raw data file: {path}\")\n",
|
||||
"\n",
|
||||
" frame = pd.read_parquet(path)\n",
|
||||
" if \"date\" in frame.columns:\n",
|
||||
" frame = frame.set_index(\"date\")\n",
|
||||
"\n",
|
||||
" frame.index = pd.to_datetime(frame.index)\n",
|
||||
" frame = frame.sort_index()\n",
|
||||
" frames.append(frame[[\"close\"]].rename(columns={\"close\": f\"{alias}_close\"}))\n",
|
||||
"\n",
|
||||
" return pd.concat(frames, axis=1, join=\"inner\")\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def compute_features(prices: pd.DataFrame) -> pd.DataFrame:\n",
|
||||
" \"\"\"Computes engineered features from raw merged price history.\"\"\"\n",
|
||||
" df = prices.copy()\n",
|
||||
"\n",
|
||||
" # Target asset features\n",
|
||||
" df[\"SPY_ret_5\"] = df[\"SPY_close\"].pct_change(5)\n",
|
||||
" df[\"SPY_ret_20\"] = df[\"SPY_close\"].pct_change(20)\n",
|
||||
"\n",
|
||||
" sma_50 = df[\"SPY_close\"].rolling(50).mean()\n",
|
||||
" df[\"SPY_dist_sma50\"] = (df[\"SPY_close\"] - sma_50) / sma_50\n",
|
||||
"\n",
|
||||
" # Volatility / Market Stress\n",
|
||||
" df[\"VIX_change_5\"] = df[\"VIX_close\"].pct_change(5)\n",
|
||||
" df[\"VIX_rank_20\"] = df[\"VIX_close\"].rolling(20).rank(pct=True)\n",
|
||||
"\n",
|
||||
" # Macro & Relative ratios\n",
|
||||
" df[\"TLT_ret_10\"] = df[\"TLT_close\"].pct_change(10)\n",
|
||||
" df[\"USO_ret_5\"] = df[\"USO_close\"].pct_change(5)\n",
|
||||
" df[\"SPY_TLT_ratio_ret\"] = (df[\"SPY_close\"] / df[\"TLT_close\"]).pct_change(5)\n",
|
||||
"\n",
|
||||
" return df[FEATURE_COLUMNS]\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def get_latest_inference_features(\n",
|
||||
" raw_data_dir: Path,\n",
|
||||
" symbols: dict[str, str] | None = None,\n",
|
||||
" max_age_days: int = 1,\n",
|
||||
") -> pd.DataFrame:\n",
|
||||
" \"\"\"Loads raw prices, computes features, verifies date freshness,\n",
|
||||
"\n",
|
||||
" and returns the latest single row for model prediction.\n",
|
||||
" \"\"\"\n",
|
||||
" symbols = symbols or DEFAULT_SYMBOLS\n",
|
||||
"\n",
|
||||
" # 1. Load prices & compute rolling features\n",
|
||||
" prices = load_raw_close_prices(raw_data_dir, symbols)\n",
|
||||
" features = compute_features(prices).dropna()\n",
|
||||
"\n",
|
||||
" if features.empty:\n",
|
||||
" raise ValueError(\n",
|
||||
" \"Not enough historical rows to compute 50-day rolling window features.\"\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" # 2. Extract latest available row as a 1-row DataFrame\n",
|
||||
" latest_row = features.iloc[[-1]]\n",
|
||||
" latest_date = latest_row.index[0]\n",
|
||||
"\n",
|
||||
" # 3. Check data freshness and raise a warning if stale\n",
|
||||
" now = pd.Timestamp.now()\n",
|
||||
" latest_date_naive = (\n",
|
||||
" latest_date.tz_localize(None)\n",
|
||||
" if latest_date.tz is not None\n",
|
||||
" else latest_date\n",
|
||||
" )\n",
|
||||
" days_old = (now.floor(\"D\") - latest_date_naive.floor(\"D\")).days\n",
|
||||
"\n",
|
||||
" if days_old > max_age_days:\n",
|
||||
" warnings.warn(\n",
|
||||
" f\"STALE DATA WARNING: Latest feature row is from {latest_date.strftime('%Y-%m-%d')} \"\n",
|
||||
" f\"({days_old} day(s) old). Update raw Parquet files before executing trades.\",\n",
|
||||
" UserWarning,\n",
|
||||
" stacklevel=2,\n",
|
||||
" )\n",
|
||||
"\n",
|
||||
" return latest_row\n",
|
||||
"\n",
|
||||
"def get_target_exposure(\n",
|
||||
" p_pred: float, p_base: float, sensitivity: float = 5.0\n",
|
||||
") -> float:\n",
|
||||
" \"\"\"Maps predicted probability to a target portfolio equity allocation (0.0 to 1.0).\n",
|
||||
"\n",
|
||||
" - p_pred == p_base --> 50% Target Exposure (Neutral)\n",
|
||||
" - p_pred > p_base --> Scale up toward 100% (Bullish)\n",
|
||||
" - p_pred < p_base --> Scale down toward 0% (Bearish / Cash)\n",
|
||||
" \"\"\"\n",
|
||||
" # Calculate deviation from the historical average\n",
|
||||
" delta = p_pred - p_base\n",
|
||||
"\n",
|
||||
" # Base target allocation is 50% equity / 50% cash\n",
|
||||
" base_allocation = 0.50\n",
|
||||
"\n",
|
||||
" # Sensitivity controls how aggressively probability changes alter allocation\n",
|
||||
" # e.g., a +0.08 delta * 5.0 = +0.40 -> 90% Equity Allocation\n",
|
||||
" target_allocation = base_allocation + (delta * sensitivity)\n",
|
||||
"\n",
|
||||
" # Clamp bounds strictly between 0% (full cash) and 100% (full SPY)\n",
|
||||
" return float(np.clip(target_allocation, 0.0, 1.0))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "0b3d2c25",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Date: 2026-07-27 | Prob: 0.5843 | Base: 0.5830\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"0.5064256139268726"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"import xgboost as xgb\n",
|
||||
"\n",
|
||||
"# 1. Load model and metadata\n",
|
||||
"model = xgb.XGBClassifier()\n",
|
||||
"model.load_model(\"models/spy_xgb_v1.json\")\n",
|
||||
"\n",
|
||||
"with open(\"models/spy_xgb_v1_meta.json\", \"r\") as f:\n",
|
||||
" meta = json.load(f)\n",
|
||||
"\n",
|
||||
"# 2. Fetch latest features from raw parquet files\n",
|
||||
"RAW_DATA_DIR = Path(\"../data/ibkr/daily\")\n",
|
||||
"X_latest = get_latest_inference_features(RAW_DATA_DIR, max_age_days=1)\n",
|
||||
"\n",
|
||||
"# 3. Predict probability\n",
|
||||
"p_pred = float(model.predict_proba(X_latest[meta[\"feature_cols\"]])[0, 1])\n",
|
||||
"p_base = meta[\"p_base\"]\n",
|
||||
"\n",
|
||||
"print(\n",
|
||||
" f\"Date: {X_latest.index[0].date()} | Prob: {p_pred:.4f} | Base: {p_base:.4f}\"\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"get_target_exposure(p_pred, p_base, sensitivity=5.0)"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "3f823fa6",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/html": [
|
||||
"<div>\n",
|
||||
"<style scoped>\n",
|
||||
" .dataframe tbody tr th:only-of-type {\n",
|
||||
" vertical-align: middle;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe tbody tr th {\n",
|
||||
" vertical-align: top;\n",
|
||||
" }\n",
|
||||
"\n",
|
||||
" .dataframe thead th {\n",
|
||||
" text-align: right;\n",
|
||||
" }\n",
|
||||
"</style>\n",
|
||||
"<table border=\"1\" class=\"dataframe\">\n",
|
||||
" <thead>\n",
|
||||
" <tr style=\"text-align: right;\">\n",
|
||||
" <th></th>\n",
|
||||
" <th>SPY_ret_5</th>\n",
|
||||
" <th>SPY_ret_20</th>\n",
|
||||
" <th>SPY_dist_sma50</th>\n",
|
||||
" <th>VIX_change_5</th>\n",
|
||||
" <th>VIX_rank_20</th>\n",
|
||||
" <th>TLT_ret_10</th>\n",
|
||||
" <th>USO_ret_5</th>\n",
|
||||
" <th>SPY_TLT_ratio_ret</th>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>date</th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" <th></th>\n",
|
||||
" </tr>\n",
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>2026-07-27</th>\n",
|
||||
" <td>-0.004043</td>\n",
|
||||
" <td>0.013855</td>\n",
|
||||
" <td>-0.007938</td>\n",
|
||||
" <td>0.007065</td>\n",
|
||||
" <td>0.75</td>\n",
|
||||
" <td>-0.00262</td>\n",
|
||||
" <td>-0.005976</td>\n",
|
||||
" <td>-0.002378</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
],
|
||||
"text/plain": [
|
||||
" SPY_ret_5 SPY_ret_20 SPY_dist_sma50 VIX_change_5 VIX_rank_20 \\\n",
|
||||
"date \n",
|
||||
"2026-07-27 -0.004043 0.013855 -0.007938 0.007065 0.75 \n",
|
||||
"\n",
|
||||
" TLT_ret_10 USO_ret_5 SPY_TLT_ratio_ret \n",
|
||||
"date \n",
|
||||
"2026-07-27 -0.00262 -0.005976 -0.002378 "
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"X_latest"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "476fd7fd",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
+141
-20
@@ -17,7 +17,26 @@
|
||||
"execution_count": null,
|
||||
"id": "543426a2",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Connecting to IBKR Gateway at 127.0.0.1:4002 with client id 101...\n",
|
||||
"Failed to connect to IBKR Gateway: This event loop is already running\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Error 200, reqId 9: No security definition has been found for the request, contract: Stock(symbol='VUAA', exchange='SMART', currency='USD')\n",
|
||||
"Error 200, reqId 10: No security definition has been found for the request\n",
|
||||
"Canceled order: Trade(contract=Stock(symbol='VUAA', exchange='SMART', currency='USD'), order=MarketOrder(orderId=10, clientId=101, action='BUY', totalQuantity=0.7), orderStatus=OrderStatus(orderId=10, status='Cancelled', filled=0.0, remaining=0.0, avgFillPrice=0.0, permId=0, parentId=0, lastFillPrice=0.0, clientId=0, whyHeld='', mktCapPrice=0.0), fills=[], log=[TradeLogEntry(time=datetime.datetime(2026, 7, 30, 19, 36, 57, 539380, tzinfo=datetime.timezone.utc), status='PendingSubmit', message='', errorCode=0), TradeLogEntry(time=datetime.datetime(2026, 7, 30, 19, 36, 57, 743208, tzinfo=datetime.timezone.utc), status='Cancelled', message='Error 200, reqId 10: No security definition has been found for the request', errorCode=200)], advancedError='')\n",
|
||||
"Peer closed connection.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import sys\n",
|
||||
"from pathlib import Path\n",
|
||||
@@ -35,36 +54,55 @@
|
||||
"if str(src_path) not in sys.path:\n",
|
||||
" sys.path.insert(0, str(src_path))\n",
|
||||
"\n",
|
||||
"from trading_bot.data.fetch_ibkr_daily import (\n",
|
||||
" IBKR_CLIENT_ID,\n",
|
||||
" IBKR_CONNECT_TIMEOUT_SECONDS,\n",
|
||||
" IBKR_HOST,\n",
|
||||
" IBKR_PORT,\n",
|
||||
")\n",
|
||||
"IBKR_HOST = \"127.0.0.1\"\n",
|
||||
"IBKR_PORT = 4002\n",
|
||||
"IBKR_CLIENT_ID = 101\n",
|
||||
"IBKR_CONNECT_TIMEOUT_SECONDS = 10\n",
|
||||
"\n",
|
||||
"ib = IB()\n",
|
||||
"print(f\"Connecting to IBKR Gateway at {IBKR_HOST}:{IBKR_PORT} with client id {IBKR_CLIENT_ID}...\")\n",
|
||||
"ib.connect(IBKR_HOST, IBKR_PORT, clientId=IBKR_CLIENT_ID, timeout=IBKR_CONNECT_TIMEOUT_SECONDS)\n",
|
||||
"print(\"Connection established.\")\n"
|
||||
"try:\n",
|
||||
" ib.connect(IBKR_HOST, IBKR_PORT, clientId=IBKR_CLIENT_ID, timeout=IBKR_CONNECT_TIMEOUT_SECONDS)\n",
|
||||
" print(\"Connection established.\")\n",
|
||||
"except Exception as e:\n",
|
||||
" print(f\"Failed to connect to IBKR Gateway: {e}\")\n",
|
||||
" ib.disconnect()\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 7,
|
||||
"id": "ecdc721f",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Connected: True\n",
|
||||
"Client ID: 101\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"print(f\"Connected: {ib.isConnected()}\")\n",
|
||||
"print(f\"Client ID: {ib.clientId}\")\n"
|
||||
"print(f\"Client ID: {ib.client.clientId}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 8,
|
||||
"id": "10ebc240",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"No open portfolio positions were returned.\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"portfolio = ib.portfolio()\n",
|
||||
"if not portfolio:\n",
|
||||
@@ -79,27 +117,110 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 23,
|
||||
"id": "fed654ec",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Unknown contract: Stock(symbol='VUAA', exchange='SMART', currency='USD')\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Submitted order for VUAA: Trade(contract=Stock(symbol='VUAA', exchange='SMART', currency='USD'), order=MarketOrder(orderId=10, clientId=101, action='BUY', totalQuantity=0.7), orderStatus=OrderStatus(orderId=10, status='PendingSubmit', filled=0.0, remaining=0.0, avgFillPrice=0.0, permId=0, parentId=0, lastFillPrice=0.0, clientId=0, whyHeld='', mktCapPrice=0.0), fills=[], log=[TradeLogEntry(time=datetime.datetime(2026, 7, 30, 19, 36, 57, 539380, tzinfo=datetime.timezone.utc), status='PendingSubmit', message='', errorCode=0)], advancedError='')\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"from ib_insync import MarketOrder\n",
|
||||
"\n",
|
||||
"contract = Stock(\"SPY\", \"SMART\", \"USD\")\n",
|
||||
"ib.qualifyContracts(contract)\n",
|
||||
"contract = Stock(\"VUAA\", \"SMART\", \"USD\")\n",
|
||||
"await ib.qualifyContractsAsync(contract)\n",
|
||||
"\n",
|
||||
"# Adjust the quantity as needed before running this cell.\n",
|
||||
"order = MarketOrder(\"BUY\", 1)\n",
|
||||
"order = MarketOrder(\"BUY\", 0.70)\n",
|
||||
"trade = ib.placeOrder(contract, order)\n",
|
||||
"\n",
|
||||
"print(f\"Submitted order for {contract.symbol}: {trade}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 34,
|
||||
"id": "06881101",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"trds = ib.trades()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 42,
|
||||
"id": "08663602",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"['Cancelled', 'Cancelled', 'Cancelled']"
|
||||
]
|
||||
},
|
||||
"execution_count": 42,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"list(map(lambda x: x.orderStatus.status, trds))"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 43,
|
||||
"id": "2c7abf23",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"[Trade(contract=Stock(conId=756733, symbol='SPY', right='?', exchange='SMART', currency='USD', localSymbol='SPY', tradingClass='SPY'), order=Order(permId=1578393268, action='BUY', totalQuantity=1.0, orderType='MKT', lmtPrice=0.0, auxPrice=0.0, tif='DAY', ocaType=3, displaySize=2147483647, rule80A='0', openClose='', volatilityType=0, deltaNeutralOrderType='None', referencePriceType=0, account='DUR281921', clearingIntent='IB', cashQty=0.0, dontUseAutoPriceForHedge=True, filledQuantity=0.0, refFuturesConId=2147483647, shareholder='Not an insider or substantial shareholder'), orderStatus=OrderStatus(orderId=0, status='Cancelled', filled=0.0, remaining=0.0, avgFillPrice=0.0, permId=0, parentId=0, lastFillPrice=0.0, clientId=0, whyHeld='', mktCapPrice=0.0), fills=[], log=[], advancedError=''),\n",
|
||||
" Trade(contract=Stock(conId=756733, symbol='SPY', right='?', exchange='SMART', currency='USD', localSymbol='SPY', tradingClass='SPY'), order=Order(permId=24474350, action='BUY', totalQuantity=0.1345, orderType='MKT', lmtPrice=0.0, auxPrice=0.0, tif='DAY', ocaType=3, displaySize=2147483647, rule80A='0', openClose='', volatilityType=0, deltaNeutralOrderType='None', referencePriceType=0, account='DUR281921', clearingIntent='IB', cashQty=0.0, dontUseAutoPriceForHedge=True, filledQuantity=0.0, refFuturesConId=2147483647, shareholder='Not an insider or substantial shareholder'), orderStatus=OrderStatus(orderId=0, status='Cancelled', filled=0.0, remaining=0.0, avgFillPrice=0.0, permId=0, parentId=0, lastFillPrice=0.0, clientId=0, whyHeld='', mktCapPrice=0.0), fills=[], log=[], advancedError=''),\n",
|
||||
" Trade(contract=Stock(symbol='VUAA', exchange='SMART', currency='USD'), order=MarketOrder(orderId=10, clientId=101, action='BUY', totalQuantity=0.7), orderStatus=OrderStatus(orderId=10, status='Cancelled', filled=0.0, remaining=0.0, avgFillPrice=0.0, permId=0, parentId=0, lastFillPrice=0.0, clientId=0, whyHeld='', mktCapPrice=0.0), fills=[], log=[TradeLogEntry(time=datetime.datetime(2026, 7, 30, 19, 36, 57, 539380, tzinfo=datetime.timezone.utc), status='PendingSubmit', message='', errorCode=0), TradeLogEntry(time=datetime.datetime(2026, 7, 30, 19, 36, 57, 743208, tzinfo=datetime.timezone.utc), status='Cancelled', message='Error 200, reqId 10: No security definition has been found for the request', errorCode=200)], advancedError='')]"
|
||||
]
|
||||
},
|
||||
"execution_count": 43,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"trds"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"language": "python",
|
||||
"name": "python3"
|
||||
},
|
||||
"language_info": {
|
||||
"name": "python"
|
||||
"codemirror_mode": {
|
||||
"name": "ipython",
|
||||
"version": 3
|
||||
},
|
||||
"file_extension": ".py",
|
||||
"mimetype": "text/x-python",
|
||||
"name": "python",
|
||||
"nbconvert_exporter": "python",
|
||||
"pygments_lexer": "ipython3",
|
||||
"version": "3.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"learner":{"attributes":{"best_iteration":"41","best_score":"0.6593948980172475","scikit_learn":"{\"_estimator_type\": \"classifier\"}"},"feature_names":["VIX_rank_20","TLT_ret_10","USO_ret_5","SPY_TLT_ratio_ret","SPY_ret_5","SPY_ret_20","SPY_dist_sma50"],"feature_types":["float","float","float","float","float","float","float"],"gradient_booster":{"model":{"cats":{"enc":[],"feature_segments":[],"sorted_idx":[]},"gbtree_model_param":{"num_parallel_tree":"1","num_trees":"62"},"iteration_indptr":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62],"tree_info":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"trees":[{"base_weights":[1.1503149E-2,3.602216E-4,-1.4237287E-2],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":0,"left_children":[1,-1,-1],"loss_changes":[5.1499934E0,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[6.892705E-2,3.602216E-4,-1.4237287E-2],"split_indices":[6,0,0],"split_type":[0,0,0],"sum_hessian":[1.4388889E2,1.4243056E2,1.4583333E0],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[2.2381552E-2,2.2380585E-3,-2.9987623E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":1,"left_children":[1,-1,-1],"loss_changes":[9.187705E0,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[1.4205446E-2,2.2380585E-3,-2.9987623E-3],"split_indices":[3,0,0],"split_type":[0,0,0],"sum_hessian":[1.3951015E2,8.603824E1,5.3471912E1],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[9.584328E-2,-1.7863275E-3,6.1051417E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":2,"left_children":[1,-1,-1],"loss_changes":[2.0163263E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[7.100592E-3,-1.7863275E-3,6.1051417E-3],"split_indices":[1,0,0],"split_type":[0,0,0],"sum_hessian":[1.4096178E2,9.2359184E1,4.86026E1],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[4.7446564E-2,7.0196665E-3,-1.0298543E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":3,"left_children":[1,-1,-1],"loss_changes":[1.4477959E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[-1.7963655E-2,7.0196665E-3,-1.0298543E-3],"split_indices":[3,0,0],"split_type":[0,0,0],"sum_hessian":[1.4530872E2,2.6470573E1,1.1883814E2],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[6.2338286E-3,3.326677E-3,-1.7981239E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":4,"left_children":[1,-1,-1],"loss_changes":[8.945065E0,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[-1.1166078E-2,3.326677E-3,-1.7981239E-3],"split_indices":[2,0,0],"split_type":[0,0,0],"sum_hessian":[1.4530305E2,5.2463432E1,9.283963E1],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[-3.0314475E-2,6.923221E-3,-1.2672357E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":5,"left_children":[1,-1,-1],"loss_changes":[1.0347677E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[-2.858291E-2,6.923221E-3,-1.2672357E-3],"split_indices":[3,0,0],"split_type":[0,0,0],"sum_hessian":[1.4626752E2,1.648944E1,1.2977808E2],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[2.5649436E-2,-3.922325E-3,4.0787407E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":6,"left_children":[1,-1,-1],"loss_changes":[2.3413443E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[-8.8626295E-3,-3.922325E-3,4.0787407E-3],"split_indices":[1,0,0],"split_type":[0,0,0],"sum_hessian":[1.4458455E2,6.905801E1,7.5526535E1],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[-3.350957E-2,2.346545E-3,-3.7299043E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":7,"left_children":[1,-1,-1],"loss_changes":[1.3207154E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[1.2120422E-2,2.346545E-3,-3.7299043E-3],"split_indices":[2,0,0],"split_type":[0,0,0],"sum_hessian":[1.431Line truncated
|
||||
{"learner":{"attributes":{"best_iteration":"131","best_score":"0.6530751986636056","scikit_learn":"{\"_estimator_type\": \"classifier\"}"},"feature_names":["VIX_rank_20","TLT_ret_10","USO_ret_5","SPY_TLT_ratio_ret","SPY_ret_5","SPY_ret_20","SPY_dist_sma50"],"feature_types":["float","float","float","float","float","float","float"],"gradient_booster":{"model":{"cats":{"enc":[],"feature_segments":[],"sorted_idx":[]},"gbtree_model_param":{"num_parallel_tree":"1","num_trees":"152"},"iteration_indptr":[0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152],"tree_info":[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0],"trees":[{"base_weights":[9.09237E-3,3.342369E-4,-1.4195536E-2],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":0,"left_children":[1,-1,-1],"loss_changes":[5.105532E0,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[6.892705E-2,3.342369E-4,-1.4195536E-2],"split_indices":[6,0,0],"split_type":[0,0,0],"sum_hessian":[1.4451103E2,1.4305133E2,1.4597074E0],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[1.9695353E-2,2.3339752E-3,-3.2156436E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":1,"left_children":[1,-1,-1],"loss_changes":[1.0367284E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[1.4354015E-2,2.3339752E-3,-3.2156436E-3],"split_indices":[3,0,0],"split_type":[0,0,0],"sum_hessian":[1.4012877E2,8.636299E1,5.376578E1],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[9.294399E-2,-1.785295E-3,6.0087293E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":2,"left_children":[1,-1,-1],"loss_changes":[1.9767847E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[7.100592E-3,-1.785295E-3,6.0087293E-3],"split_indices":[1,0,0],"split_type":[0,0,0],"sum_hessian":[1.4158235E2,9.269047E1,4.8891888E1],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[4.908758E-2,7.0679425E-3,-1.0177689E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":3,"left_children":[1,-1,-1],"loss_changes":[1.4627343E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[-1.7963655E-2,7.0679425E-3,-1.0177689E-3],"split_indices":[3,0,0],"split_type":[0,0,0],"sum_hessian":[1.4569156E2,2.6495813E1,1.1919575E2],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[3.9804457E-3,3.4415158E-3,-1.9024174E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":4,"left_children":[1,-1,-1],"loss_changes":[9.772864E0,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[-1.0558252E-2,3.4415158E-3,-1.9024174E-3],"split_indices":[2,0,0],"split_type":[0,0,0],"sum_hessian":[1.4592857E2,5.2756344E1,9.317223E1],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[-2.8463421E-2,6.9694594E-3,-1.250735E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":5,"left_children":[1,-1,-1],"loss_changes":[1.0434825E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[-2.858291E-2,6.9694594E-3,-1.250735E-3],"split_indices":[3,0,0],"split_type":[0,0,0],"sum_hessian":[1.4665097E2,1.6505238E1,1.3014574E2],"tree_param":{"num_deleted":"0","num_feature":"7","num_nodes":"3","size_leaf_vector":"1"}},{"base_weights":[2.73039E-2,-3.935056E-3,4.084229E-3],"categories":[],"categories_nodes":[],"categories_segments":[],"categories_sizes":[],"default_left":[0,0,0],"id":6,"left_children":[1,-1,-1],"loss_changes":[2.3570513E1,0E0,0E0],"parents":[2147483647,0,0],"right_children":[2,-1,-1],"split_conditions":[-9.210526E-3,-3.935056E-3,4.084229E-3],"split_indices":[1,0,0],"split_type":[0,0,0],"sum_hessian":[1.4496594E2,6.888007E1,7.608588E1],"tree_paLine truncated
|
||||
@@ -1,5 +1,5 @@
|
||||
{
|
||||
"p_base": 0.5833333333333334,
|
||||
"p_base": 0.5819477434679335,
|
||||
"feature_cols": [
|
||||
"VIX_rank_20",
|
||||
"TLT_ret_10",
|
||||
@@ -9,5 +9,5 @@
|
||||
"SPY_ret_20",
|
||||
"SPY_dist_sma50"
|
||||
],
|
||||
"last_trained_date": "2026-07-24 00:00:00"
|
||||
"last_trained_date": "2026-07-29 00:00:00"
|
||||
}
|
||||
@@ -20,14 +20,14 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 1,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(PosixPath('/home/jarno/repos/trading-bot'),\n",
|
||||
" PosixPath('/home/jarno/repos/trading-bot/data/ibkr/daily'),\n",
|
||||
" PosixPath('/home/jarno/repos/trading-bot/data/alpaca/daily'),\n",
|
||||
" PosixPath('/home/jarno/repos/trading-bot/data/training/spy_direction_5d.parquet'))"
|
||||
]
|
||||
},
|
||||
@@ -87,16 +87,16 @@
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"<class 'pandas.DataFrame'>\n",
|
||||
"DatetimeIndex: 1251 entries, 2021-07-30 to 2026-07-24\n",
|
||||
"DatetimeIndex: 1255 entries, 2021-08-02 to 2026-07-31\n",
|
||||
"Data columns (total 4 columns):\n",
|
||||
" # Column Non-Null Count Dtype \n",
|
||||
"--- ------ -------------- ----- \n",
|
||||
" 0 SPY_close 1251 non-null float64\n",
|
||||
" 1 VIX_close 1251 non-null float64\n",
|
||||
" 2 TLT_close 1251 non-null float64\n",
|
||||
" 3 USO_close 1251 non-null float64\n",
|
||||
" 0 SPY_close 1255 non-null float64\n",
|
||||
" 1 VIX_close 1255 non-null float64\n",
|
||||
" 2 TLT_close 1255 non-null float64\n",
|
||||
" 3 USO_close 1255 non-null float64\n",
|
||||
"dtypes: float64(4)\n",
|
||||
"memory usage: 48.9 KB\n"
|
||||
"memory usage: 49.0 KB\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -135,40 +135,40 @@
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-07-30</th>\n",
|
||||
" <td>438.51</td>\n",
|
||||
" <td>495.4</td>\n",
|
||||
" <td>149.52</td>\n",
|
||||
" <td>50.66</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-08-02</th>\n",
|
||||
" <td>437.59</td>\n",
|
||||
" <td>513.6</td>\n",
|
||||
" <td>25.68</td>\n",
|
||||
" <td>150.67</td>\n",
|
||||
" <td>49.18</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-08-03</th>\n",
|
||||
" <td>441.15</td>\n",
|
||||
" <td>488.0</td>\n",
|
||||
" <td>24.40</td>\n",
|
||||
" <td>150.75</td>\n",
|
||||
" <td>48.85</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-08-04</th>\n",
|
||||
" <td>438.98</td>\n",
|
||||
" <td>487.6</td>\n",
|
||||
" <td>24.38</td>\n",
|
||||
" <td>151.06</td>\n",
|
||||
" <td>47.20</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-08-05</th>\n",
|
||||
" <td>441.76</td>\n",
|
||||
" <td>474.8</td>\n",
|
||||
" <td>23.74</td>\n",
|
||||
" <td>150.29</td>\n",
|
||||
" <td>48.10</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-08-06</th>\n",
|
||||
" <td>442.49</td>\n",
|
||||
" <td>23.14</td>\n",
|
||||
" <td>147.78</td>\n",
|
||||
" <td>47.57</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
@@ -176,11 +176,11 @@
|
||||
"text/plain": [
|
||||
" SPY_close VIX_close TLT_close USO_close\n",
|
||||
"date \n",
|
||||
"2021-07-30 438.51 495.4 149.52 50.66\n",
|
||||
"2021-08-02 437.59 513.6 150.67 49.18\n",
|
||||
"2021-08-03 441.15 488.0 150.75 48.85\n",
|
||||
"2021-08-04 438.98 487.6 151.06 47.20\n",
|
||||
"2021-08-05 441.76 474.8 150.29 48.10"
|
||||
"2021-08-02 437.59 25.68 150.67 49.18\n",
|
||||
"2021-08-03 441.15 24.40 150.75 48.85\n",
|
||||
"2021-08-04 438.98 24.38 151.06 47.20\n",
|
||||
"2021-08-05 441.76 23.74 150.29 48.10\n",
|
||||
"2021-08-06 442.49 23.14 147.78 47.57"
|
||||
]
|
||||
},
|
||||
"execution_count": 2,
|
||||
@@ -234,7 +234,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -283,24 +283,12 @@
|
||||
" </thead>\n",
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-10-08</th>\n",
|
||||
" <td>0.008336</td>\n",
|
||||
" <td>-0.017017</td>\n",
|
||||
" <td>-0.011155</td>\n",
|
||||
" <td>-0.075239</td>\n",
|
||||
" <td>0.125</td>\n",
|
||||
" <td>-0.034239</td>\n",
|
||||
" <td>0.041495</td>\n",
|
||||
" <td>0.032998</td>\n",
|
||||
" <td>1.0</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-10-11</th>\n",
|
||||
" <td>0.014114</td>\n",
|
||||
" <td>-0.026625</td>\n",
|
||||
" <td>-0.018145</td>\n",
|
||||
" <td>-0.090869</td>\n",
|
||||
" <td>0.200</td>\n",
|
||||
" <td>0.20</td>\n",
|
||||
" <td>-0.033135</td>\n",
|
||||
" <td>0.031015</td>\n",
|
||||
" <td>0.038908</td>\n",
|
||||
@@ -312,7 +300,7 @@
|
||||
" <td>-0.023752</td>\n",
|
||||
" <td>-0.020386</td>\n",
|
||||
" <td>-0.074091</td>\n",
|
||||
" <td>0.100</td>\n",
|
||||
" <td>0.10</td>\n",
|
||||
" <td>-0.001041</td>\n",
|
||||
" <td>0.008628</td>\n",
|
||||
" <td>-0.001303</td>\n",
|
||||
@@ -324,7 +312,7 @@
|
||||
" <td>-0.028356</td>\n",
|
||||
" <td>-0.016597</td>\n",
|
||||
" <td>-0.081006</td>\n",
|
||||
" <td>0.050</td>\n",
|
||||
" <td>0.05</td>\n",
|
||||
" <td>0.006928</td>\n",
|
||||
" <td>0.036928</td>\n",
|
||||
" <td>-0.005897</td>\n",
|
||||
@@ -336,12 +324,24 @@
|
||||
" <td>-0.010443</td>\n",
|
||||
" <td>-0.000214</td>\n",
|
||||
" <td>-0.096759</td>\n",
|
||||
" <td>0.050</td>\n",
|
||||
" <td>0.05</td>\n",
|
||||
" <td>0.010809</td>\n",
|
||||
" <td>0.026192</td>\n",
|
||||
" <td>-0.011991</td>\n",
|
||||
" <td>1.0</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>2021-10-15</th>\n",
|
||||
" <td>0.018294</td>\n",
|
||||
" <td>0.010127</td>\n",
|
||||
" <td>0.007213</td>\n",
|
||||
" <td>-0.079882</td>\n",
|
||||
" <td>0.05</td>\n",
|
||||
" <td>-0.002202</td>\n",
|
||||
" <td>0.030287</td>\n",
|
||||
" <td>-0.003823</td>\n",
|
||||
" <td>1.0</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
"</div>"
|
||||
@@ -349,22 +349,22 @@
|
||||
"text/plain": [
|
||||
" SPY_ret_5 SPY_ret_20 SPY_dist_sma50 VIX_change_5 VIX_rank_20 \\\n",
|
||||
"date \n",
|
||||
"2021-10-08 0.008336 -0.017017 -0.011155 -0.075239 0.125 \n",
|
||||
"2021-10-11 0.014114 -0.026625 -0.018145 -0.090869 0.200 \n",
|
||||
"2021-10-12 0.001201 -0.023752 -0.020386 -0.074091 0.100 \n",
|
||||
"2021-10-13 0.000644 -0.028356 -0.016597 -0.081006 0.050 \n",
|
||||
"2021-10-14 0.008754 -0.010443 -0.000214 -0.096759 0.050 \n",
|
||||
"2021-10-11 0.014114 -0.026625 -0.018145 -0.090869 0.20 \n",
|
||||
"2021-10-12 0.001201 -0.023752 -0.020386 -0.074091 0.10 \n",
|
||||
"2021-10-13 0.000644 -0.028356 -0.016597 -0.081006 0.05 \n",
|
||||
"2021-10-14 0.008754 -0.010443 -0.000214 -0.096759 0.05 \n",
|
||||
"2021-10-15 0.018294 0.010127 0.007213 -0.079882 0.05 \n",
|
||||
"\n",
|
||||
" TLT_ret_10 USO_ret_5 SPY_TLT_ratio_ret spy_up_5d \n",
|
||||
"date \n",
|
||||
"2021-10-08 -0.034239 0.041495 0.032998 1.0 \n",
|
||||
"2021-10-11 -0.033135 0.031015 0.038908 1.0 \n",
|
||||
"2021-10-12 -0.001041 0.008628 -0.001303 1.0 \n",
|
||||
"2021-10-13 0.006928 0.036928 -0.005897 1.0 \n",
|
||||
"2021-10-14 0.010809 0.026192 -0.011991 1.0 "
|
||||
"2021-10-14 0.010809 0.026192 -0.011991 1.0 \n",
|
||||
"2021-10-15 -0.002202 0.030287 -0.003823 1.0 "
|
||||
]
|
||||
},
|
||||
"execution_count": 3,
|
||||
"execution_count": 4,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -384,7 +384,7 @@
|
||||
"df[\"TLT_ret_10\"] = df[\"TLT_close\"].pct_change(10)\n",
|
||||
"df[\"USO_ret_5\"] = df[\"USO_close\"].pct_change(5)\n",
|
||||
"df[\"SPY_TLT_ratio_ret\"] = (df[\"SPY_close\"] / df[\"TLT_close\"]).pct_change(5)\n",
|
||||
"df_model\n",
|
||||
"\n",
|
||||
"spy_forward_close = df[\"SPY_close\"].shift(-5)\n",
|
||||
"df[\"spy_up_5d\"] = np.nan\n",
|
||||
"df.loc[spy_forward_close > df[\"SPY_close\"], \"spy_up_5d\"] = 1.0\n",
|
||||
@@ -418,7 +418,7 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
@@ -458,24 +458,24 @@
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>train</th>\n",
|
||||
" <td>837</td>\n",
|
||||
" <td>2021-10-08</td>\n",
|
||||
" <td>2025-02-07</td>\n",
|
||||
" <td>0.583035</td>\n",
|
||||
" <td>840</td>\n",
|
||||
" <td>2021-10-11</td>\n",
|
||||
" <td>2025-02-13</td>\n",
|
||||
" <td>0.583333</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>validation</th>\n",
|
||||
" <td>179</td>\n",
|
||||
" <td>2025-02-10</td>\n",
|
||||
" <td>2025-10-24</td>\n",
|
||||
" <td>0.653631</td>\n",
|
||||
" <td>180</td>\n",
|
||||
" <td>2025-02-14</td>\n",
|
||||
" <td>2025-10-31</td>\n",
|
||||
" <td>0.633333</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>test</th>\n",
|
||||
" <td>181</td>\n",
|
||||
" <td>2025-10-27</td>\n",
|
||||
" <td>2026-07-17</td>\n",
|
||||
" <td>0.558011</td>\n",
|
||||
" <td>2025-11-03</td>\n",
|
||||
" <td>2026-07-24</td>\n",
|
||||
" <td>0.569061</td>\n",
|
||||
" </tr>\n",
|
||||
" </tbody>\n",
|
||||
"</table>\n",
|
||||
@@ -484,12 +484,12 @@
|
||||
"text/plain": [
|
||||
" rows start_date end_date target_mean\n",
|
||||
"split \n",
|
||||
"train 837 2021-10-08 2025-02-07 0.583035\n",
|
||||
"validation 179 2025-02-10 2025-10-24 0.653631\n",
|
||||
"test 181 2025-10-27 2026-07-17 0.558011"
|
||||
"train 840 2021-10-11 2025-02-13 0.583333\n",
|
||||
"validation 180 2025-02-14 2025-10-31 0.633333\n",
|
||||
"test 181 2025-11-03 2026-07-24 0.569061"
|
||||
]
|
||||
},
|
||||
"execution_count": 4,
|
||||
"execution_count": 5,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -543,17 +543,17 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Rows: 1,197\n",
|
||||
"Rows: 1,201\n",
|
||||
"Feature columns: ['SPY_ret_5', 'SPY_ret_20', 'SPY_dist_sma50', 'VIX_change_5', 'VIX_rank_20', 'TLT_ret_10', 'USO_ret_5', 'SPY_TLT_ratio_ret']\n",
|
||||
"Target column: spy_up_5d\n",
|
||||
"Training matrix shape: (837, 8)\n"
|
||||
"Training matrix shape: (840, 8)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -592,16 +592,16 @@
|
||||
" <tbody>\n",
|
||||
" <tr>\n",
|
||||
" <th>count</th>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197.000000</td>\n",
|
||||
" <td>1197</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201.000000</td>\n",
|
||||
" <td>1201</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>unique</th>\n",
|
||||
@@ -640,32 +640,32 @@
|
||||
" <td>NaN</td>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" <td>837</td>\n",
|
||||
" <td>840</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>mean</th>\n",
|
||||
" <td>0.002556</td>\n",
|
||||
" <td>0.009841</td>\n",
|
||||
" <td>0.011067</td>\n",
|
||||
" <td>-0.008646</td>\n",
|
||||
" <td>0.387009</td>\n",
|
||||
" <td>-0.004083</td>\n",
|
||||
" <td>0.004662</td>\n",
|
||||
" <td>0.004927</td>\n",
|
||||
" <td>0.589808</td>\n",
|
||||
" <td>0.002502</td>\n",
|
||||
" <td>0.009848</td>\n",
|
||||
" <td>0.011028</td>\n",
|
||||
" <td>0.017763</td>\n",
|
||||
" <td>0.403476</td>\n",
|
||||
" <td>-0.004099</td>\n",
|
||||
" <td>0.005024</td>\n",
|
||||
" <td>0.004880</td>\n",
|
||||
" <td>0.588676</td>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>std</th>\n",
|
||||
" <td>0.023081</td>\n",
|
||||
" <td>0.043634</td>\n",
|
||||
" <td>0.037371</td>\n",
|
||||
" <td>0.092413</td>\n",
|
||||
" <td>0.333697</td>\n",
|
||||
" <td>0.028557</td>\n",
|
||||
" <td>0.052319</td>\n",
|
||||
" <td>0.027656</td>\n",
|
||||
" <td>0.492074</td>\n",
|
||||
" <td>0.023057</td>\n",
|
||||
" <td>0.043558</td>\n",
|
||||
" <td>0.037315</td>\n",
|
||||
" <td>0.299417</td>\n",
|
||||
" <td>0.337501</td>\n",
|
||||
" <td>0.028502</td>\n",
|
||||
" <td>0.052628</td>\n",
|
||||
" <td>0.027606</td>\n",
|
||||
" <td>0.492279</td>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
@@ -683,40 +683,40 @@
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>25%</th>\n",
|
||||
" <td>-0.009535</td>\n",
|
||||
" <td>-0.017017</td>\n",
|
||||
" <td>-0.008847</td>\n",
|
||||
" <td>-0.058376</td>\n",
|
||||
" <td>0.075000</td>\n",
|
||||
" <td>-0.023517</td>\n",
|
||||
" <td>-0.026200</td>\n",
|
||||
" <td>-0.009601</td>\n",
|
||||
" <td>-0.016509</td>\n",
|
||||
" <td>-0.008656</td>\n",
|
||||
" <td>-0.056615</td>\n",
|
||||
" <td>0.100000</td>\n",
|
||||
" <td>-0.023304</td>\n",
|
||||
" <td>-0.025866</td>\n",
|
||||
" <td>-0.010013</td>\n",
|
||||
" <td>0.000000</td>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>50%</th>\n",
|
||||
" <td>0.003869</td>\n",
|
||||
" <td>0.015797</td>\n",
|
||||
" <td>0.017906</td>\n",
|
||||
" <td>-0.020655</td>\n",
|
||||
" <td>0.250000</td>\n",
|
||||
" <td>-0.004339</td>\n",
|
||||
" <td>0.004817</td>\n",
|
||||
" <td>0.005710</td>\n",
|
||||
" <td>0.003798</td>\n",
|
||||
" <td>0.015725</td>\n",
|
||||
" <td>0.017802</td>\n",
|
||||
" <td>-0.018570</td>\n",
|
||||
" <td>0.300000</td>\n",
|
||||
" <td>-0.004435</td>\n",
|
||||
" <td>0.004950</td>\n",
|
||||
" <td>0.005537</td>\n",
|
||||
" <td>1.000000</td>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" </tr>\n",
|
||||
" <tr>\n",
|
||||
" <th>75%</th>\n",
|
||||
" <td>0.015957</td>\n",
|
||||
" <td>0.038385</td>\n",
|
||||
" <td>0.038332</td>\n",
|
||||
" <td>0.027778</td>\n",
|
||||
" <td>0.700000</td>\n",
|
||||
" <td>0.014272</td>\n",
|
||||
" <td>0.031883</td>\n",
|
||||
" <td>0.021406</td>\n",
|
||||
" <td>0.015955</td>\n",
|
||||
" <td>0.038179</td>\n",
|
||||
" <td>0.038282</td>\n",
|
||||
" <td>0.030814</td>\n",
|
||||
" <td>0.750000</td>\n",
|
||||
" <td>0.014227</td>\n",
|
||||
" <td>0.032486</td>\n",
|
||||
" <td>0.021390</td>\n",
|
||||
" <td>1.000000</td>\n",
|
||||
" <td>NaN</td>\n",
|
||||
" </tr>\n",
|
||||
@@ -724,8 +724,8 @@
|
||||
" <th>max</th>\n",
|
||||
" <td>0.082843</td>\n",
|
||||
" <td>0.157566</td>\n",
|
||||
" <td>0.088103</td>\n",
|
||||
" <td>0.937828</td>\n",
|
||||
" <td>0.088105</td>\n",
|
||||
" <td>3.846154</td>\n",
|
||||
" <td>1.000000</td>\n",
|
||||
" <td>0.091322</td>\n",
|
||||
" <td>0.327273</td>\n",
|
||||
@@ -739,33 +739,33 @@
|
||||
],
|
||||
"text/plain": [
|
||||
" SPY_ret_5 SPY_ret_20 SPY_dist_sma50 VIX_change_5 VIX_rank_20 \\\n",
|
||||
"count 1197.000000 1197.000000 1197.000000 1197.000000 1197.000000 \n",
|
||||
"count 1201.000000 1201.000000 1201.000000 1201.000000 1201.000000 \n",
|
||||
"unique NaN NaN NaN NaN NaN \n",
|
||||
"top NaN NaN NaN NaN NaN \n",
|
||||
"freq NaN NaN NaN NaN NaN \n",
|
||||
"mean 0.002556 0.009841 0.011067 -0.008646 0.387009 \n",
|
||||
"std 0.023081 0.043634 0.037371 0.092413 0.333697 \n",
|
||||
"mean 0.002502 0.009848 0.011028 0.017763 0.403476 \n",
|
||||
"std 0.023057 0.043558 0.037315 0.299417 0.337501 \n",
|
||||
"min -0.114962 -0.123975 -0.141995 -0.387709 0.050000 \n",
|
||||
"25% -0.009535 -0.017017 -0.008847 -0.058376 0.075000 \n",
|
||||
"50% 0.003869 0.015797 0.017906 -0.020655 0.250000 \n",
|
||||
"75% 0.015957 0.038385 0.038332 0.027778 0.700000 \n",
|
||||
"max 0.082843 0.157566 0.088103 0.937828 1.000000 \n",
|
||||
"25% -0.009601 -0.016509 -0.008656 -0.056615 0.100000 \n",
|
||||
"50% 0.003798 0.015725 0.017802 -0.018570 0.300000 \n",
|
||||
"75% 0.015955 0.038179 0.038282 0.030814 0.750000 \n",
|
||||
"max 0.082843 0.157566 0.088105 3.846154 1.000000 \n",
|
||||
"\n",
|
||||
" TLT_ret_10 USO_ret_5 SPY_TLT_ratio_ret spy_up_5d split \n",
|
||||
"count 1197.000000 1197.000000 1197.000000 1197.000000 1197 \n",
|
||||
"count 1201.000000 1201.000000 1201.000000 1201.000000 1201 \n",
|
||||
"unique NaN NaN NaN NaN 3 \n",
|
||||
"top NaN NaN NaN NaN train \n",
|
||||
"freq NaN NaN NaN NaN 837 \n",
|
||||
"mean -0.004083 0.004662 0.004927 0.589808 NaN \n",
|
||||
"std 0.028557 0.052319 0.027656 0.492074 NaN \n",
|
||||
"freq NaN NaN NaN NaN 840 \n",
|
||||
"mean -0.004099 0.005024 0.004880 0.588676 NaN \n",
|
||||
"std 0.028502 0.052628 0.027606 0.492279 NaN \n",
|
||||
"min -0.092880 -0.196652 -0.117208 0.000000 NaN \n",
|
||||
"25% -0.023517 -0.026200 -0.010013 0.000000 NaN \n",
|
||||
"50% -0.004339 0.004817 0.005710 1.000000 NaN \n",
|
||||
"75% 0.014272 0.031883 0.021406 1.000000 NaN \n",
|
||||
"25% -0.023304 -0.025866 -0.010013 0.000000 NaN \n",
|
||||
"50% -0.004435 0.004950 0.005537 1.000000 NaN \n",
|
||||
"75% 0.014227 0.032486 0.021390 1.000000 NaN \n",
|
||||
"max 0.091322 0.327273 0.129204 1.000000 NaN "
|
||||
]
|
||||
},
|
||||
"execution_count": 5,
|
||||
"execution_count": 6,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
@@ -787,30 +787,30 @@
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"text/plain": [
|
||||
"(PosixPath('/home/jarno/repos/trading-bot/data/training/spy_direction_5d.parquet'),\n",
|
||||
" (1197, 11),\n",
|
||||
" (1201, 11),\n",
|
||||
" date SPY_ret_5 SPY_ret_20 SPY_dist_sma50 VIX_change_5 \\\n",
|
||||
" 0 2021-10-08 0.008336 -0.017017 -0.011155 -0.075239 \n",
|
||||
" 1 2021-10-11 0.014114 -0.026625 -0.018145 -0.090869 \n",
|
||||
" 2 2021-10-12 0.001201 -0.023752 -0.020386 -0.074091 \n",
|
||||
" 3 2021-10-13 0.000644 -0.028356 -0.016597 -0.081006 \n",
|
||||
" 4 2021-10-14 0.008754 -0.010443 -0.000214 -0.096759 \n",
|
||||
" 0 2021-10-11 0.014114 -0.026625 -0.018145 -0.090869 \n",
|
||||
" 1 2021-10-12 0.001201 -0.023752 -0.020386 -0.074091 \n",
|
||||
" 2 2021-10-13 0.000644 -0.028356 -0.016597 -0.081006 \n",
|
||||
" 3 2021-10-14 0.008754 -0.010443 -0.000214 -0.096759 \n",
|
||||
" 4 2021-10-15 0.018294 0.010127 0.007213 -0.079882 \n",
|
||||
" \n",
|
||||
" VIX_rank_20 TLT_ret_10 USO_ret_5 SPY_TLT_ratio_ret spy_up_5d split \n",
|
||||
" 0 0.125 -0.034239 0.041495 0.032998 1.0 train \n",
|
||||
" 1 0.200 -0.033135 0.031015 0.038908 1.0 train \n",
|
||||
" 2 0.100 -0.001041 0.008628 -0.001303 1.0 train \n",
|
||||
" 3 0.050 0.006928 0.036928 -0.005897 1.0 train \n",
|
||||
" 4 0.050 0.010809 0.026192 -0.011991 1.0 train )"
|
||||
" 0 0.20 -0.033135 0.031015 0.038908 1.0 train \n",
|
||||
" 1 0.10 -0.001041 0.008628 -0.001303 1.0 train \n",
|
||||
" 2 0.05 0.006928 0.036928 -0.005897 1.0 train \n",
|
||||
" 3 0.05 0.010809 0.026192 -0.011991 1.0 train \n",
|
||||
" 4 0.05 -0.002202 0.030287 -0.003823 1.0 train )"
|
||||
]
|
||||
},
|
||||
"execution_count": 6,
|
||||
"execution_count": 7,
|
||||
"metadata": {},
|
||||
"output_type": "execute_result"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,336 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "882d4907",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import pandas as pd\n",
|
||||
"import xgboost as xgb\n",
|
||||
"from sklearn.metrics import classification_report, accuracy_score\n",
|
||||
"import matplotlib.pyplot as plt\n",
|
||||
"import numpy as np"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 2,
|
||||
"id": "947722b7",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"\n",
|
||||
"# 1. Load data\n",
|
||||
"df = pd.read_parquet(\"../data/training/spy_direction_5d.parquet\")\n",
|
||||
"\n",
|
||||
"# 2. Define features & target\n",
|
||||
"target_col = \"spy_up_5d\"\n",
|
||||
"feature_cols = [\n",
|
||||
" \"VIX_rank_20\",\n",
|
||||
" \"TLT_ret_10\",\n",
|
||||
" \"USO_ret_5\",\n",
|
||||
" \"SPY_TLT_ratio_ret\",\n",
|
||||
" \"SPY_ret_5\",\n",
|
||||
" \"SPY_ret_20\",\n",
|
||||
" \"SPY_dist_sma50\"\n",
|
||||
"]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "5b02ce5e",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"# 3. Create three distinct splits\n",
|
||||
"train_df = df[df[\"split\"] == \"train\"]\n",
|
||||
"val_df = df[df[\"split\"] == \"validation\"] # Adjust string if named \"val\"\n",
|
||||
"test_df = df[df[\"split\"] == \"test\"]\n",
|
||||
"\n",
|
||||
"X_train, y_train = train_df[feature_cols], train_df[target_col]\n",
|
||||
"X_val, y_val = val_df[feature_cols], val_df[target_col]\n",
|
||||
"X_test, y_test = test_df[feature_cols], test_df[target_col]"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 4,
|
||||
"id": "93a128a7",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Train positive %: 0.5819477434679335\n",
|
||||
"Val positive %: 0.6388888888888888\n",
|
||||
"Test positive %: 0.5769230769230769\n",
|
||||
"\n",
|
||||
"Train Feature Correlations with Target:\n",
|
||||
"VIX_rank_20 -0.004576\n",
|
||||
"TLT_ret_10 0.162370\n",
|
||||
"USO_ret_5 -0.113602\n",
|
||||
"SPY_TLT_ratio_ret -0.111551\n",
|
||||
"SPY_ret_5 -0.001073\n",
|
||||
"SPY_ret_20 0.006523\n",
|
||||
"SPY_dist_sma50 -0.035773\n",
|
||||
"dtype: float64\n",
|
||||
"\n",
|
||||
"Stump Model Best Iteration: 22\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# 1. Check target distribution across splits\n",
|
||||
"print(\"Train positive %:\", y_train.mean())\n",
|
||||
"print(\"Val positive %: \", y_val.mean())\n",
|
||||
"print(\"Test positive %: \", y_test.mean())\n",
|
||||
"\n",
|
||||
"# 2. Check simple linear correlation with target\n",
|
||||
"print(\"\\nTrain Feature Correlations with Target:\")\n",
|
||||
"print(train_df[feature_cols].apply(lambda col: col.corr(y_train)))\n",
|
||||
"\n",
|
||||
"# 3. Test a super simple model (Decision Stump)\n",
|
||||
"stump_model = xgb.XGBClassifier(\n",
|
||||
" n_estimators=100,\n",
|
||||
" max_depth=2, # Decision stumps (1 split per tree, minimal overfitting)\n",
|
||||
" learning_rate=0.01,\n",
|
||||
" early_stopping_rounds=15,\n",
|
||||
" eval_metric=\"logloss\"\n",
|
||||
")\n",
|
||||
"stump_model.fit(\n",
|
||||
" X_train, y_train,\n",
|
||||
" eval_set=[(X_train, y_train), (X_val, y_val)],\n",
|
||||
" verbose=False\n",
|
||||
")\n",
|
||||
"print(f\"\\nStump Model Best Iteration: {stump_model.best_iteration}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 5,
|
||||
"id": "2f82ce9d",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Optimal number of trees (best iteration): 131\n",
|
||||
"Starting Validation Loss: 0.6606\n",
|
||||
"Final Validation Loss: 0.6537\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"\n",
|
||||
"# 4. Initialize XGBoost Classifier with Early Stopping\n",
|
||||
"model = xgb.XGBClassifier(\n",
|
||||
" n_estimators=300, # Set higher; early stopping will truncate training automatically\n",
|
||||
" max_depth=1, # Keep shallow to prevent memorizing noise\n",
|
||||
" learning_rate=0.01,\n",
|
||||
" subsample=0.7,\n",
|
||||
" colsample_bytree=0.7,\n",
|
||||
" early_stopping_rounds=20, # Stop if validation loss stops improving for 15 rounds\n",
|
||||
" eval_metric=\"logloss\",\n",
|
||||
" random_state=42\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"# 5. Fit using Validation set to monitor performance\n",
|
||||
"model.fit(\n",
|
||||
" X_train, y_train,\n",
|
||||
" eval_set=[(X_train, y_train), (X_val, y_val)],\n",
|
||||
" verbose=False\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"print(f\"Optimal number of trees (best iteration): {model.best_iteration}\")\n",
|
||||
"\n",
|
||||
"evals = model.evals_result()\n",
|
||||
"val_loss = evals['validation_1']['logloss']\n",
|
||||
"print(f\"Starting Validation Loss: {val_loss[0]:.4f}\")\n",
|
||||
"print(f\"Final Validation Loss: {val_loss[-1]:.4f}\")\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 6,
|
||||
"id": "2d54aaee",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"\n",
|
||||
"--- Final Holdout Test Performance ---\n",
|
||||
"Test Set Accuracy: 57.69%\n",
|
||||
"\n",
|
||||
"Classification Report:\n",
|
||||
" precision recall f1-score support\n",
|
||||
"\n",
|
||||
" 0.0 0.00 0.00 0.00 77\n",
|
||||
" 1.0 0.58 1.00 0.73 105\n",
|
||||
"\n",
|
||||
" accuracy 0.58 182\n",
|
||||
" macro avg 0.29 0.50 0.37 182\n",
|
||||
"weighted avg 0.33 0.58 0.42 182\n",
|
||||
"\n",
|
||||
"classification_report with custom threshold (0.5819477434679335):\n",
|
||||
" precision recall f1-score support\n",
|
||||
"\n",
|
||||
" 0.0 0.46 0.74 0.57 77\n",
|
||||
" 1.0 0.66 0.37 0.48 105\n",
|
||||
"\n",
|
||||
" accuracy 0.53 182\n",
|
||||
" macro avg 0.56 0.56 0.52 182\n",
|
||||
"weighted avg 0.58 0.53 0.52 182\n",
|
||||
"\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "stderr",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"/home/jarno/repos/trading-bot/.venv/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1879: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
|
||||
" _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
|
||||
"/home/jarno/repos/trading-bot/.venv/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1879: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
|
||||
" _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n",
|
||||
"/home/jarno/repos/trading-bot/.venv/lib/python3.11/site-packages/sklearn/metrics/_classification.py:1879: UndefinedMetricWarning: Precision is ill-defined and being set to 0.0 in labels with no predicted samples. Use `zero_division` parameter to control this behavior.\n",
|
||||
" _warn_prf(average, modifier, f\"{metric.capitalize()} is\", result.shape[0])\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"image/png": "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 truncated
|
||||
"text/plain": [
|
||||
"<Figure size 640x480 with 1 Axes>"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"\n",
|
||||
"# 6. Evaluate strictly on the holdout Test Set\n",
|
||||
"y_pred = model.predict(X_test)\n",
|
||||
"y_proba = model.predict_proba(X_test)[:, 1]\n",
|
||||
"\n",
|
||||
"print(\"\\n--- Final Holdout Test Performance ---\")\n",
|
||||
"print(f\"Test Set Accuracy: {accuracy_score(y_test, y_pred):.2%}\\n\")\n",
|
||||
"print(\"Classification Report:\")\n",
|
||||
"print(classification_report(y_test, y_pred))\n",
|
||||
"threshold = y_train.mean()\n",
|
||||
"y_pred_custom = (y_proba >= threshold).astype(int)\n",
|
||||
"print(f\"classification_report with custom threshold ({threshold}):\")\n",
|
||||
"print(classification_report(y_test, y_pred_custom))\n",
|
||||
"\n",
|
||||
"# 7. Feature Importance\n",
|
||||
"xgb.plot_importance(model, importance_type=\"gain\")\n",
|
||||
"plt.title(\"Feature Importance (Gain)\")\n",
|
||||
"plt.show()"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 7,
|
||||
"id": "1e4b1067",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Min probability: 0.5154\n",
|
||||
"Max probability: 0.6891\n",
|
||||
"Mean probability: 0.5719\n",
|
||||
"Median probability: 0.5641\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"# 1. Inspect raw predicted probabilities for the test set\n",
|
||||
"y_proba_test = model.predict_proba(X_test)[:, 1]\n",
|
||||
"\n",
|
||||
"print(f\"Min probability: {y_proba_test.min():.4f}\")\n",
|
||||
"print(f\"Max probability: {y_proba_test.max():.4f}\")\n",
|
||||
"print(f\"Mean probability: {y_proba_test.mean():.4f}\")\n",
|
||||
"print(f\"Median probability: {np.median(y_proba_test):.4f}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 8,
|
||||
"id": "0f415ee9",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"name": "stdout",
|
||||
"output_type": "stream",
|
||||
"text": [
|
||||
"Successfully saved model to models/spy_xgb_v1.json\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"import json\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"# 1. Create the 'models' directory if it doesn't exist\n",
|
||||
"models_dir = Path(\"models\")\n",
|
||||
"models_dir.mkdir(parents=True, exist_ok=True)\n",
|
||||
"\n",
|
||||
"# 2. Define paths\n",
|
||||
"model_path = models_dir / \"spy_xgb_v1.json\"\n",
|
||||
"meta_path = models_dir / \"spy_xgb_v1_meta.json\"\n",
|
||||
"\n",
|
||||
"# 3. Save XGBoost model (explicitly converted to str)\n",
|
||||
"model.save_model(str(model_path))\n",
|
||||
"\n",
|
||||
"# 4. Save metadata alongside model\n",
|
||||
"metadata = {\n",
|
||||
" \"p_base\": float(y_train.mean()),\n",
|
||||
" \"feature_cols\": feature_cols,\n",
|
||||
" \"last_trained_date\": str(df[\"date\"].iloc[-1]),\n",
|
||||
"}\n",
|
||||
"\n",
|
||||
"with open(meta_path, \"w\") as f:\n",
|
||||
" json.dump(metadata, f, indent=2)\n",
|
||||
"\n",
|
||||
"print(f\"Successfully saved model to {model_path}\")"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "4d73c06a",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": []
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "Python 3",
|
||||
"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.11.15"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in new issue
Block a user