Files
NewQuant/data/ analysis/Trend.ipynb

882 lines
823 KiB
Plaintext
Raw Normal View History

2025-09-16 09:59:38 +08:00
{
"cells": [
{
"metadata": {},
"cell_type": "raw",
"source": "# Please replace 'your_futures_data.csv' with the actual path to your CSV file",
"id": "fb1975346060eb6d"
},
{
"cell_type": "code",
"id": "initial_id",
"metadata": {
"collapsed": true,
"ExecuteTime": {
2025-09-24 23:14:14 +08:00
"end_time": "2025-09-21T14:34:34.700968Z",
"start_time": "2025-09-21T14:34:34.697756Z"
2025-09-16 09:59:38 +08:00
}
},
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"import talib as ta # Make sure TA-Lib is installed: pip install TA-Lib\n",
"import statsmodels.api as sm\n",
"\n",
"import warnings\n",
"\n",
"# 忽略所有警告\n",
"warnings.filterwarnings(\"ignore\")\n",
"\n",
"# --- 0. Configure your file path ---\n",
"# Please replace 'your_futures_data.csv' with the actual path to your CSV file\n",
"file_path = '/mnt/d/PyProject/NewQuant/data/data/KQ_m@CZCE_SA/KQ_m@CZCE_SA_min15.csv'\n",
"\n",
"sns.set(style='whitegrid')\n",
"plt.rcParams['font.sans-serif'] = ['SimHei'] # 用来正常显示中文标签\n",
"plt.rcParams['axes.unicode_minus'] = False # 用来正常显示负号\n"
],
"outputs": [],
2025-09-24 23:14:14 +08:00
"execution_count": 11
2025-09-16 09:59:38 +08:00
},
{
"metadata": {
"ExecuteTime": {
2025-09-24 23:14:14 +08:00
"end_time": "2025-09-21T14:34:34.784945Z",
"start_time": "2025-09-21T14:34:34.736929Z"
2025-09-16 09:59:38 +08:00
}
},
"cell_type": "code",
"source": [
"\n",
"# --- 1. Data Loading and Preprocessing ---\n",
"def load_and_preprocess_data(file_path):\n",
" \"\"\"\n",
" Loads historical futures data and performs basic preprocessing.\n",
" Assumes data contains 'datetime', 'open', 'high', 'low', 'close', 'volume' columns.\n",
" \"\"\"\n",
" try:\n",
" df = pd.read_csv(file_path, parse_dates=['datetime'], index_col='datetime')\n",
" # Ensure data is sorted by time\n",
" df = df.sort_index()\n",
" # Check and handle missing values\n",
" initial_rows = len(df)\n",
" df.dropna(inplace=True)\n",
" if len(df) < initial_rows:\n",
" print(f\"Warning: Missing values found in data, deleted {initial_rows - len(df)} rows.\")\n",
"\n",
" # Check if necessary columns exist\n",
" required_columns = ['open', 'high', 'low', 'close', 'volume']\n",
" if not all(col in df.columns for col in required_columns):\n",
" raise ValueError(f\"CSV file is missing required columns. Please ensure it contains: {required_columns}\")\n",
"\n",
" print(f\"Successfully loaded {len(df)} rows of data.\")\n",
" print(\"First 5 rows of data:\")\n",
" print(df.head())\n",
" return df\n",
" except FileNotFoundError:\n",
" print(f\"Error: File '{file_path}' not found. Please check the path.\")\n",
" return None\n",
" except Exception as e:\n",
" print(f\"Error during data loading or preprocessing: {e}\")\n",
" return None\n",
"\n",
"\n",
"df_raw = load_and_preprocess_data(file_path)\n",
2025-09-20 00:04:51 +08:00
"df_raw = df_raw[df_raw.index >= '2024-01-01']"
2025-09-16 09:59:38 +08:00
],
"id": "1638e05ca7ef1ac8",
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Successfully loaded 25662 rows of data.\n",
"First 5 rows of data:\n",
" open high low close volume open_oi \\\n",
"datetime \n",
"2020-12-31 14:45:00 1607.0 1611.0 1603.0 1611.0 19480.0 148833.0 \n",
"2021-01-04 09:00:00 1610.0 1636.0 1601.0 1620.0 55486.0 146448.0 \n",
"2021-01-04 09:15:00 1620.0 1620.0 1601.0 1604.0 30314.0 153373.0 \n",
"2021-01-04 09:30:00 1604.0 1606.0 1590.0 1595.0 30803.0 157091.0 \n",
"2021-01-04 09:45:00 1595.0 1601.0 1594.0 1600.0 10031.0 158730.0 \n",
"\n",
" close_oi underlying_symbol \n",
"datetime \n",
"2020-12-31 14:45:00 146448.0 CZCE.SA105 \n",
"2021-01-04 09:00:00 153373.0 CZCE.SA105 \n",
"2021-01-04 09:15:00 157091.0 CZCE.SA105 \n",
"2021-01-04 09:30:00 158730.0 CZCE.SA105 \n",
"2021-01-04 09:45:00 160031.0 CZCE.SA105 \n"
]
}
],
2025-09-24 23:14:14 +08:00
"execution_count": 12
2025-09-16 09:59:38 +08:00
},
{
"metadata": {
"ExecuteTime": {
2025-09-24 23:14:14 +08:00
"end_time": "2025-09-21T14:34:36.540760Z",
"start_time": "2025-09-21T14:34:34.804153Z"
2025-09-20 00:04:51 +08:00
}
},
"cell_type": "code",
"source": [
"import numpy as np\n",
2025-09-24 23:14:14 +08:00
"import pandas as pd\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"from pykalman import KalmanFilter\n",
"from statsmodels.tsa.stattools import acf\n",
"\n",
"# 1. 典型价格 + 收益率\n",
"df = df_raw.copy()\n",
"df['tp'] = (df['high'] + df['low'] + df['close']) / 3\n",
"df['ret'] = df['tp'].pct_change()\n",
"\n",
"# 2. 增量 Kalman价格斜率0 状态)\n",
"def single_pass_kalman(df, tcov=1e-4, ocov=1.):\n",
" kf = KalmanFilter(\n",
" transition_matrices=[[1, 1], [0, 1]],\n",
" observation_matrices=[[1, 0]],\n",
" transition_covariance=tcov * np.eye(2),\n",
" observation_covariance=ocov\n",
" )\n",
" n = len(df)\n",
" states = np.zeros((n, 2))\n",
" P = np.ones((2, 2))\n",
" states[0] = [df['tp'].iloc[0], 0.]\n",
" for i in range(1, n):\n",
" states[i], P = kf.filter_update(states[i-1], P, observation=df['tp'].iloc[i])\n",
" df[['trend', 'slope']] = states\n",
2025-09-20 00:04:51 +08:00
" return df\n",
"\n",
2025-09-24 23:14:14 +08:00
"df = single_pass_kalman(df)\n",
2025-09-20 00:04:51 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"# 3. 滚动趋势标签(持续 15 根 + 斜率阈值)\n",
"def roll_trend_label(df, persist=5, slope_thres=2e-4):\n",
" up_cnt = (df['slope'] > slope_thres).astype(int).rolling(persist).sum()\n",
" dn_cnt = (df['slope'] < -slope_thres).astype(int).rolling(persist).sum()\n",
" df['trend_label'] = np.where(up_cnt >= persist, 1,\n",
" np.where(dn_cnt >= persist, -1, 0))\n",
" return df\n",
2025-09-20 00:04:51 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"df = roll_trend_label(df)\n",
"\n",
"# 4. 逐段 ACF收益率序列lag 0-15\n",
"def segment_acf_ret(df, label, max_lag=5):\n",
" seg = df[df['trend_label'] == label]\n",
" if len(seg) < max_lag + 5:\n",
" return None, None\n",
" series = seg['ret'].dropna()\n",
" acf_val, confint = acf(series, nlags=max_lag, alpha=0.05)\n",
" return acf_val, confint\n",
"\n",
"# 5. 全段统计(平均 ACF + 段数)\n",
"def all_segments_stats(df, max_lag=5):\n",
" labels = {1: 'Up-Trend', -1: 'Down-Trend', 0: 'Sideways'}\n",
" stats = []\n",
" for lbl, name in labels.items():\n",
" seg_list = []\n",
" seg_id = (df['trend_label'] != lbl).cumsum()\n",
" for _, g in df[df['trend_label'] == lbl].groupby(seg_id):\n",
" if len(g) < max_lag + 5:\n",
" continue\n",
" series = g['ret'].dropna()\n",
" acf_val = acf(series, nlags=max_lag, alpha=None)\n",
" seg_list.append(acf_val)\n",
" if seg_list:\n",
" acf_mean = np.mean(seg_list, axis=0)\n",
" stats.append([name, acf_mean[1], len(seg_list)])\n",
" return pd.DataFrame(stats, columns=['Segment', 'Mean_lag1_ACF', 'Segment_Count'])\n",
"\n",
"stats_df = all_segments_stats(df)\n",
"\n",
"# 6. 可视化\n",
"# 6.1 平均 ACF收益率\n",
"plt.figure(figsize=(10, 4))\n",
"lags = np.arange(16)\n",
"for _, row in stats_df.iterrows():\n",
" acf_mean = [1] + [row['Mean_lag1_ACF']] * 15 # 简化:只画 lag-1 均值\n",
" plt.plot(lags, acf_mean, marker='o', label=f\"{row['Segment']} (n={row['Segment_Count']})\")\n",
"plt.axhline(0.05, color='red', ls=':', alpha=0.7, label='5% sig')\n",
"plt.axhline(0, color='gray', ls='--')\n",
"plt.title('Mean ACF (Returns) by Trend Segment')\n",
"plt.xlabel('Lag'); plt.ylabel('ACF ρ')\n",
"plt.legend(); plt.grid(alpha=0.3); plt.show()\n",
"\n",
"# 6.2 滚动 lag-1 ACF收益率\n",
"roll_df = []\n",
"for lbl, name in {1: 'Up', -1: 'Down', 0: 'Side'}.items():\n",
" seg_id = (df['trend_label'] != lbl).cumsum()\n",
" for _, g in df[df['trend_label'] == lbl].groupby(seg_id):\n",
" if len(g) < 20:\n",
" continue\n",
" acf_val = acf(g['ret'], nlags=1, alpha=None)[1]\n",
" roll_df.append([g.index[-1], name, acf_val])\n",
"roll_df = pd.DataFrame(roll_df, columns=['end_time', 'label', 'lag1_acf'])\n",
"roll_df = roll_df.set_index('end_time')\n",
"\n",
"plt.figure(figsize=(14, 3))\n",
"for lab in ['Up', 'Down', 'Side']:\n",
" sub = roll_df[roll_df['label'] == lab]\n",
" plt.plot(sub.index, sub['lag1_acf'], marker='o', markersize=2, label=lab, alpha=0.8)\n",
"plt.axhline(0.20, color='red', ls='--', alpha=0.7, label='强惯性阈值')\n",
"plt.title('滚动 lag-1 ACF收益率全历史200根窗')\n",
"plt.ylabel('ρ'); plt.legend(); plt.grid(alpha=0.3); plt.show()\n",
"\n",
"# 7. 结论打印\n",
"print('\\n=== 收益率自相关结论(策略级) ===')\n",
"for _, row in stats_df.iterrows():\n",
" sig = abs(row['Mean_lag1_ACF']) > 0.05\n",
" print(f\"{row['Segment']:12s} | 平均 lag-1 ρ={row['Mean_lag1_ACF']:+.3f} | 显著={sig} | 段数={row['Segment_Count']}\")"
2025-09-20 00:04:51 +08:00
],
2025-09-24 23:14:14 +08:00
"id": "5e9912a53438b337",
2025-09-20 00:04:51 +08:00
"outputs": [
{
2025-09-24 23:14:14 +08:00
"data": {
"text/plain": [
"<Figure size 1000x400 with 1 Axes>"
],
"image/png": "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+Hhmz57NU089xcKFCx2TZwwcOJDly5fz+uuvA/ZDupYvX57rfDBXxMfHc++99+Lp6cmoUaNy5Sa/92Pjxo2888473H///cydO5emTZvy+OOPk5mZ6bSOKlWqcPbsWbdjyzFz5kx69erF7NmzsVgsPPbYY47n6NevH+vWrcNisQD2GRK/++47tw6RdFV+uRo3bhzr16/nlVde4e233yYqKirX+/Hpp5+yadMmJk2axMMPP8zixYv5v//7PwC2b9/Oiy++SPfu3ZkzZw6pqaluj+I9++yz9OvXjyVLltCjRw/efvtt0tPTHY8fPnyYhx56iGbNmvHxxx9zyy238MwzzzgVOi+//DImk4m5c+fSoEEDPv30U2bOnOmYbOXrr7/m/vvvp1WrVrz00ktce+21DB48mE8//RS48nYA7MXaqFGjuOuuu3j33Xc5deoUb731FgAjR450fJarVKni+GwXxQ8dIlJy6RwpESlzGjduzJIlSwgODqZx48ZOj82aNYsbb7yRJ554AoC2bdvy3XffsXr1akaPHk16ejrPPvssPXv2pGLFimRkZDB//nx27NjhtNNbo0YNpkyZAthHWlavXs3hw4dznWOUl59//pnWrVvj7e1Nx44dmT9/PjExMdSsWdOx831pAXg5CxcuZOHChY77gwcP5tVXX3Xp+S9lNpudRhwutWTJEscEDGazmf79+/PAAw8AsHr1ak6ePMnSpUupVKkSYJ/cY9WqVTz55JNUq1aNatWqkZqaCnBV53P98MMPvPTSS47n/qf83o8TJ07g5eXFwIED8ff355prruGmm25yFDU5LBaL00Qb7rr11lsdMx7Wrl2bf/3rX3z33Xfcdttt9O3bl/fee48tW7bQtWtXtmzZQkpKCrfffnuBny8veeUqZ9KPmTNnctNNNwGQnZ3NiBEjOH78OLVr1wbg/PnzrF+/noCAALp3787XX3/Nvn37uPHGG1m4cCGNGjXitddeA+zfH3dnivT19WXq1Kncf//9vPfee3z44YdERkaycOFCfHx8mDdvHuHh4Y4RvPbt27N582a++uorrrvuOgB27NjBjBkz6NChA5UqVeKrr76iWrVqVK1aFbBPxNK7d28OHDhATEwMI0aM4Ndff+Wjjz4CrrwdyMnXzJkzufnmmwH76GnONPlhYWGEhYURHR2Nt7d3oZ2rKCKliwopESlzGjduzIEDBwgKCqJr165OF1yNiooiPj6eJk2aOC1z9OhRACpXrkyHDh1YtGgRkZGR/PXXX2RkZOQqyO677z7H3znFU84hbVeSnZ3N1q1bSU5Odorj559/ZsCAAY5Z2FJSUpyWGzduHKGhoY6dv9tvv52HH344Vxzuym8kpnfv3gwfPpwvvviCr776iueeew4vLy8Ax3lonTt3zrVcZmZmgWcQtFqtuQqaRo0a5TvNd37vx80338y8efO4/fbb6dy5MxEREdxyyy2O2Q9znDt37oojc/lp37694+/69esTHBzMkSNHAPuOd9u2bVm9ejVdu3Zl7dq1XH/99QV+z/KTV65yDhm93Ijg0aNHHYVU//79nWYCrFixoiOXR44ccSoavL29C3wO4rXXXsu8efNYunQpr7/+OkuWLOGhhx5yjCb/8zvq4+Pj+Lt+/fr873//o0OHDvzwww9UqFCBypUrOx7PKah
},
"metadata": {},
"output_type": "display_data"
2025-09-20 00:04:51 +08:00
},
{
"data": {
"text/plain": [
2025-09-24 23:14:14 +08:00
"<Figure size 1400x300 with 1 Axes>"
2025-09-20 00:04:51 +08:00
],
2025-09-24 23:14:14 +08:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAABJAAAAEqCAYAAABKuaqdAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzsnXd4HNXZvu+Z7aveLVnuveCGsY0x1XQChBpCCQTIl4QQ0sMPyEcIKfAFAiEJhNBC78UQmsFAwNjGFfcuF1mWrF52tX1mfn/MzqhLu9JKWtvnvi5foC2zZ3d2Z+Y853mfV9I0TUMgEAgEAoFAIBAIBAKBQCDoAnmwByAQCAQCgUAgEAgEAoFAIEhuhIAkEAgEAoFAIBAIBAKBQCDoFiEgCQQCgUAgEAgEAoFAIBAIukUISAKBQCAQCAQCgUAgEAgEgm4RApJAIBAIBAKBQCAQCAQCgaBbhIAkEAgEAoFAIBAIBAKBQCDoFiEgCQQCgUAgEAgEAoFAIBAIukUISAKBQCAQCAQCgUAgEAgEgm4RApJAIBAIBAJBlEAgQElJCaqqmrft2bOHAwcOxLWdcDjc4bZQKNTn8QkEAoFAIBAMFkJAEggEAsFRhaZpNDQ0dHn/X//6V2655ZY+v87//d//8fOf/7zP2xEknhUrVhCJRDq9b8eOHZx77rlt7n/yySc599xzqauri2n7L7zwAhdeeGEHwegPf/gDl156KcFgMOaxBgIB/vGPf7Bx48aYHr9u3Tq8Xm/M248FRVG4/PLLue2221AUJaHbVlWVQCDQRrDrihUrVnDNNdewadOmhI7B2HZX34n+5uuvv+bFF19M2PZKSkq46KKLKC8vj+nx27dvp6qqKmGvLxAIBIIjFyEgCQQCgeCo4oMPPuAb3/gGa9euBaC5uZlAIICmaQDMmjWLTz/9FJ/PZz4nFArh9/vNvwOBQBuHiaqq+P3+NreFQiHq6+v7++0I4uTNN9/klltuYe/evZ3e73A4ALDb7YC+b7/44gvOP/98srOzY3qNE088kf379/Pqq6+at4VCIT744APmz59vvkZ7fD5fB+eS0+lk0aJFvPLKK21u1zStw/cS4Pnnn+eGG26gqakpprHGwnPPPcfmzZv59NNP+frrr3t8fF1dHRMmTIjp36RJk5g+fXpMDi9Jkli1apW5bxJFT9+J/mb16tU8//zz5t+vvvpql5/XZ599Zj5O0zSampo6/EtNTWX37t189tlnnd7fXmB87733uOaaa6ioqBiw9ywQCASCwxPrYA9AIBAIBIKB5Oyzz2b9+vVce+21/P73v+edd95h/fr1bSalKSkpnHrqqebfkUiEM888k3vuuQeAH/3oR3z55Zcdtv3oo4+az5NlGbfb3efxnnbaadx8881cfPHFfd5Wb/nyyy+56667WLJkSa+eX1dXx/z587n55pu5+eabO9z/2Wef8Ze//IX9+/czZcoU7rrrLiZOnAjo7//gwYMdnjN06FA+/fTTuMaxdOlS7r//fp555hnGjRtn3v78888TCAS48cYbkeW2a2tffvklVVVVfOc732lzezgcJhwO43K5kCSJYDCIJElYLBaGDRvGddddR0ZGhvn4Tz/9FEVRuP7669E0DVVVCYVC2O12LBYLAN///vdZs2YNbrcbq1W/RFMUBYvFwpIlS9p8/qFQiEAgwIUXXsi9995r3v7nP/+Z22+/nZtvvpmnn366w/uJl02bNvHQQw/x/e9/n+zsbH7+85/z2muvUVBQ0OVz0tLSAPjjH//IvHnzzNt9Ph9vvvkmJ598MsOGDWvzWRYWFrbZRl1dXRsRFzAFWY/HQ1lZWZv7cnNzcTqdcb+/rr4TkUiEv/zlLyxatAiPx8Po0aO5++67mTFjhvmY7du387vf/Y7t27czffp07rnnnjbvo76+nrvvvpsvvviCIUOGcOeddzJ37twOY7Db7W3GbrVayc/P57333mvzuDPOOKON+Njc3Mxxxx3X5Xu7++67ufvuuzvcnp+fz9KlS82/f/GLXyBJEjfccANvvPEGLpery20KBAKB4OhGCEgCgUAgOKqQZZnbb7+d4uJiZs2axUUXXQTAfffdx1lnncW0adMAKC0t5Y477uBXv/qVeZvBn/70JyKRSBuXSigUQtM0NmzYgN1up66ujubmZrZt22Y+b/jw4aSkpAzQO00Mu3fv5he/+EWfxv3VV1+haRrLly/vICCtWbOGm2++meuuu44FCxbw3HPPceONN/Lxxx+bE9lvfOMbXHfddW2eF68Lpa6ujttuu43/+7//Y/LkyW3u27hxY5flS6+99hoAF154Yaf3f/LJJxQXF3PHHXfwn//8p8P9v/zlL9v83V5AePbZZ83bnnzySRoaGsjPzzfv//GPf4zNZuP+++8HMAUhVVVZv349xxxzTJvtWa1W/vjHP3LllVfyxBNP8D//8z+djjsWNm7cyPXXX8/s2bO5+eabkSSJL774gssvv5x//etfpsjXHpvNBuiiTnFxsXl7dXU1//73vxkxYgTHH398t69977338vbbb3d631VXXdXhtqeeeooTTjgh1rcGdP+d+Pvf/86LL77Itddey8iRI/n3v//N97//fZYsWUJaWhq1tbVcd911TJgwgb/97W+8//77/M///A9vvfWWKf79+Mc/pqysjHvvvZcDBw7wgx/8gHfeeccUz/x+vykegi6kqaqKzWZDURT279/fZkyKoiBJkvm38Rt4/PHHOemkkzhw4AAvvfQSP/3pT8373nrrLfbt28cll1zC8OHDWbJkCX/60586fBY///nP2bNnD/fcc0+nopNAIBAIBCAEJIFAIBAcZRiuD8NRomkaf/3rX3n99ddZsGABDz74IN/85jcZOnQo8+bN45prruHGG2/kpptuMid7XbkvXnvtNX7zm9+0ue2b3/ym+f+vvvoq06dP75831g9s3LiRG2+8keHDh8ec/9MZy5Ytw2azsWHDBrxeL6mpqeZ9DzzwACeccAK/+tWvAJgyZQoLFizgrbfe4sorrwQgOzu7g1ASL88//zwLFizgxBNP7HCfzWbr1Kmzd+9elixZwgMPPMCcOXMAWL58Ob///e959913CQaD5nfh1ltvNSfuFouFSCSC1WptM+E3MBxIgUCAvLw88/bFixdz5513cuedd3LRRRexaNEitmzZwuOPP055eTnXX389v/vd75g1axa33347n3/+OW+99VYbN4/xfu69916uuOIKrr766l454d555x3uuusupkyZwl//+lfzu//Xv/6VH//4x3zrW9/iuuuu43vf+16b/dma9u/dyDkaMWJEj69vt9uZNWsWL730knnbmjVruOqqq0zRDqCsrIyFCxf2qqytq+9EU1MTzzzzDPfddx9nnnkmADNmzOCcc85h5cqVnH766TzzzDNIksQ///lP3G438+fP54wzzuDjjz/mnHPO4csvv2T16tW89tprpgC9fft2nnrqKX77298CcNlll7Fr1y7zdadOncrChQs577zzaGpq6iD0+P3+Nt/TcDjMlClT2L59Ox988AEfffQRc+fOZcWKFbjdbo499licTqcpIE2cOJFZs2Z1KYb+4Q9/YOHChfzgBz+gqKgo7s9TIBAIBEc+IgNJIBAIBEcNdXV1nHbaaTz22GOoqkp9fT033HAD7733Hi+99BI7d+7khRdeoKmpCVmWuf7663nuued46aWX+N3vfgfoAlRnbpVIJMLChQtZtWoV69at47LLLuOcc85h3bp1ZrlI65Kmw4HVq1dz6623mkJOb1m+fDlXX301qqqycuVK8/a6ujrWrVvHOeecY96Wnp7OiBEj2kysE8Hrr7/Ot7/97bie8+ijj5KWlsbKlSvJzc0lLy+PUChEYWEhQ4YMYcSIEabbJi8vj+LiYvLz80lJSeGOO+7ggQceIDs7m+zsbPbt28d3vvMdli5dSkZGBnl5eQwbNqxN6dL555/Pb3/7W958802efvppbrvtNubOncv777/PDTfcwMSJE5kxYwb/+Mc/2Lx5M2+88UYH8chgzJgxTJo0icWLF8f1njdu3MgNN9zAr37
2025-09-20 00:04:51 +08:00
},
"metadata": {},
"output_type": "display_data"
2025-09-24 23:14:14 +08:00
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"=== 收益率自相关结论(策略级) ===\n",
"Up-Trend | 平均 lag-1 ρ=+0.211 | 显著=True | 段数=105\n",
"Down-Trend | 平均 lag-1 ρ=+0.165 | 显著=True | 段数=112\n",
"Sideways | 平均 lag-1 ρ=+0.169 | 显著=True | 段数=2\n"
]
2025-09-20 00:04:51 +08:00
}
],
2025-09-24 23:14:14 +08:00
"execution_count": 13
2025-09-20 00:04:51 +08:00
},
{
"metadata": {
"ExecuteTime": {
2025-09-24 23:14:14 +08:00
"end_time": "2025-09-21T14:34:36.931817Z",
"start_time": "2025-09-21T14:34:36.564989Z"
2025-09-20 00:04:51 +08:00
}
},
"cell_type": "code",
2025-09-24 23:14:14 +08:00
"source": [
"# 1. 导入库并准备数据 (继续使用上一轮的模拟数据)\n",
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"\n",
"# 设置绘图风格\n",
"sns.set_style('whitegrid')\n",
"\n",
"# 解决matplotlib中文显示问题\n",
"plt.rcParams['font.sans-serif'] = ['SimHei']\n",
"plt.rcParams['axes.unicode_minus'] = False\n",
"\n",
"# --- 2. 分析问题一:价格 vs 长期均线 ---\n",
"print(\"\\n--- 问题一分析:价格与长期均线的相关性 ---\")\n",
"# 计算12周期的移动平均线\n",
"ma_period = 12\n",
"df_raw['ma_12'] = df_raw['close'].rolling(window=ma_period).mean()\n",
"\n",
"# 丢弃前面没有均线值的NaN行以便计算相关性\n",
"df_analysis = df_raw.dropna()\n",
"\n",
"# 计算相关系数\n",
"correlation_price_ma = df_analysis['close'].corr(df_analysis['ma_12'])\n",
"print(f\"收盘价 'close' 与 12周期均线 'ma_12' 之间的皮尔逊相关系数为: {correlation_price_ma:.4f}\")\n",
"\n",
"# 可视化\n",
"plt.figure(figsize=(14, 7))\n",
"plt.plot(df_analysis.index, df_analysis['close'], label='收盘价 (Close)', alpha=0.8)\n",
"plt.plot(df_analysis.index, df_analysis['ma_12'], label=f'{ma_period}周期均线 (MA)', color='red', linestyle='--')\n",
"plt.title('收盘价与长期移动平均线的关系', fontsize=16)\n",
"plt.xlabel('日期')\n",
"plt.ylabel('价格')\n",
"plt.legend()\n",
"plt.show()\n",
"\n",
"print(\"分析:正如预期,均线紧随价格之后,它们的相关性极高。\")\n",
"print(\"\\n\" + \"=\"*80 + \"\\n\")\n",
"\n",
"\n",
"# --- 3. 分析问题二:价差 vs 下一期收益率 ---\n",
"print(\"--- 问题二分析:价差与下一期收益率的相关性 ---\")\n",
"# 步骤A: 计算价差 (Spread)\n",
"# 为了方便比较,我们使用归一化价差(价差 / 均线),也可以直接用价差\n",
"df_analysis['spread'] = df_analysis['close'] - df_analysis['ma_12']\n",
"# df_analysis['spread_normalized'] = (df_analysis['close'] - df_analysis['ma_12']) / df_analysis['ma_12']\n",
"\n",
"# 步骤B: 计算'未来'收益率\n",
"# 我们需要用 t+1 期的收益率来匹配 t 期的价差\n",
"# df['close'].pct_change() 计算的是当期收益率 (P_t - P_{t-1}) / P_{t-1}\n",
"# 我们需要的是下一期收益率 (P_{t+1} - P_t) / P_t\n",
"df_analysis['next_return'] = df_analysis['close'].pct_change().shift(-1)\n",
"\n",
"# 再次清理数据,因为最后一行没有'next_return'\n",
"df_final = df_analysis.dropna()\n",
"\n",
"# 步骤C: 可视化分析 - 散点图\n",
"plt.figure(figsize=(12, 8))\n",
"sns.regplot(x='spread', y='next_return', data=df_final,\n",
" scatter_kws={'alpha':0.5}, line_kws={'color':'red'})\n",
"plt.title('价差 (Price - MA) vs. 下一期收益率', fontsize=16)\n",
"plt.xlabel('价差 (当前价格 - 12周期均线)')\n",
"plt.ylabel('下一根K线的收益率')\n",
"plt.axhline(0, color='grey', linestyle='--')\n",
"plt.axvline(0, color='grey', linestyle='--')\n",
"plt.grid(True)\n",
"plt.show()\n",
"\n",
"# 步骤D: 量化分析 - 计算相关系数\n",
"correlation_spread_return = df_final['spread'].corr(df_final['next_return'])\n",
"print(f\"价差 'spread' 与 '下一期收益率' 之间的皮尔逊相关系数为: {correlation_spread_return:.4f}\")\n",
"\n",
"if correlation_spread_return < -0.1:\n",
" print(\"结论:存在较明显的负相关性,支持'均值回归'假说。\")\n",
"elif correlation_spread_return > 0.1:\n",
" print(\"结论:存在较明显的正相关性,支持'趋势跟随'假说。\")\n",
"else:\n",
" print(\"结论:相关性非常弱,接近于零。这表明,仅凭当前价格偏离均线的程度,无法有效预测下一期的收益方向。\")\n",
"print(\"散点图中的点云分布非常弥散,没有形成清晰的线性趋势,红色的回归线也近乎水平,这与计算出的低相关系数相符。\")"
],
"id": "605d0f5c12a2da6b",
2025-09-20 00:04:51 +08:00
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
2025-09-24 23:14:14 +08:00
"--- 问题一分析:价格与长期均线的相关性 ---\n",
"收盘价 'close' 与 12周期均线 'ma_12' 之间的皮尔逊相关系数为: 0.9990\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1400x700 with 1 Axes>"
],
"image/png": "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OniPp2TvpNWrVy+aNGlC5cqVKVKkCN26deOuu+7i4Ycf9vaJiorinXfeATxZT3fddVe6az/zzDO89NJL3udOp5M333wz09cEnoBHSmAMPO9VZgG3C61cuTLTY08//TS///47P/74o09gCzxZaxf+zF4o5Z5llOGUEswsXrx4psvdQkND02Xe7dmzh4ULF9K9e/d02U0pUoJSgYGBhISEMGHCBB599FFWr16d5SBSx44dMzyeshStZ8+eFx0nM61ataJLly789NNPzJkzh8ceewyAc+fO8dtvv1GtWjXuv//+S45jMpn49NNPSUxMJDQ0lODgYIKDg7FYLAwdOpTZs2cTHR3NoEGDvEE1ERGRK0FBJBERkWw6deoUa9as8X4BzIjD4WDOnDkA9OvX76LjffTRRz5BCkgNIt1222106NABwzB44okncLvdDBw40OfL9c6dO70ZRBcze/Zs7Ha7TyAlxYgRIzhz5gwTJ07ktttuY8uWLQwbNoyQkJAsZVEdO3YMgC1btmS5blPKMpyQkJBLBpEysmzZMv7v//6PgIAApkyZQqNGjS7av1q1anz//ff8/vvvfPTRR3z44YfeAB1A7969cTqdfPTRRxQtWjRdzaS0LgyM3HTTTd4sqOjo6HT9nU4nAwYM4MSJEzz//PPplj327NmTPXv28OSTT/q0+/n5MWfOHPz8/HwykVwuFy6Xi8TERJKSkkhMTCQwMNCbfZTVpWngWV5ntVrp0aNHlvqvWLGC4cOH8+abb3LzzTdn+TopDMNg0aJFAAwZMiTTfs2bN+frr7/2aRs1ahTh4eEMGDAAgA8//JDQ0FDuv//+dFlJKdliNWrUYMWKFZw6dYoZM2bQvn17nyyi2NhYvv32W8DzMxUYGMjy5cu9x0uXLs3tt9+O3W7n119/xWw2M3jw4Azn/PXXX1OgQAGftu3bt2O1Wr2Bvq5du1K2bFlatWrlzUT76aefsNvtdO3alQMHDgCee5yUlETFihUzzLgqX758urbvvvuO2bNnU6lSJcaNG+cNIBmGwZQpU4iLi+O5557LcpBRRETkQgoiiYiIZFNKBsbFluEsWLCA06dP061bN59ARUREBDNnzqR9+/bUr1+fhISEDL8MXsjtdtOpUyc+/PBDunfvzsKFC6lYsaL3+KWCBmfOnOHLL78ESJdVMmvWLH744Qd69OjBbbfdBniWbB05coSBAwdy/Phx+vTpc9FlL4cPHwY8WVHZqaWUU3PnzmXw4MEEBwfzxRdfULdu3UueExoaSr169di+fTsApUqV8slMsVqtmEwm6tWrB1z8PU37XqQsk7PZbD4ZQZAaCPj6669ZuXIlnTp1okaNGtx7772888471K1bl88++4x169bx1ltvERYWlu46l3pt7777LtOmTUvXfuHSx1tvvTVd4fMzZ84wfvx4EhISqFSp0kWXA6a81tGjR3P06NEMi69v27bNu/wuJUCycOFCtm7dSlJSEoMGDeKvv/7ixIkTPPnkk9x5553pxoiNjeWJJ55Il3U3bdo0/vrrL95//31CQ0OJiopi6tSpmM1mWrRoke71rlixgqVLl7J37162bt3KqVOnAAgJCeHee+/19ktKSmLMmDE+56Z9fuutt3L77bezaNEizp07R9WqVb1ZaikOHTrEyZMn091/gCeffJL
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"分析:正如预期,均线紧随价格之后,它们的相关性极高。\n",
2025-09-20 00:04:51 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"================================================================================\n",
"\n",
"--- 问题二分析:价差与下一期收益率的相关性 ---\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1200x800 with 1 Axes>"
],
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA/8AAALECAYAAABeyICMAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XeYlNX5N/Dv06Zvb7Ds0hEBUVEBG6gYu6CiUROV5JdYEDXW2KNvjMaSaIwmJsYCKjHBWNEYiBUbKIIi4NKWtssu22enz1PfP4aZ7LJtdtk6+/1cl5fLzFPOlGdm7nPucx/BsiwLRERERERERJSyxL5uABERERERERH1LAb/RERERERERCmOwT8RERERERFRimPwT0RERERERJTiGPwTERERERERpTgG/0REREREREQpjsE/ERERERERUYpj8E9ERERERESU4hj8ExEREREREaU4Bv9ERAPE008/jfnz52PlypUt7vP5fPjZz36GDz/8EJZlden477//PlatWtXqfe+88w5effVVqKrapWMT0cB155134vTTT0cwGEzctn37dpx++um49957u+Ucb775Ju655x4EAoFmt3/99deYN28e/v73v3fpuKtWrcIFF1yAjz/+uNX7X3/9dZx77rn4xz/+0aXjN7V3717U1NQc8HGIiHoKg38iogHi/fffx0cffQSHw9HivqVLl+Lzzz/Hc889B0EQOn3s6upq/PKXv8Qtt9wCr9fb4v7FixfjrrvuwrJly7rS9E5766238MUXX/TKuajnbNq0CYsWLerrZvQLpmmioaEBwWAQmqZ16RiGYSAcDsPr9fZqR5zX68WOHTtgt9sTt8myjB07dqChoSFx23XXXYeJEye2+t+CBQvaPcfatWuxZMkSiGLzn6YfffQRvvzyS5SUlHSp7Q6HAxs3bsRvfvObVp/3V199FSUlJcjLy+vS8eP+85//4IQTTsATTzxxQMchIupJcl83gIiIOlZWVoYNGzZg2rRpmDJlSrP7VFXFc889BwBYt24dNmzYgEMOOaRTx8/Pz8eNN96IBx54AK+++iouv/zyxH0bN27EN998gzPOOANz5sw58AfTgRUrVuCOO+7AVVddhWOPPbbHz0c9Z/369XjwwQfhdDpx0UUX9XVz+lR1dTVOOOGEbjveM888g5kzZ3bb8VoTiURgWRZkWYYgCJDl//1sjP9ts9kQDoehKApkWYZhGLjvvvuQk5MDAGhoaMDdd98Nm83W7rninZqKoiRusywL77//Pux2O6677rouPYbDDz8cZ511Ft5++23861//wo9//OPEfbt27cKaNWtw+OGH4wc/+EGHx1q3bh3sdnuzNsYVFRVBFEUsX74cP/nJT1p0wlqWBU3TYJomJk2a1KXHQkR0oBj8ExENAIsXL4Zpmvi///u/Fve98MILqKiowA033IBly5bhl7/8JZYsWYL09PQ2j/f111/j008/hc1mS/yQ1XUdEydOhGma+Nvf/pbY9sMPPwQA5ObmJm43DAOapuGwww7r1oCmrKwMN910Ey644AJcf/31AIAvv/wS8+bNa7adoigYNmwYzjjjDMyfP7/VbIjOuv322/HGG2/ggw8+QFFR0QEfbyC57LLL8NVXX0EQBHz99dfweDwAgC1btmD27NkAgPPOOw8PPfRQs/0sy8KMGTNQU1ODjz/+GEOHDm12/w9/+EPU1dXhN7/5DSZMmIBDDz20dx5QPyTLMsaNGwebzdbsumtqzZo1EEWxRQdfnGEYidF/l8vV003G888/jz/+8Y+Jf48fP77FNm+//TbefvttvPnmm4kOgeOOOy5xDe3duxdA86C+tLQUv/nNb3DsscfiyiuvBABIkgQAzYLmVatWYefOnZg3bx4KCgpanFvX9UQGRPz5CIfDME0TiqJAkiSIoojLL78cmZmZOP7445vt/9prrwEAbrjhBgCx93P8OY5Goy0+Qy+88MJ2ny8AaGxsxFlnndXm/bm5ufj88887PA4RUU9g8E9E1M8FAgG89tprGDZsGE488cRm923fvh1//vOfMXHiRFx55ZU49dRTccEFF+DnP/85Fi5cmAji9rdx40Y899xzsNvtsNvtzX5wL1y4EPX19bDb7UhLSwMQ+8H6n//8B8D/fiCrqoof/ehH3Rr833XXXRgxYkSr84gLCwsTmQc1NTVYtWoV/vKXv+DLL7/ECy+80OHIInXMsixs2rQJRx11FADg+++/b3f7DRs2JOY4r1ixAhdffHGLbebPn4/169fj9ttvx9KlS5uNHg8mubm5eOedd9rd5qijjoLL5cJLL73US61q35w5c3DCCSfg8ccfx+rVqxPBMgDU1dXhsssuw8knn4zLL78cxcXFiQC+NU3T+SORCFauXImDDz643fPH5+G/+OKLePHFF9vcbt68ebjrrrsAAA8++CCWLFnS6nZtPa8//elPW7198+bNzf69bNkyuFwuuN1uuFyuxGPy+Xy4+OKLE//t/1lkWRZUVYXX6+3ylA8iou4wOL+BiYgGkL/85S/w+/2YMGFCsx/QDQ0NuPbaayFJEh599FFIkoQxY8bgD3/4A6655hpccskl+OMf/4iRI0e2OOZPfvIT/OQnP2n1fF9//TUuueQSXHnllbj22mt76mG18PHHH2PNmjV47bXXWg0iioqKcOONNyb+raoqfvGLX+Cjjz7Cv/71L1xyySUHdP6bbroJV1xxRasjjINJSUlJIvjftGlTu9uuWLGi2d+tBf8AcM899+Css87Cq6++2uY21P8UFRWhqKgIHo8HgiBgzJgxifvcbjcAID09HUcccQQAtFtvpOl98doB7WXs7N69G++//z6OOuoozJo1q9l9v//97zF27FjMnj0bqqpi4sSJifuOP/54ZGZmNhv537+OQFtM04Rpms0yCpry+/34zW9+g9tuu61ZFsTDDz+M7du3w+/3Q1EUrF69GqZpYvr06YnH/swzz+C9997Ds88+m1RbiIh6AoN/IqJ+bPfu3XjhhRda3F5dXY2rrroKO3fuxJNPPonRo0cn7jvxxBPx29/+FnfeeSfmzp2LW2+9FRdccEGLEdf6+nqsXbsWdru9WbC9fPlyALE05f2L7lmWBdM0oaoqRo4c2SwYOFCLFy/GrFmzOhwNjLPZbPjJT36Cjz76CO+///4BB//5+fnIz88/oGOkgqaj/R2N/H/yySfIz8/HpEmTsGrVKqiq2moGRkFBAc4//3wsXryYwf8AZVkWSktLE/+uq6trsU08wC8rK0M0Gu1wu/YyBf7yl7/Asizce++9OOiggxK3+3w+PPLII5gyZUpiykBTp556Kk499dRmt0WjUTz33HOYN29es2yoxsZGrFixAqeffnpSmUMlJSX45ptv8MMf/hAPPPAAZs+ejZdffhlvvPEGfve732H27Nmorq7GzTffjNraWixatAiHH344fvvb3+If//gHJkyYgOrq6gMuLkhE1FUM/omI+inDMPCrX/0Kuq43+8G6Zs0a3HzzzaiqqsJDDz2Ek08+ucW+55xzDrKzs3H99dfj3nvvxcKFC/H00083ywLYvn07rrnmmjbP/4c//KHd9t18883dFvyHQiGsWrUKjz32WKf2i88xr6io6JZ2DHbHHHNMs6rqmzdvxrHHHtvqygv19fVYv349Tj31VEyaNAkfffQRVq9ejeOOO67VY8+ePRuLFi1CWVkZiouLe+wxUM8Ih8M488wz290mHtS3lUafrK1bt+Ktt95CTk4ObrvtNtxwww2J6UXxaSbJZuhUVVXhuuuuw7p16/Df//4XL774YmIu/6OPPoolS5bggQcewHnnnYeLLroIo0aNavNYF110EY444ghcccUVCIfDuP/++/HSSy9h0qRJ2LZtG/7whz/ggw8+gN/vxzPPPINp06ZhyZIl+Mc//oG5c+fivvvua7XWAxFRb2HwT0TUTz366KNYtWoVzj//fGzcuBFALMi9/PLLYZomHn30UQiCgNdeew1nnXVWIoXW7/fj2WefxZgxY/Dmm2/irrvuwtChQ1uk/0+cOBFLly6Fw+FIpOF6vV6cf/75mDZtGh5
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"价差 'spread' 与 '下一期收益率' 之间的皮尔逊相关系数为: 0.0151\n",
"结论:相关性非常弱,接近于零。这表明,仅凭当前价格偏离均线的程度,无法有效预测下一期的收益方向。\n",
"散点图中的点云分布非常弥散,没有形成清晰的线性趋势,红色的回归线也近乎水平,这与计算出的低相关系数相符。\n"
2025-09-20 00:04:51 +08:00
]
}
],
2025-09-24 23:14:14 +08:00
"execution_count": 14
2025-09-20 00:04:51 +08:00
},
{
"metadata": {
"ExecuteTime": {
2025-09-24 23:14:14 +08:00
"end_time": "2025-09-21T14:34:37.441919Z",
"start_time": "2025-09-21T14:34:36.956292Z"
2025-09-16 09:59:38 +08:00
}
},
"cell_type": "code",
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
2025-09-24 23:14:14 +08:00
"from statsmodels.tsa.stattools import adfuller\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"def calculate_daily_vwap(df):\n",
" \"\"\"\n",
" 为整个DataFrame计算每日重置的VWAP。\n",
" 确保索引是DatetimeIndex。\n",
2025-09-16 09:59:38 +08:00
" \"\"\"\n",
2025-09-24 23:14:14 +08:00
" if not isinstance(df.index, pd.DatetimeIndex):\n",
" raise ValueError(\"DataFrame的索引必须是DatetimeIndex类型。\")\n",
"\n",
" df['date'] = df.index.date\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" # 计算典型价格和相关项\n",
" df['typical_price'] = (df['high'] + df['low'] + df['close']) / 3\n",
" df['tp_x_vol'] = df['typical_price'] * df['volume']\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" # 按天分组计算累积值\n",
" cum_vol = df.groupby('date')['volume'].cumsum()\n",
" cum_tp_x_vol = df.groupby('date')['tp_x_vol'].cumsum()\n",
"\n",
" df['vwap'] = cum_tp_x_vol / cum_vol\n",
"\n",
" # 清理辅助列\n",
" df = df.drop(columns=['date', 'typical_price', 'tp_x_vol'])\n",
" return df\n",
"\n",
"def plot_vwap_analysis(df, event_indices, deviation_threshold):\n",
2025-09-16 09:59:38 +08:00
" \"\"\"\n",
2025-09-24 23:14:14 +08:00
" 可视化价格、VWAP以及价差。\n",
" \"\"\"\n",
" fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(15, 10), sharex=True,\n",
" gridspec_kw={'height_ratios': [2, 1]})\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" # --- 上图: 价格 vs VWAP ---\n",
" ax1.plot(df.index, df['close'], label='Close Price', color='black', linewidth=1.0)\n",
" ax1.plot(df.index, df['vwap'], label='VWAP', color='blue', linestyle='--', linewidth=1.5)\n",
" ax1.set_title('Price vs. Daily VWAP', fontsize=16)\n",
" ax1.set_ylabel('Price')\n",
" ax1.legend()\n",
" ax1.grid(True, which='both', linestyle='--', linewidth=0.5)\n",
"\n",
" # --- 下图: 价差百分比与事件点 ---\n",
" ax2.plot(df.index, df['spread_pct'], label='Price-VWAP Spread (%)', color='purple', linewidth=1.0)\n",
" # 绘制触发分析的阈值线\n",
" ax2.axhline(deviation_threshold, color='red', linestyle=':', label=f'Threshold ({deviation_threshold:.2f}%)')\n",
" ax2.axhline(-deviation_threshold, color='red', linestyle=':')\n",
" ax2.axhline(0, color='gray', linestyle='-')\n",
" # 高亮显示被分析的极端偏离事件点\n",
" ax2.plot(event_indices, df.loc[event_indices, 'spread_pct'], 'o', color='red', markersize=5, label='Extreme Deviation Events')\n",
"\n",
" ax2.set_title('Price Deviation from VWAP', fontsize=14)\n",
" ax2.set_xlabel('Date')\n",
" ax2.set_ylabel('Spread (%)')\n",
" ax2.legend()\n",
" ax2.grid(True, which='both', linestyle='--', linewidth=0.5)\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" plt.tight_layout()\n",
" plt.show()\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"def plot_contribution_summary(price_pct, vwap_pct):\n",
" \"\"\"\n",
" 可视化价格和VWAP对均值回归的贡献度。\n",
" \"\"\"\n",
" labels = ['Price Contribution', 'VWAP Contribution']\n",
" percentages = [price_pct, vwap_pct]\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" fig, ax = plt.subplots(figsize=(8, 6))\n",
" bars = ax.bar(labels, percentages, color=['skyblue', 'salmon'])\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" ax.set_ylabel('Contribution to Mean Reversion (%)')\n",
" ax.set_title('Who is Regressing to Whom?', fontsize=16)\n",
" ax.set_ylim(0, 100)\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" # 在条形图上显示百分比\n",
" for bar in bars:\n",
" height = bar.get_height()\n",
" ax.annotate(f'{height:.2f}%',\n",
" xy=(bar.get_x() + bar.get_width() / 2, height),\n",
" xytext=(0, 3), # 3 points vertical offset\n",
" textcoords=\"offset points\",\n",
" ha='center', va='bottom')\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" plt.tight_layout()\n",
" plt.show()\n",
2025-09-16 09:59:38 +08:00
"\n",
"\n",
2025-09-24 23:14:14 +08:00
"# 2. 计算VWAP和价差\n",
"print(\"\\n步骤1: 计算VWAP和价格偏离度...\")\n",
"df = calculate_daily_vwap(df_raw.copy())\n",
"df['spread'] = df['close'] - df['vwap']\n",
"df['spread_pct'] = (df['spread'] / df['vwap']) * 100\n",
"df.dropna(inplace=True)\n",
"\n",
"# 3. ADF检验\n",
"print(\"\\n步骤2: 使用ADF检验测试价差的平稳性...\")\n",
"adf_result = adfuller(df['spread_pct'])\n",
"print(f'ADF 统计量: {adf_result[0]:.4f}')\n",
"print(f'p-value: {adf_result[1]:.4f}')\n",
"if adf_result[1] < 0.05:\n",
" print(\"结论: p-value < 0.05, 价差序列是平稳的,存在显著的均值回归效应。\")\n",
"else:\n",
" print(\"结论: p-value >= 0.05, 价差序列可能是非平稳的,均值回归效应不显著。\")\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"# 4. 量化贡献度分析\n",
"print(\"\\n步骤3: 量化价格和VWAP对均值回归的贡献...\")\n",
"# 定义参数\n",
"deviation_threshold_pct = df['spread_pct'].std() * 1.5 # 使用1.5倍标准差作为阈值\n",
"forward_periods = 4 # 向前看1小时 (4 * 15min)\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"# 识别极端偏离事件\n",
"event_indices = df[np.abs(df['spread_pct']) > deviation_threshold_pct].index\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"price_contributions = []\n",
"vwap_contributions = []\n",
"total_reversions = []\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"for idx in event_indices:\n",
" try:\n",
" future_idx = df.index[df.index.get_loc(idx) + forward_periods]\n",
" except (KeyError, IndexError):\n",
" continue\n",
"\n",
" initial_spread = df.loc[idx, 'spread']\n",
" future_spread = df.loc[future_idx, 'spread']\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" if np.sign(initial_spread) != np.sign(future_spread) or abs(future_spread) < abs(initial_spread):\n",
" initial_price, future_price = df.loc[idx, 'close'], df.loc[future_idx, 'close']\n",
" initial_vwap, future_vwap = df.loc[idx, 'vwap'], df.loc[future_idx, 'vwap']\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" total_reversion = initial_spread - future_spread\n",
" price_change = future_price - initial_price\n",
" vwap_change = future_vwap - initial_vwap\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" price_contribution = -price_change * np.sign(initial_spread)\n",
" vwap_contribution = vwap_change * np.sign(initial_spread)\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" if total_reversion > 0:\n",
" total_reversions.append(total_reversion)\n",
" price_contributions.append(price_contribution)\n",
" vwap_contributions.append(vwap_contribution)\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
"# 5. 展示结果\n",
"if total_reversions:\n",
" total_price_contribution = np.sum(price_contributions)\n",
" total_vwap_contribution = np.sum(vwap_contributions)\n",
" total_contribution_sum = total_price_contribution + total_vwap_contribution\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" price_pct = (total_price_contribution / total_contribution_sum) * 100\n",
" vwap_pct = (total_vwap_contribution / total_contribution_sum) * 100\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" print(f\"\\n在 {len(total_reversions)} 个极端偏离事件中,观察未来{forward_periods*15}分钟的回归情况:\")\n",
" print(f\"价格移动贡献了 {price_pct:.2f}% 的均值回归。\")\n",
" print(f\"VWAP移动贡献了 {vwap_pct:.2f}% 的均值回归。\")\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" if price_pct > vwap_pct:\n",
" print(\"\\n最终结论: 数据表明,主要是'价格主动回归VWAP',为交易策略提供了统计支持。\")\n",
" else:\n",
" print(\"\\n最终结论: 数据表明,主要是'VWAP被动追赶价格',简单的回归策略可能存在逻辑缺陷。\")\n",
2025-09-16 09:59:38 +08:00
"\n",
2025-09-24 23:14:14 +08:00
" # 可视化\n",
" plot_vwap_analysis(df, event_indices, deviation_threshold_pct)\n",
" plot_contribution_summary(price_pct, vwap_pct)\n",
2025-09-16 09:59:38 +08:00
"\n",
"else:\n",
2025-09-24 23:14:14 +08:00
" print(\"\\n结论: 在当前数据和阈值下,未找到足够的极端偏离事件进行分析。尝试放宽阈值或使用更长的数据。\")\n",
" # 即使没有事件也画出价格和VWAP图\n",
" plot_vwap_analysis(df, event_indices, deviation_threshold_pct)"
2025-09-16 09:59:38 +08:00
],
2025-09-24 23:14:14 +08:00
"id": "d03102ef353f9e79",
2025-09-16 09:59:38 +08:00
"outputs": [
2025-09-24 23:14:14 +08:00
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"步骤1: 计算VWAP和价格偏离度...\n",
"\n",
"步骤2: 使用ADF检验测试价差的平稳性...\n",
"ADF 统计量: -21.8008\n",
"p-value: 0.0000\n",
"结论: p-value < 0.05, 价差序列是平稳的,存在显著的均值回归效应。\n",
"\n",
"步骤3: 量化价格和VWAP对均值回归的贡献...\n",
"\n",
"在 363 个极端偏离事件中观察未来60分钟的回归情况:\n",
"价格移动贡献了 17.20% 的均值回归。\n",
"VWAP移动贡献了 82.80% 的均值回归。\n",
"\n",
"最终结论: 数据表明,主要是'VWAP被动追赶价格',简单的回归策略可能存在逻辑缺陷。\n"
]
},
2025-09-16 09:59:38 +08:00
{
"data": {
"text/plain": [
2025-09-24 23:14:14 +08:00
"<Figure size 1500x1000 with 2 Axes>"
2025-09-16 09:59:38 +08:00
],
2025-09-24 23:14:14 +08:00
"image/png": "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AH379uXUU08lPDy8RgXzn376CYCePXvW9G2JiIiIyHHopp8iIiIiUm/y8vLYsmULzzzzDEVFRVx33XVERERU2e6+++6jQ4cOvPfeeyd8M8jFixcze/ZsTj75ZN5++20aNWoEwKWXXsrll1/O9OnTOeusswDjppaPPvooHTp04MMPP/QUV2+++WbOO+88pk+fzoUXXljlho3HExISwgUXXMCbb77JunXrOO200wAoLS1l4cKFnHTSSZxyyime7ZcsWUJ2djZ/+ctfuOWWWzzL77rrLtatW0dhYaGlo4nNPisoKPAse/HFFyktLeX111/n1FNPBYxfEYwePZoPPviAP/7xj/XergkTJvDRRx/x2WefeY36Nr+UmDhxYq33PWLECN555x1WrFjBNddcw+bNmzl48CA9evTwyqg5enz37t1kZmbSvn17MjMz+frrrz0F88ojzMvKyli+fDnR0dH06NGD0NBQTj75ZH7++Wf27NlzzC+LMjIyWLt2LU888QQA1157ba3fn4iIiIj4poK5iIiIiNS50aNHV1l2zjnnMGXKFJ/bh4aG8vzzz9dJUfiTTz4B4M477/QUywH69OnDjBkzcDgcnmVffvklxcXFNG/enNdff91rP3Fxcezbt49t27bRrVu3Grfjwgsv5M0332T+/Pmegvn3339PZmZmlWKyOYJ706ZNXsVxX1O6WMGcAiQqKsqzbPbs2V7bFBQUsHLlSoqLiz1TkdS3fv36kZiYyJIlS8jPzyc6OhqXy8X8+fNp06YNp59+eq33PWjQIKKioli1ahXFxcWe0eWVC/NQMXrcHPWdlJRERkYGKSkpPgvmv/zyC1lZWZx++umEhhr/c6xv3778/PPPfPvtt1xzzTVe+09NTa2Sv9DQUO666y6f/85ERERE5MSoYC4iIiIide6qq66iUaNGOJ1O4uLi6N+/v9eI6iPddddddTaC2pxz2leR+7zzzvN6vmvXLgBWrVrFqlWrfO5v//79tSqY9+zZk65du/Lll19y77334nA4mD9/Pk6nk/Hjx3ttO2bMGAYOHMiCBQtYtmwZPXr0oGfPnpxxxhmMGDGixiPc65o5FUuTJk28li9evJglS5Z4RkeXlZXhdDpxuVx+aZfD4eCiiy7i2WefZfHixYwfP57Vq1dz4MABbrnlFpzO2s9AGRERwaBBg/j222/56aefPPOXV56OBSqK4T///DMhISEkJiaSmZlJfn4+mzZtIjw8nBYtWni2N6dj+eGHH6rkaunSpVUK5o0bN+bKK68EjEJ58+bNGT58OG3atKn1exMRERGRo1PBXERERETq3PXXX1/lRofHYt5Usr4VFxdTUlJCVFQUTqfTc3PK559/3jNNS1268MILefrpp1mzZg2nnXYaixcvZuDAgbRq1cpru9DQUN5++21Wr17N6tWr2bJlC1999RWzZs2iX79+vPnmm4SHh9d5+6pr06ZNgHc/3XXXXXz++ed07dqVM888ky5dutCnTx8eeuiho375UB8uuuginnvuOebNm8f48eP54osvcDgcTJgw4YT3PXz4cL799lsWL17MmjVriI+Pp3fv3l7bmCPMly1bRtu2bQkPD6d9+/aeZR06dPD6VYM5V7n5pZLpzTffZPXq1RQUFHiN5I+NjeXOO+884fciIiIiItWjgrmIiIi
2025-09-16 09:59:38 +08:00
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
2025-09-24 23:14:14 +08:00
"<Figure size 800x600 with 1 Axes>"
2025-09-16 09:59:38 +08:00
],
2025-09-24 23:14:14 +08:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAAAxAAAAJICAYAAADxUwLTAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAX0xJREFUeJzt3XlcVGX///H3sCmiKK6JgDu44S64J+7ivmWLC5W7eLdYKmmmuWW5S9pdmVua+1a3prlkq4pmai6oJaa44q4gIMzvD3/MtwnNwzDGoK/n4+Ej5jpnzvnMxBzmfa7rOsdkNpvNAgAAAAADnLK6AAAAAADZBwECAAAAgGEECAAAAACGESAAAAAAGEaAAAAAAGAYAQIAAACAYQQIAAAAAIYRIAAAAAAYRoAAAAAAYBgBAoBdtGzZUgEBAbp9+7YkadiwYQoICNAXX3whSfrpp58UEBCgiIgISdLw4cMVEBCgM2fOZFnNfxcQEKAePXpkyX7/+q9ixYp6+umnFRERoZiYmH+9nuxo9erVCggI0OrVq7O6FIeWnT+n3377rZ599llVrlxZ1atX10svvaRffvklq8sCnkgECAB24ePjI0mKi4uTJJ0+fdrqvxcvXpQkFStWLAuqyx769++v/v37q3PnzsqTJ49Wr16tTp066ejRo1ldGh4T2fVzunLlSvXr10/Hjh1T+/btFRISop07d6pnz57as2dPVpcHPHFcsroAAI+HtC8cly5dUvHixS1nLNP+m/aFJe0LjCPasGGD3N3ds2z/r732muXn1NRUjRw5UqtWrdL06dP10UcfZVld2UGzZs1UpUoVFS5cOKtLMaRHjx7avXu3oqOj/9X9ZsfP6Z07d/T+++/LZDJp/vz5qly5siSpfPny+uCDDzRt2jQtXrw4i6sEniz0QACwi7QvHJcuXVJSUpLlTGbaF5NLly5ZreeISpcuLW9v76wuQ5Lk5OSkAQMGSJL27duXxdU4vjx58qh06dLKkydPVpfi0LLj5/TAgQO6fv26KleubAkPktSgQQNJ0sGDB7OqNOCJRYAAYBdpZzbj4uJ05swZmc1mtWjRwjI0Iu3MpqMNjXBkBQoUkHTvDCxgD9nxc5qQkKCyZcuqevXqVu1JSUmSlKW9hsCTigABwC7+emYz7ctIgwYNdOPGDd24cUOXLl2Sq6urihQpku6527dvV5cuXVS1alWFhIRo+vTpunv3rtU6qampWrhwodq2bavAwEDVq1dPI0aM0IULF+z2GoxMov7uu+/Uo0cP1a5dW9WqVVOHDh20bNkyu9XwV4cPH5Yk+fr6WrUnJyfr448/Vps2bRQYGKjg4GANGTJEsbGx6bZhNpu1ZMkShYaGqlq1aurUqZO2bNmi8ePHKzg4WMOHD7es+9eJyKmpqVq6dKm6deummjVr6ubNm+m2vXPnTr388suqWbOmKleurK5du2rLli33fS0Zed9+//13vfrqq3r66adVuXJlNW/eXFOmTLFM/L2fh02injVrlgICArRr1y798ssv6tGjh6pVq6b69etr9OjRio+PT/ecr7/+Wh06dFC1atXUunVrrV27VpGRkWrQoIFNk+179OhhmSi/e/duSdYT6GfNmvXA9yI4OFiVK1dW586dtX79+gzvO40jfE537dqlgIAATZ06VePGjVP16tXVokULHT16VB9++KGCgoLUoEEDbd68WZL09NNP66uvvrL6XZXuDTmUpKCgIJvfDwC2IUAAsIu/fjE5c+aM8uTJYzljePr0aV26dElFixaVk5P1YWft2rUaNGiQnnrqKXXu3Fnx8fGaM2eOFi5caFknNTVVr7zyisaPH6/ExER17dpV5cqV08qVK9WpUyedPHnyX3mNW7ZsUd++fXX8+HE1b95c3bp1U0pKikaNGqUFCxbYbT9JSUmKiorSiBEjJEnPPPOMZVlycrL69OmjKVOmKF++fOrevbuCgoK0ceNGdevWzTIEJc2nn36qMWPGyNPTU126dNH169c1aNAgHT9+XN26dVPVqlXT7f/u3bvq27ev3nnnHd24cUOlSpVK9/9txYoVCgsL0/Hjx9W2bVt16tRJZ8+e1aBBg9J9ic/I+3bmzBk999xz2rJli2rUqKHu3bvLz89PH3/8sYYOHZqZt1WS9PPPP6tnz55ydXVVly5d5Orqqi+++EJTpkyxWm/Dhg165ZVXlJKSoq5du8rJyUnDhg3Tt99+q06dOql+/foZ3ne7du0sE+XThsqlPe7fv79q1qxptf6ePXvUpUsXbd68WcHBwerYsaMuXryoN998UxMnTrTp9TvS53TRokU6ePCg6tWrp5iYGPXq1UsrV65Uq1atdPnyZU2YMOGBr2Pr1q1asGCBXFxcLEP9APyLzABgJ1WrVjX37t3b/N5775k7duxoTkxMNJcrV8789ddfm2vWrGkOCwuzrDts2DCzv7+/ZXmagwcPmv39/c3PPPOMpW3x4sVmf39/84svvmi+c+eOpX3p0qVmf39/c9euXe1Sv7+/v7l79+4PXD5w4ECzv7+/OSoqytKWmJhobtWqlXngwIGZ2u/9/gUEBJjfffdd8927dy3rfvrpp2Z/f3/zBx98YLWNefPmmf39/c3vvfeeVXvNmjXNLVu2NKekpJjNZrP57Nmz5nLlypl79uyZro5Vq1aZ/f39zU2aNDE3btzYvH///vvWe/78eXOlSpXMLVq0MF+/ft3Sfu7cOXPVqlXN9erVs6o5I+/b/Pnzzf7+/uYPP/zQqv311183N2nSxJyQkHDfmtJqX7Vq1X2Xz5w50/KefvbZZ1avJTAw0FyvXj2r9du1a2euVauW+datW2az2Wy+deuWuVatWuYmTZrcd/sZ1b17d7O/v/8DlycmJpqffvppc7ly5cw//vijpf3q1avm1q1bm/39/c07duywad9Z/TnduXOn2d/f39yiRQtzUlKS+ebNm2Z/f39zxYoVzadOnTKbzWZzhw4dHvj+/Prrr+bAwEBz+fLlzevXr7fpPQCQOVyFCYDdFCtWTJcuXVKOHDlUvHhxubm56amnntKJEyd048aN+46rbtWqlVq0aGF5XKlSJeXPn9/qTPrKlSslSREREcqRI4elvVu3blq2bJn279+v48ePq2zZso/w1UkFCxaUdG9SZ40aNWQymeTm5mYZSpFZ/fv3lyRt27ZNx44d06RJk9S+fXurddKGr9y5c0fTpk2ztKcNMdq1a5elLS4uTjdu3FD9+vUtZ5SLFi0qLy8vnTt37oF1XLhwQWvXrlXp0qXvu3zjxo1KSkpSoUKFNHfuXKtlefPm1blz53TixAkFBARIytj7VqhQIUnS0aNHdefOHeXMmVOS0vUQ2Kpq1ap68cUXLY+LFCkif3//dBNxT548qXLlysnDw0OS5OHhoeLFi1uGlT1qP/74o86dO6cWLVqobt26lvZ8+fLp9ddf14ABA7Ry5Uo1bNgww9vO6s9pmqefflqurq5ydXWVJJUtW1Z+fn6SpNy5cz+w/jFjxigxMVHvv/++2rZtm+HXDyDzCBAA7MbHx0e//fabJKlRo0aSpOLFi1uuInS/K7u0a9cuXdvfJ0X+/vvvcnNzu+8X2ooVK+rQoUM6ceLEIw8Qffv21fbt2zVp0iR9+umnqlChgipWrKgmTZpYXR3GVmmXca1Ro4b69OmjNWvWpAsQaTeWW7Ro0X238dex5vny5ZOnp6eOHj2q1NRUOTk56fz587p69arKly//wDq6dev2wPDw1xp2795tGct/vzrSAkRG3rfmzZsrKChImzZt0vfff6/y5curQoUKqlu3rp5++mk5Ozs/sC4j7veF836TcIsXL65Tp04pPj5euXLlUnx8vE6dOvWvXZ3oxIkTkqQKFSqkW1axYkVJ9z4Xtsjqz2n+/PklpQ8J/xQa0ly9elWHDh1S/fr10302APx7CBAA7KZYsWLavn27Ll26pF69ekmS/Pz8LJNl73dms3jx4oa2bTKZZDKZ7FesDYoVK6bNmzdrx44dOnDggKKjo7Vw4UJ99NFH6t27t95880277Kdhw4aqWLGifv75Z+3bt0/VqlWzLDObzfL
2025-09-16 09:59:38 +08:00
},
"metadata": {},
"output_type": "display_data"
}
],
2025-09-24 23:14:14 +08:00
"execution_count": 15
2025-09-16 09:59:38 +08:00
},
{
"metadata": {
"ExecuteTime": {
2025-09-24 23:14:14 +08:00
"end_time": "2025-09-21T14:51:34.214107Z",
"start_time": "2025-09-21T14:51:33.595310Z"
2025-09-16 09:59:38 +08:00
}
},
"cell_type": "code",
"source": [
2025-09-24 23:14:14 +08:00
"# ================= 0. 环境 =================\n",
2025-09-16 09:59:38 +08:00
"import pandas as pd\n",
"import numpy as np\n",
2025-09-24 23:14:14 +08:00
"import talib # 只用 ATR\n",
"from sklearn.linear_model import LinearRegression\n",
"from statsmodels.tsa.stattools import adfuller\n",
"from statsmodels.tsa.vector_ar.vecm import VECM\n",
"from statsmodels.tsa.vector_ar.irf import IRAnalysis\n",
"from statsmodels.tsa.stattools import grangercausalitytests\n",
2025-09-16 09:59:38 +08:00
"import matplotlib.pyplot as plt\n",
2025-09-24 23:14:14 +08:00
"plt.style.use('seaborn-v0_8-darkgrid')\n",
"\n",
"# ================= 1. 读入 15 min K 线 =================\n",
"# df_raw = pd.read_csv('xxx.csv', index_col='timestamp', parse_dates=True)\n",
"# 示例生成随机数据,你换成自己的即可\n",
"np.random.seed(42)\n",
"idx = pd.date_range('2023-01-01', periods=5000, freq='15min')\n",
"df_raw = pd.DataFrame(index=idx,\n",
" data={'open': 100,\n",
" 'high': 100.5,\n",
" 'low': 99.5,\n",
" 'close': 100,\n",
" 'volume': 1e4})\n",
"# 生成随机游走\n",
"df_raw[['open', 'high', 'low', 'close']] = (df_raw[['open', 'high', 'low', 'close']]\n",
" .cumsum() + 100)\n",
"df_raw['volume'] = np.random.poisson(1e4, len(df_raw))\n",
"\n",
"# ================= 2. 计算 VWAP =================\n",
"def vwap_15min(df):\n",
" \"\"\"典型价格 * 成交量累积\"\"\"\n",
" tp = (df['high'] + df['low'] + df['close']) / 3\n",
" return (tp * df['volume']).cumsum() / df['volume'].cumsum()\n",
"\n",
"df_raw['vwap'] = vwap_15min(df_raw)\n",
"df = df_raw[['close', 'vwap']].dropna()\n",
"spread = df['close'] - df['vwap']\n",
"spread.name = 'spread'\n",
"\n",
"# ================= 3. 平稳性 + Hurst =================\n",
"def adf_p(x):\n",
" return adfuller(x, regression='c')[1]\n",
"\n",
"def hurst_sklearn(ts, min_win=32):\n",
" \"\"\"log(R/S) vs log(n) 回归\"\"\"\n",
" ts = ts.dropna().values\n",
" n = len(ts)\n",
" if n < min_win * 2:\n",
" return np.nan\n",
" rs, sz = [], []\n",
" for size in np.logspace(np.log10(min_win), np.log10(n//4), 8, dtype=int):\n",
" m = n // size\n",
" if m < 2:\n",
" continue\n",
" _rs = 0\n",
" for i in range(m):\n",
" idx = slice(i*size, (i+1)*size)\n",
" x = ts[idx]\n",
" z = np.cumsum(x - x.mean())\n",
" r = z.max() - z.min()\n",
" s = x.std(ddof=1)\n",
" if s == 0:\n",
" continue\n",
" _rs += r / s\n",
" rs.append(_rs / m)\n",
" sz.append(size)\n",
" if len(rs) < 3:\n",
" return np.nan\n",
" lg_sz = np.log(sz)\n",
" lg_rs = np.log(rs)\n",
" H = LinearRegression().fit(lg_sz.reshape(-1,1), lg_rs).coef_[0]\n",
" return H\n",
"\n",
"print('ADF p 值:', adf_p(spread))\n",
"print('Hurst :', hurst_sklearn(spread))\n",
"# ========= 阈值 =========\n",
"if adf_p(spread) < 0.05 and hurst_sklearn(spread) < 0.45:\n",
" print('→ 价差平稳且强均值回归,可继续\\n')\n",
2025-09-16 09:59:38 +08:00
"else:\n",
2025-09-24 23:14:14 +08:00
" print('→ 价差非平稳或趋势性强,策略终止\\n')\n",
" exit\n",
"\n",
"# ================= 4. Johansen 协整 + VECM =================\n",
"from statsmodels.tsa.vector_ar.vecm import coint_johansen\n",
"\n",
"jres = coint_johansen(df[['close', 'vwap']], det_order=0, k_ar_diff=1)\n",
"print('迹统计量:', jres.lr1)\n",
"print('5% 临界:', jres.cvt[:, 0])\n",
"if jres.lr1[0] < jres.cvt[0, 0]:\n",
" print('无协整,退出')\n",
" exit\n",
"\n",
"vecm = VECM(df[['close', 'vwap']], k_ar_diff=1, coint_rank=1)\n",
"vecm_res = vecm.fit()\n",
"print(vecm_res.summary())\n",
"\n",
"α_close = vecm_res.alpha[0, 0]\n",
"α_vwap = vecm_res.alpha[1, 0]\n",
"print(f'Adjustment coef -> close: {α_close:.4f} vwap: {α_vwap:.4f}')\n",
"if abs(α_close) > abs(α_vwap) and α_close < 0:\n",
" print('结论:价格主动向下修正 → 价格被 VWAP 拉回\\n')\n",
"elif abs(α_vwap) > abs(α_close) and α_vwap > 0:\n",
" print('结论VWAP 主动向上修正 → VWAP 被价格拉回\\n')\n",
"else:\n",
" print('结论:双向修正,强弱相近\\n')\n",
"\n",
"\n",
"# ================= 6. 交易信号骨架 =================\n",
"sigma = spread.rolling(500).std()\n",
"mean = spread.rolling(500).mean()\n",
"df['z'] = (spread - mean) / sigma\n",
"\n",
"# 阈值 ±1.8σ,持有 1 根 15min\n",
"entry_long = df['z'] < -1.8\n",
"entry_short = df['z'] > 1.8\n",
"exit_long = df['z'] > -0.5\n",
"exit_short = df['z'] < 0.5\n",
"\n",
"df['signal'] = 0\n",
"df.loc[entry_long, 'signal'] = 1\n",
"df.loc[entry_short, 'signal'] = -1\n",
"df.loc[exit_long, 'signal'] = 0\n",
"df.loc[exit_short, 'signal'] = 0\n",
"df['position'] = df['signal'].replace(0, method='ffill').fillna(0)\n",
"\n",
"# 简单回测:收益按 close 差值 + 滑点 1 tick\n",
"tick_size = 0.5 # 螺纹钢例\n",
"tick_val = 5 # 1 点 10 元,半跳 5 元\n",
"df['ret'] = df['position'].shift(1) * df['close'].diff() - \\\n",
" tick_val * np.abs(df['position'].diff())\n",
"df['cum'] = df['ret'].cumsum()\n",
"\n",
"print('样本内夏普:', (df['ret'].mean() / df['ret'].std()) * np.sqrt(252 * 4 * 24))\n",
"df['cum'].plot(figsize=(10, 4), title='Close-VWAP Mean-Reversion PnL (1 lot)')\n",
"plt.show()"
2025-09-16 09:59:38 +08:00
],
2025-09-24 23:14:14 +08:00
"id": "e94477905a3b23a4",
2025-09-16 09:59:38 +08:00
"outputs": [
2025-09-24 23:14:14 +08:00
{
"name": "stdout",
"output_type": "stream",
"text": [
"ADF p 值: 0.5570947123320896\n",
"Hurst : 1.0036766904694356\n",
"→ 价差非平稳或趋势性强,策略终止\n",
"\n",
"迹统计量: [ 3.67182643e+00 -1.59697411e-06]\n",
"5% 临界: [13.4294 2.7055]\n",
"无协整,退出\n",
"Det. terms outside the coint. relation & lagged endog. parameters for equation close\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"L1.close 1.0000 4.5e-13 2.22e+12 0.000 1.000 1.000\n",
"L1.vwap -2.617e-12 8.99e-13 -2.910 0.004 -4.38e-12 -8.55e-13\n",
"Det. terms outside the coint. relation & lagged endog. parameters for equation vwap\n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"L1.close 0.4958 0.007 70.022 0.000 0.482 0.510\n",
"L1.vwap 0.0104 0.014 0.737 0.461 -0.017 0.038\n",
" Loading coefficients (alpha) for equation close \n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"ec1 -1.556e-17 1.9e-14 -0.001 0.999 -3.73e-14 3.72e-14\n",
" Loading coefficients (alpha) for equation vwap \n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"ec1 0.0006 0.000 1.917 0.055 -1.3e-05 0.001\n",
" Cointegration relations for loading-coefficients-column 1 \n",
"==============================================================================\n",
" coef std err z P>|z| [0.025 0.975]\n",
"------------------------------------------------------------------------------\n",
"beta.1 1.0000 0 0 0.000 1.000 1.000\n",
"beta.2 -1.9995 0.000 -1.16e+04 0.000 -2.000 -1.999\n",
"==============================================================================\n",
"Adjustment coef -> close: -0.0000 vwap: 0.0006\n",
"结论VWAP 主动向上修正 → VWAP 被价格拉回\n",
"\n",
"样本内夏普: nan\n"
]
},
2025-09-16 09:59:38 +08:00
{
"data": {
"text/plain": [
2025-09-24 23:14:14 +08:00
"<Figure size 1000x400 with 1 Axes>"
2025-09-16 09:59:38 +08:00
],
2025-09-24 23:14:14 +08:00
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA0cAAAGmCAYAAAC3Pf+NAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAARp1JREFUeJzt3Xl0VPX9//FnAoQlMQE0omxKoASQXQUxCAoiBERcUEJd2IpRATVof6JVhKoVcS2hFVBQXIoo4IIsQl2gAmoFEfVoi4RNKYhGsoAkIZnfH57M1zEkBBMIy/Nxjuc4n/v53HnfmeFz55W7TFggEAggSZIkSce58IouQJIkSZKOBIYjSZIkScJwJEmSJEmA4UiSJEmSAMORJEmSJAGGI0mSJEkCDEeSJEmSBBiOJEmSJAkwHEmSJEkSYDiSdITo1q0bY8aMqegypCNSamoq8fHxFV3GEWHdunW0bNmSb7/9ttzWeSjmn5SUFG655ZZyXaekQ89wJOmQ2rJlC2PHjqV79+60atWK9u3bk5SUxMyZM9m7d29Fl1ei+++/n/j4eDZv3lxsn8cff5z4+Hi++uorevfuzSWXXFKkz9KlS4mPj+eaa64psmzOnDnEx8fz/vvvh7QvW7aM+Ph4OnfuTEFBwX6fu1u3bsTHxwf/69SpE7///e9ZunTpAbet8Mt2s2bN+N///ldkeXZ2Nq1btyY+Pp4///nPB1xfRZo3b17I69CiRQvOO+88xowZw44dOyq6vKPeL1/bZs2a0blzZ4YOHcqHH374m9Y3ZswY2rVr95vrefzxx+nTpw/16tULtq1bt45x48Zx+eWXc8YZZxy2ILljxw5SU1P58ssviywbPnw4S5Ys4auvvjostUgqH4YjSYfMe++9R9++fVm0aBEXXHAB99xzD7fddht169bl4Ycf5oEHHqjoEkvUt29fAObPn19snzfffJOmTZvSrFkzzjzzTNavX09WVlZInzVr1lC5cmU+++wz8vLyiiyrVKkSbdu2DWl/4403qFevHjt37uSDDz4o9vmbN2/OxIkTmThxIkOHDuW7775j5MiRzJo1q1TbGBERwZtvvlmkfcmSJaUafyS5+eabmThxIuPHj6dLly688cYbXHPNNeTk5FR0aWV24403sm7dugp7/oSEBCZOnMiECRNISkriP//5D4MGDWLZsmWHtY4vv/ySlStXkpSUFNK+bNky5syZA0D9+vUPWz3fffcdkydP3m84atGiBS1btmTGjBmHrR5JZWc4knRIbN26lZSUFOrWrcuCBQu4++67ueqqq7j66qt57LHHWLBgAU2aNKnoMkvUpk0bTjvtNBYsWLDf5Z988gnffPNNMESdeeaZFBQUsGbNmpB+a9asoVevXuzdu5cvvvgiZNnq1auJj48nKioq2LZnzx7eeecdhgwZQosWLUoMZ3Xq1KFfv37069eP4cOHM2vWLGrUqMGzzz5bqm3s2rXrfrfvzTff5Pzzzy/VOo4UXbp0oV+/flx55ZU88MADDB06lC1btvD2229XdGn7lZOTU+xRwV+rXLkyVatWPcQVFe/000+nX79+XHrppYwcOZIZM2YQCAR47rnnDmsdc+fOpW7dukX+mDBw4EA+/vhj5s2bR0JCwmGtqSSJiYksXbqU3bt3V3QpkkrJcCTpkHj66afZs2cPDzzwACeffHKR5aeddhqDBg0qcR1bt27l5ptvpkOHDrRp04arrrqK9957r0i/559/nj59+tCmTRvOPvtsLr/88iKBYseOHdx5552ce+65tGzZkj59+gT/0lySvn37kpaWViTUwM8BIiwsjIsvvhj4ORwBIeEoJyeHL774gosuuogGDRqELEtPT2fTpk3BcYWWLl3K3r176dWrF71792bJkiWlPvoRGxtLXFxcqa/HuPjii/nyyy/ZsGFDsK3waFXhdv1abm4ukyZNokePHrRs2ZKuXbsyceJEcnNzQ/rNnTuX6667jk6dOtGyZUt69+7NP/7xjyLr69atG8nJyXz88cf079+fVq1a0b17d1577bVSbUNxzjrrLODnz9EvbdiwIfi5atWqFZdffnlIgPrss8+Ij4/n1VdfLbLOf/3rX8THx/Puu+8G20rz2frwww+Jj49nwYIFPP7445x33nm0adOG7Oxs8vLymDx5MhdddBGtWrWiY8eODBw4kBUrVgTH7++ao3379vG3v/2NCy+8kJYtW9KtWzcee+yxIu/DoXh94+PjqVWrFt98803I9i1cuJAnn3ySLl260KpVKwYNGlTiaakH6+233+acc84hLCwspP2kk06iWrVq5fY8cOD558MPP6R///4A3HnnncFTD+fNmxfsc+6557Jnzx5WrlxZrrVJOnQMR5IOiXfffZcGDRrQvn373zT++++/Jykpiffff5+BAweSkpJCTk4ON954Y8g1NS+//DL3338/jRs35q677mLUqFE0b96cTz/9NGRdV111FatWreLqq6/mT3/6Ew0bNuRPf/rTAY+wFB4V+vWpZ/n5+SxatIizzjqLunXrAtCgQQNOPvnkkABUeCpdu3btaNeuXciywv//9Ws0f/58OnbsSGxsLH369GH37t288847pXrd8vLy2L59OzVr1ixV/7PPPptTTjklZPsWLlxIjRo19nvkqKCggBtvvJEZM2YET5W88MILmTlzJrfeemtI31mzZlGvXj2Sk5MZM2YMp556KuPHj+fFF18sst7Nmzdzyy23kJCQwJgxY4iJiWHMmDGsX7++VNuxP4UBMTo6Oti2fv16BgwYwIYNGxg+fDhjxoyhRo0ajBgxIvi5atWqFQ0aNGDRokVF1rlw4UJiYmLo3LkzcPCfrb///e8sW7aMYcOGMXr0aKpUqcLkyZOZPHkyHTt2ZOzYsdxwww3UrVt3v4H8l+6++24mTZpEixYtuPPOOzn77LOZOnUqKSkpRfqW9+ubkZFBZmZmkc/ZU089xdKlSxk6dCjJycl8+umn3H777b/pOX5tx44dbNu2jRYtWpTL+kpSmvmncePG3HzzzQAMGDAgeHrr2WefHVxPkyZNqFatWpGjyZKOXJUrugBJx57s7Gx27NhB9+7df/M6pk2bxvfff8+LL74YPAJw5ZVXcskll/Dggw/SvXt3wsPDee+99/jd737HpEmTil3X448/Tn5+PvPnz6dWrVrAz6fhjB49msmTJ5OUlFTsX51PP/10WrVqxcKFC/njH/9IePjPf1NauXIlP/zwQ5G7UbVv35733nuPvLw8qlSpwpo1a6hfvz4nn3wy7dq1Y/LkycG+q1evBgg5cvTDDz+watUqxo0bBxA8hWj+/PkkJiYWqW/fvn2kp6cDP1//UPi6XXvttSW+vr/Uu3dvFixYENyW+fPn06NHDyIiIor0nT9/PitXruT5558Pvi8Av/vd77j33ntZs2ZNMOy98MILIa/rNddcw7Bhw3jmmWe4+uqrQ9a7cePGkPc6MTGRrl27Mm/ePO64445SbUd2djbp6enk5uby6aefMnnyZCIiIrjggguCfR544AFOPfVU5s6dG9y+3//+9wwcOJBHHnmEHj16BF+TGTNmkJGRQUxMDPDzEbN//vOf9OjRgypVqgAH/9nKyclh7ty5IW3vvfceXbt25b777ivVdgJ89dVXvPrqq1x55ZXcf//9AFx99dXUrl2bGTNm8MEHH3DOOeeU2+ubk5MT/Jx98803PPbYY+Tn59OrV68i/V577bXgaxsdHc0DDzzAf//7X5o2bVrq7duftLQ04PBcU1Sa+eekk06iS5cuTJo0ibZt29KvX78i66lcuTKnnHIKX3/99SGvWVL58MiRpHKXnZ0NQGRk5G9ex7Jly2jdunXIF/DIyEgGDBjAt99+G/yyER0dzfbt24u9WD0QCLBkyRK6detGIBAgPT09+F/nzp3Jyso64F/oL7nkErZv386///3vYNubb75JlSpVinw5PPPMM0OuLVqzZk3wzlzt27fnhx9+YNOmTcDP1yzVr1+fOnXqBMcvWLCAsLAwLrroomDbxRdfzPLly8nIyChS2/vvv0+nTp3o1KkT/fr1Y/HixfTr1++
2025-09-16 09:59:38 +08:00
},
"metadata": {},
"output_type": "display_data"
}
],
2025-09-24 23:14:14 +08:00
"execution_count": 31
2025-09-16 09:59:38 +08:00
},
{
"metadata": {
"ExecuteTime": {
2025-09-24 23:14:14 +08:00
"end_time": "2025-09-21T14:51:34.237353Z",
"start_time": "2025-09-21T14:51:34.235152Z"
2025-09-16 09:59:38 +08:00
}
},
"cell_type": "code",
2025-09-24 23:14:14 +08:00
"source": "",
"id": "d29a1f47e00d5315",
"outputs": [],
"execution_count": null
2025-09-16 09:59:38 +08:00
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 2
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython2",
"version": "2.7.6"
}
},
"nbformat": 4,
"nbformat_minor": 5
}