0.0.8
This commit is contained in:
@@ -0,0 +1,329 @@
|
||||
{
|
||||
"cells": [
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Pair Selection History\n",
|
||||
"\n",
|
||||
"Interactive notebook for exploring pair selection history from a SQLite database.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"**Usage**\n",
|
||||
"- Enter the SQLite `db_path` (file path).\n",
|
||||
"- Click `Load pairs` to populate the dropdown.\n",
|
||||
"- Select a `pair_name`, then click `Plot`.\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "668ebf19",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Settings"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 1,
|
||||
"id": "c78db847",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"import sqlite3\n",
|
||||
"from pathlib import Path\n",
|
||||
"\n",
|
||||
"import pandas as pd\n",
|
||||
"import plotly.express as px\n",
|
||||
"import ipywidgets as widgets\n",
|
||||
"from IPython.display import display\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "e7ac6adc",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Data Loading"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": null,
|
||||
"id": "766bcf9f",
|
||||
"metadata": {},
|
||||
"outputs": [
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "a0582ba92c744e08b9267176f463701a",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"VBox(children=(Text(value='', description='pair_db', layout=Layout(width='80%'), placeholder='/path/to/pairs.d…"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
},
|
||||
{
|
||||
"data": {
|
||||
"application/vnd.jupyter.widget-view+json": {
|
||||
"model_id": "eeaaaf780af64a278b0948c0c787cf33",
|
||||
"version_major": 2,
|
||||
"version_minor": 0
|
||||
},
|
||||
"text/plain": [
|
||||
"Output()"
|
||||
]
|
||||
},
|
||||
"metadata": {},
|
||||
"output_type": "display_data"
|
||||
}
|
||||
],
|
||||
"source": [
|
||||
"db_path = widgets.Text(\n",
|
||||
" value='',\n",
|
||||
" placeholder='/path/to/pairs.db',\n",
|
||||
" description='pair_db',\n",
|
||||
" layout=widgets.Layout(width='80%')\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"md_db_path = widgets.Text(\n",
|
||||
" value='',\n",
|
||||
" placeholder='/path/to/market_data.db',\n",
|
||||
" description='md_db',\n",
|
||||
" layout=widgets.Layout(width='80%')\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"load_button = widgets.Button(description='Load pairs', button_style='info')\n",
|
||||
"plot_button = widgets.Button(description='Plot', button_style='primary')\n",
|
||||
"\n",
|
||||
"pair_name = widgets.Dropdown(\n",
|
||||
" options=[],\n",
|
||||
" value=None,\n",
|
||||
" description='pair_name',\n",
|
||||
" layout=widgets.Layout(width='80%')\n",
|
||||
")\n",
|
||||
"\n",
|
||||
"status = widgets.HTML(value='')\n",
|
||||
"output = widgets.Output()\n",
|
||||
"\n",
|
||||
"controls = widgets.VBox([\n",
|
||||
" db_path,\n",
|
||||
" md_db_path,\n",
|
||||
" widgets.HBox([load_button, plot_button]),\n",
|
||||
" pair_name,\n",
|
||||
" status,\n",
|
||||
"])\n",
|
||||
"\n",
|
||||
"display(controls, output)\n"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "markdown",
|
||||
"id": "a4d47855",
|
||||
"metadata": {},
|
||||
"source": [
|
||||
"# Processing"
|
||||
]
|
||||
},
|
||||
{
|
||||
"cell_type": "code",
|
||||
"execution_count": 3,
|
||||
"id": "2c710f51",
|
||||
"metadata": {},
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"PLOT_WIDTH = 1100\n",
|
||||
"PLOT_HEIGHT = 320\n",
|
||||
"\n",
|
||||
"def _connect(path: str):\n",
|
||||
" if not path:\n",
|
||||
" raise ValueError('Please provide db_path.')\n",
|
||||
" p = Path(path).expanduser().resolve()\n",
|
||||
" if not p.exists():\n",
|
||||
" raise FileNotFoundError(f'Database not found: {p}')\n",
|
||||
" return sqlite3.connect(p)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _parse_tstamp(series: pd.Series) -> pd.Series:\n",
|
||||
" return pd.to_datetime(series, utc=True, errors='coerce').dt.tz_convert(None)\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _style_fig(fig, tmin, tmax):\n",
|
||||
" fig.update_layout(\n",
|
||||
" legend=dict(orientation='h', yanchor='bottom', y=1.02, xanchor='left', x=0),\n",
|
||||
" margin=dict(l=50, r=20, t=60, b=40),\n",
|
||||
" height=PLOT_HEIGHT,\n",
|
||||
" width=PLOT_WIDTH,\n",
|
||||
" )\n",
|
||||
" fig.update_xaxes(range=[tmin, tmax])\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _load_pairs(_=None):\n",
|
||||
" status.value = ''\n",
|
||||
" with output:\n",
|
||||
" output.clear_output()\n",
|
||||
" try:\n",
|
||||
" with _connect(db_path.value) as conn:\n",
|
||||
" rows = conn.execute(\n",
|
||||
" \"WITH first_rows AS (\"\n",
|
||||
" \" SELECT pair_name, MIN(tstamp) AS tmin \"\n",
|
||||
" \" FROM pair_selection_history \"\n",
|
||||
" \" GROUP BY pair_name\"\n",
|
||||
" \" ) \"\n",
|
||||
" \"SELECT p.pair_name \"\n",
|
||||
" \"FROM pair_selection_history p \"\n",
|
||||
" \"JOIN first_rows f \"\n",
|
||||
" \" ON p.pair_name = f.pair_name AND p.tstamp = f.tmin \"\n",
|
||||
" \"GROUP BY p.pair_name \"\n",
|
||||
" \"ORDER BY MIN(p.composite_rank), p.pair_name\"\n",
|
||||
" ).fetchall()\n",
|
||||
" options = [r[0] for r in rows]\n",
|
||||
" pair_name.options = options\n",
|
||||
" pair_name.value = options[0] if options else None\n",
|
||||
" status.value = f'Loaded {len(options)} pairs.'\n",
|
||||
" except Exception as exc:\n",
|
||||
" status.value = f\"<span style='color:#b00'>Error: {exc}</span>\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"def _plot(_=None):\n",
|
||||
" status.value = ''\n",
|
||||
" with output:\n",
|
||||
" output.clear_output()\n",
|
||||
" try:\n",
|
||||
" if not pair_name.value:\n",
|
||||
" raise ValueError('Please select a pair_name.')\n",
|
||||
" if not md_db_path.value:\n",
|
||||
" raise ValueError('Please provide md_db path.')\n",
|
||||
" query = (\n",
|
||||
" 'SELECT tstamp, pvalue_eg, pvalue_adf, rank_eg, rank_adf, '\n",
|
||||
" 'exchange_a, instrument_a, exchange_b, instrument_b '\n",
|
||||
" 'FROM pair_selection_history '\n",
|
||||
" 'WHERE pair_name = ? '\n",
|
||||
" 'ORDER BY tstamp'\n",
|
||||
" )\n",
|
||||
" with _connect(db_path.value) as conn:\n",
|
||||
" df = pd.read_sql_query(query, conn, params=(pair_name.value,))\n",
|
||||
" if df.empty:\n",
|
||||
" raise ValueError('No data for selected pair_name.')\n",
|
||||
" df['tstamp'] = _parse_tstamp(df['tstamp'])\n",
|
||||
" df = df.dropna(subset=['tstamp'])\n",
|
||||
" if df.empty:\n",
|
||||
" raise ValueError('No valid timestamps in pair selection data.')\n",
|
||||
" tmin = df['tstamp'].min()\n",
|
||||
" tmax = df['tstamp'].max()\n",
|
||||
"\n",
|
||||
" first_row = df.dropna(subset=['exchange_a', 'instrument_a', 'exchange_b', 'instrument_b']).iloc[0]\n",
|
||||
" ex_a = first_row['exchange_a']\n",
|
||||
" id_a = first_row['instrument_a']\n",
|
||||
" ex_b = first_row['exchange_b']\n",
|
||||
" id_b = first_row['instrument_b']\n",
|
||||
"\n",
|
||||
" fig_p = px.line(\n",
|
||||
" df,\n",
|
||||
" x='tstamp',\n",
|
||||
" y=['pvalue_eg', 'pvalue_adf'],\n",
|
||||
" title=f'P-Values Over Time: {pair_name.value}',\n",
|
||||
" labels={'value': 'p-value', 'variable': 'metric', 'tstamp': 'timestamp'}\n",
|
||||
" )\n",
|
||||
" fig_p.update_layout(legend_title_text='metric')\n",
|
||||
" _style_fig(fig_p, tmin, tmax)\n",
|
||||
"\n",
|
||||
" # fig_r = px.line(\n",
|
||||
" # df,\n",
|
||||
" # x='tstamp',\n",
|
||||
" # y=['rank_eg', 'rank_adf'],\n",
|
||||
" # title=f'Ranks Over Time: {pair_name.value}',\n",
|
||||
" # labels={'value': 'rank', 'variable': 'metric', 'tstamp': 'timestamp'}\n",
|
||||
" # )\n",
|
||||
" # fig_r.update_layout(legend_title_text='metric')\n",
|
||||
" # _style_fig(fig_r, tmin, tmax)\n",
|
||||
"\n",
|
||||
" md_query = (\n",
|
||||
" 'SELECT tstamp, close FROM md_1min_bars '\n",
|
||||
" 'WHERE exchange_id = ? AND instrument_id = ? '\n",
|
||||
" 'ORDER BY tstamp'\n",
|
||||
" )\n",
|
||||
" with _connect(md_db_path.value) as md_conn:\n",
|
||||
" md_a = pd.read_sql_query(md_query, md_conn, params=(ex_a, id_a))\n",
|
||||
" md_b = pd.read_sql_query(md_query, md_conn, params=(ex_b, id_b))\n",
|
||||
" if md_a.empty or md_b.empty:\n",
|
||||
" raise ValueError('Market data not found for selected instruments.')\n",
|
||||
" md_a['tstamp'] = _parse_tstamp(md_a['tstamp'])\n",
|
||||
" md_b['tstamp'] = _parse_tstamp(md_b['tstamp'])\n",
|
||||
" md_a = md_a.dropna(subset=['tstamp', 'close'])\n",
|
||||
" md_b = md_b.dropna(subset=['tstamp', 'close'])\n",
|
||||
" md_a = md_a[(md_a['tstamp'] >= tmin) & (md_a['tstamp'] <= tmax)]\n",
|
||||
" md_b = md_b[(md_b['tstamp'] >= tmin) & (md_b['tstamp'] <= tmax)]\n",
|
||||
" if md_a.empty or md_b.empty:\n",
|
||||
" raise ValueError('Market data is outside the pair selection time range.')\n",
|
||||
" md_a = md_a.sort_values('tstamp')\n",
|
||||
" md_b = md_b.sort_values('tstamp')\n",
|
||||
" md_a['scaled_close'] = (md_a['close'] - md_a['close'].iloc[0]) / md_a['close'].iloc[0] * 100\n",
|
||||
" md_b['scaled_close'] = (md_b['close'] - md_b['close'].iloc[0]) / md_b['close'].iloc[0] * 100\n",
|
||||
"\n",
|
||||
" md_plot = pd.DataFrame({\n",
|
||||
" 'tstamp': md_a['tstamp'],\n",
|
||||
" f'{ex_a}:{id_a}': md_a['scaled_close'],\n",
|
||||
" })\n",
|
||||
" md_plot = md_plot.merge(\n",
|
||||
" pd.DataFrame({\n",
|
||||
" 'tstamp': md_b['tstamp'],\n",
|
||||
" f'{ex_b}:{id_b}': md_b['scaled_close'],\n",
|
||||
" }),\n",
|
||||
" on='tstamp',\n",
|
||||
" how='outer'\n",
|
||||
" ).sort_values('tstamp')\n",
|
||||
"\n",
|
||||
" fig_m = px.line(\n",
|
||||
" md_plot,\n",
|
||||
" x='tstamp',\n",
|
||||
" y=[f'{ex_a}:{id_a}', f'{ex_b}:{id_b}'],\n",
|
||||
" title='Scaled Close Price Change (%)',\n",
|
||||
" labels={'value': 'scaled % change', 'variable': 'instrument', 'tstamp': 'timestamp'}\n",
|
||||
" )\n",
|
||||
" fig_m.update_layout(legend_title_text='instrument')\n",
|
||||
" _style_fig(fig_m, tmin, tmax)\n",
|
||||
"\n",
|
||||
" with output:\n",
|
||||
" display(fig_p)\n",
|
||||
" # display(fig_r)\n",
|
||||
" display(fig_m)\n",
|
||||
" except Exception as exc:\n",
|
||||
" status.value = f\"<span style='color:#b00'>Error: {exc}</span>\"\n",
|
||||
"\n",
|
||||
"\n",
|
||||
"load_button.on_click(_load_pairs)\n",
|
||||
"plot_button.on_click(_plot)\n"
|
||||
]
|
||||
}
|
||||
],
|
||||
"metadata": {
|
||||
"kernelspec": {
|
||||
"display_name": "python3.12-venv",
|
||||
"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.9"
|
||||
}
|
||||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 5
|
||||
}
|
||||
Reference in New Issue
Block a user