523 lines
233 KiB
Plaintext
523 lines
233 KiB
Plaintext
|
|
{
|
|||
|
|
"cells": [
|
|||
|
|
{
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"id": "initial_id",
|
|||
|
|
"metadata": {
|
|||
|
|
"collapsed": true,
|
|||
|
|
"ExecuteTime": {
|
|||
|
|
"end_time": "2025-09-03T15:44:35.182316Z",
|
|||
|
|
"start_time": "2025-09-03T15:44:35.179598Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"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@SHFE_rb/KQ_m@SHFE_rb_min15.csv'\n"
|
|||
|
|
],
|
|||
|
|
"outputs": [],
|
|||
|
|
"execution_count": 52
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"metadata": {
|
|||
|
|
"ExecuteTime": {
|
|||
|
|
"end_time": "2025-09-03T15:44:35.226997Z",
|
|||
|
|
"start_time": "2025-09-03T15:44:35.194022Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"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)"
|
|||
|
|
],
|
|||
|
|
"id": "548c68daa68af8c1",
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"Successfully loaded 25470 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 4352.0 4400.0 4345.0 4388.0 213731.0 1221661.0 \n",
|
|||
|
|
"2021-01-04 09:00:00 4356.0 4368.0 4309.0 4336.0 338332.0 1217327.0 \n",
|
|||
|
|
"2021-01-04 09:15:00 4336.0 4342.0 4307.0 4318.0 144479.0 1197881.0 \n",
|
|||
|
|
"2021-01-04 09:30:00 4318.0 4329.0 4312.0 4317.0 85679.0 1194567.0 \n",
|
|||
|
|
"2021-01-04 09:45:00 4317.0 4338.0 4316.0 4338.0 66461.0 1194592.0 \n",
|
|||
|
|
"\n",
|
|||
|
|
" close_oi underlying_symbol \n",
|
|||
|
|
"datetime \n",
|
|||
|
|
"2020-12-31 14:45:00 1217327.0 SHFE.rb2105 \n",
|
|||
|
|
"2021-01-04 09:00:00 1197881.0 SHFE.rb2105 \n",
|
|||
|
|
"2021-01-04 09:15:00 1194567.0 SHFE.rb2105 \n",
|
|||
|
|
"2021-01-04 09:30:00 1194592.0 SHFE.rb2105 \n",
|
|||
|
|
"2021-01-04 09:45:00 1198035.0 SHFE.rb2105 \n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"execution_count": 53
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"metadata": {
|
|||
|
|
"ExecuteTime": {
|
|||
|
|
"end_time": "2025-09-03T15:44:35.563309Z",
|
|||
|
|
"start_time": "2025-09-03T15:44:35.232102Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"source": [
|
|||
|
|
"import pandas as pd\n",
|
|||
|
|
"import numpy as np\n",
|
|||
|
|
"import matplotlib.pyplot as plt\n",
|
|||
|
|
"import seaborn as sns\n",
|
|||
|
|
"\n",
|
|||
|
|
"\n",
|
|||
|
|
"def calculate_atr(df, period=14):\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" Calculates the Average True Range (ATR).\n",
|
|||
|
|
"\n",
|
|||
|
|
" Args:\n",
|
|||
|
|
" df (pd.DataFrame): DataFrame with 'high', 'low', and 'close' columns.\n",
|
|||
|
|
" period (int): The lookback period for the ATR calculation.\n",
|
|||
|
|
"\n",
|
|||
|
|
" Returns:\n",
|
|||
|
|
" pd.Series: A Series containing the ATR values.\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" df['high_low'] = df['high'] - df['low']\n",
|
|||
|
|
" df['high_close_prev'] = np.abs(df['high'] - df['close'].shift(1))\n",
|
|||
|
|
" df['low_close_prev'] = np.abs(df['low'] - df['close'].shift(1))\n",
|
|||
|
|
"\n",
|
|||
|
|
" df['true_range'] = df[['high_low', 'high_close_prev', 'low_close_prev']].max(axis=1)\n",
|
|||
|
|
" df['atr'] = df['true_range'].rolling(period).mean()\n",
|
|||
|
|
"\n",
|
|||
|
|
" return df['atr']\n",
|
|||
|
|
"\n",
|
|||
|
|
"\n",
|
|||
|
|
"def analyze_atr_data(df):\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" Loads data from a CSV file and performs a statistical analysis of its ATR.\n",
|
|||
|
|
"\n",
|
|||
|
|
" Args:\n",
|
|||
|
|
" file_path (str): The path to the CSV file.\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" try:\n",
|
|||
|
|
" # Step 1: Read the data\n",
|
|||
|
|
" print(\"Data loaded successfully. First 5 rows:\")\n",
|
|||
|
|
" print(df.head())\n",
|
|||
|
|
" print(\"-\" * 50)\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Step 2: Calculate ATR\n",
|
|||
|
|
" df['atr'] = calculate_atr(df)\n",
|
|||
|
|
" df.dropna(inplace=True)\n",
|
|||
|
|
" print(\"ATR calculation complete. First 5 ATR values:\")\n",
|
|||
|
|
" print(df['atr'].head())\n",
|
|||
|
|
" print(\"-\" * 50)\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Step 3: Descriptive Statistical Analysis\n",
|
|||
|
|
" print(\"Descriptive Statistics for ATR:\")\n",
|
|||
|
|
" print(df['atr'].describe())\n",
|
|||
|
|
"\n",
|
|||
|
|
" skewness = df['atr'].skew()\n",
|
|||
|
|
" kurtosis = df['atr'].kurt()\n",
|
|||
|
|
" print(f\"\\nSkewness: {skewness:.4f}\")\n",
|
|||
|
|
" print(f\"Kurtosis: {kurtosis:.4f}\")\n",
|
|||
|
|
" print(\"-\" * 50)\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Step 4: Visualization\n",
|
|||
|
|
" sns.set_style(\"whitegrid\")\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Plot price vs. ATR\n",
|
|||
|
|
" fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(15, 10), sharex=True)\n",
|
|||
|
|
"\n",
|
|||
|
|
" ax1.plot(df.index, df['close'], label='Close Price', color='blue')\n",
|
|||
|
|
" ax1.set_title('Price Movement', fontsize=16)\n",
|
|||
|
|
" ax1.set_ylabel('Price')\n",
|
|||
|
|
" ax1.legend()\n",
|
|||
|
|
"\n",
|
|||
|
|
" ax2.plot(df.index, df['atr'], label=f'ATR(14)', color='red')\n",
|
|||
|
|
" ax2.set_title('Average True Range (ATR)', fontsize=16)\n",
|
|||
|
|
" ax2.set_xlabel('Date')\n",
|
|||
|
|
" ax2.set_ylabel('ATR Value')\n",
|
|||
|
|
" ax2.legend()\n",
|
|||
|
|
"\n",
|
|||
|
|
" plt.tight_layout()\n",
|
|||
|
|
" plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Plot ATR distribution (Histogram & KDE)\n",
|
|||
|
|
" plt.figure(figsize=(10, 6))\n",
|
|||
|
|
" sns.histplot(df['atr'], kde=True, bins=50, color='purple')\n",
|
|||
|
|
" plt.title('Distribution of ATR Values', fontsize=16)\n",
|
|||
|
|
" plt.xlabel('ATR Value')\n",
|
|||
|
|
" plt.ylabel('Frequency')\n",
|
|||
|
|
" plt.show()\n",
|
|||
|
|
"\n",
|
|||
|
|
" except FileNotFoundError:\n",
|
|||
|
|
" print(f\"Error: The file was not found. Please check the path: {file_path}\")\n",
|
|||
|
|
" except Exception as e:\n",
|
|||
|
|
" print(f\"An error occurred: {e}\")\n",
|
|||
|
|
"\n",
|
|||
|
|
"\n",
|
|||
|
|
"# Call the analysis function with your file path\n",
|
|||
|
|
"# Replace 'your_data.csv' with the actual path to your file\n",
|
|||
|
|
"analyze_atr_data(df_raw)"
|
|||
|
|
],
|
|||
|
|
"id": "f07c10b87eaa78ec",
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"Data loaded successfully. First 5 rows:\n",
|
|||
|
|
" open high low close volume open_oi \\\n",
|
|||
|
|
"datetime \n",
|
|||
|
|
"2020-12-31 14:45:00 4352.0 4400.0 4345.0 4388.0 213731.0 1221661.0 \n",
|
|||
|
|
"2021-01-04 09:00:00 4356.0 4368.0 4309.0 4336.0 338332.0 1217327.0 \n",
|
|||
|
|
"2021-01-04 09:15:00 4336.0 4342.0 4307.0 4318.0 144479.0 1197881.0 \n",
|
|||
|
|
"2021-01-04 09:30:00 4318.0 4329.0 4312.0 4317.0 85679.0 1194567.0 \n",
|
|||
|
|
"2021-01-04 09:45:00 4317.0 4338.0 4316.0 4338.0 66461.0 1194592.0 \n",
|
|||
|
|
"\n",
|
|||
|
|
" close_oi underlying_symbol \n",
|
|||
|
|
"datetime \n",
|
|||
|
|
"2020-12-31 14:45:00 1217327.0 SHFE.rb2105 \n",
|
|||
|
|
"2021-01-04 09:00:00 1197881.0 SHFE.rb2105 \n",
|
|||
|
|
"2021-01-04 09:15:00 1194567.0 SHFE.rb2105 \n",
|
|||
|
|
"2021-01-04 09:30:00 1194592.0 SHFE.rb2105 \n",
|
|||
|
|
"2021-01-04 09:45:00 1198035.0 SHFE.rb2105 \n",
|
|||
|
|
"--------------------------------------------------\n",
|
|||
|
|
"ATR calculation complete. First 5 ATR values:\n",
|
|||
|
|
"datetime\n",
|
|||
|
|
"2021-01-04 14:15:00 28.000000\n",
|
|||
|
|
"2021-01-04 14:30:00 26.000000\n",
|
|||
|
|
"2021-01-04 14:45:00 21.500000\n",
|
|||
|
|
"2021-01-04 21:00:00 21.785714\n",
|
|||
|
|
"2021-01-04 21:15:00 21.571429\n",
|
|||
|
|
"Name: atr, dtype: float64\n",
|
|||
|
|
"--------------------------------------------------\n",
|
|||
|
|
"Descriptive Statistics for ATR:\n",
|
|||
|
|
"count 25457.000000\n",
|
|||
|
|
"mean 17.103424\n",
|
|||
|
|
"std 9.116101\n",
|
|||
|
|
"min 2.214286\n",
|
|||
|
|
"25% 10.428571\n",
|
|||
|
|
"50% 14.642857\n",
|
|||
|
|
"75% 21.714286\n",
|
|||
|
|
"max 77.500000\n",
|
|||
|
|
"Name: atr, dtype: float64\n",
|
|||
|
|
"\n",
|
|||
|
|
"Skewness: 1.5228\n",
|
|||
|
|
"Kurtosis: 3.2215\n",
|
|||
|
|
"--------------------------------------------------\n"
|
|||
|
|
]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1500x1000 with 2 Axes>"
|
|||
|
|
],
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAPeCAYAAADj01PlAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XmcVuP/x/H3rO37vtKetEybdglJZElSKdQPZYmsCUkUlUIUooiIiJQtS5bUt4XUVGjRppS0l9ZZf38cZ86517nve+515vV8PHqc61xn+9zN3Ec+5zqfKy47OztbAAAAAAAAAADARXykAwAAAAAAAAAAIFqRRAcAAAAAAAAAwAOS6AAAAAAAAAAAeEASHQAAAAAAAAAAD0iiAwAAAAAAAADgAUl0AAAAAAAAAAA8IIkOAAAAAAAAAIAHJNEBAAAAAAAAAPCAJDoAAAAAAAAAAB6QRAcAAEBMuvDCC9WgQQOHP40bN9YFF1yge+65R6tWrQrovCNGjFCDBg00b968IEecd/PmzXP4rAcPHvS4b1pamtq0aZOz/8svvxzGSAEAAID8gyQ6AAAAYlqLFi3Us2dP9ezZU+eff76ysrK0cOFCDRgwQDNnzox0eCGTnp6uBQsWeNz+zTff6MiRI+ELCG5NmTJFDRo00JQpUyIdCgAAAAJEEh0AAAAxrXfv3ho/frzGjx+vl19+Wd98842uvvpqZWdna+LEidq+fbtf57vvvvv0xRdfqGvXriGKOO8aNGigpKQkr6PlP/roI0lSkyZNwhUWAAAAkC+RRAcAAEC+UqhQIY0aNUpFixZVZmamvvnmG7+Or1ixourUqaMSJUqEKMK8K1u2rLp06aI//vhDa9euddm+Z88eLV++XM2aNVPdunUjECEAAACQf5BEBwAAQL5TrFgx1apVS5L0119/5fSb9cElY6R2nz591LJlSzVo0CBnv9xqov/666966KGHdOGFF6pJkyY677zzdOWVV2rChAnavXu3y/7//POPxo0bp+7du6tZs2Zq3ry5evXqpXfeeUcZGRkBf8ZevXrlfA5n8+bNU1ZWVs4+3ixZskRDhgxRu3bt1LhxY3Xs2FH33HOP1q9f77BfZmamzj//fDVo0ECpqakezzdhwgQ1aNBATz/9tMu25cuXa+jQoerYsaMaN26sdu3a6c4779SaNWvcnsv+81qwYIGuvfZaNW/eXG3bttV9992nPXv2SJKys7P1zjvv6KqrrlJKSoratGmjESNGeK0Zv337do0aNUoXX3yxmjRpopYtW6p///4eS+TccMMNatCggVauXKkNGzZo6NChatOmjRo3bqzLLrtMb7zxhrKzs13inzp1qiRp6tSpDvX7R4wY4TE2AAAARBeS6AAAAMiXjh8/LklKTk522TZmzBiNHDlSCQkJuuCCC9SsWTPFxcXles4ZM2aod+/emj9/vpKSknTRRRepRYsWysjI0BtvvKGVK1c67P/zzz/riiuu0JtvvqkzZ86offv2atGihXbt2qUxY8ZoyJAhSk9PD+jzderUSRUrVtTnn3+u06dP5/RnZ2dr3rx5KlKkiC6//HKv55g8ebJuueUWLV68WGeffba6deum8uXLa+HCherTp48+/PDDnH0TEhJ09dVXS5LHBwwZGRn65JNPJMklgT9hwgQNHDhQ3377rapUqaKLLrpINWrU0Lfffqv+/fu7fRhgevbZZ/XII4+oWLFi6tSpk4oUKaLPP/9c119/vY4ePap7771XzzzzjCpUqKCOHTsqISFBH3/8sQYNGqS0tDSX8y1cuFBXXXWV3n//fSUlJalz585q3Lixfv/9dw0fPlwPP/ywx1iWLl2q3r17a9u2berQoYOaN2+uHTt2aMKECS4PDnr27KmGDRtKkho2bJhTu79nz55q2bKlx2sAAAAguiRGOgAAAAAg2DZu3Khdu3ZJks455xyX7fPnz9d7772nlJQUn8/57bffauLEiSpUqJDGjx+vyy67zGH7li1bHBLx+/fv19ChQ3Xs2DE9/vjj6tu3r+LjjTEshw8f1j333KOlS5fq1Vdf1dChQ/3+jAkJCerZs6deffVVffnllzkJ7uXLl2v37t26+uqrVbx4cY/H//jjj3rllVdUqFAhvfLKK+rQoUPOtrlz52rkyJEaPXq0mjVrpnr16kmSrrnmGr366qtauHChHn30URUqVMjhnIsXL9aBAwd07rnn5owgl6QPPvhAb7zxhs466yy9+OKLOYllyXjQMGTIED3++ONq2bKlzj77bJdYP/jgA3300Uc5x50+fVr/93//p19++UUDBgzQ6dOntXDhQlWrVk2SdOjQIfXt21ebNm3Sl19+qSuvvDLnXJs2bdLw4cMVFxenKVOm6JJLLsnZtnv3bt12222aN2+e2rRpk/N3avfaa6/piSeeUN++fXP6li9frkGDBmn27Nm6+eabVblyZUnS+PHjNWXKFG3cuFEXX3yx7rrrLo8/DwAAAEQvRqIDAAAg3/j333+1ePFi3XXXXcrKylLFihXVvXt3l/3+7//+z68EuiRNmTJFknTvvfe6JNAlqW7duqpTp07O+ltvvaUjR46of//+uv7663MS6JJUpkwZPfPMM0pKStLs2bNdyoD46pprrpHkWNLFHD2eWymXN954Q5J0/fXXOyTQJWOy1i5duig9PV2zZs3K6T/77LPVqlUrHTt2zG2teXOEuv3aWVlZOX93zz33nEMCXZJat26tO+64Q+np6Xr//ffdxnr33Xc7HFe4cGENGjRIkrR582aNHDkyJ4EuGTXj+/XrJ8lIcNtNmzZNaWlpuueeexwS6JJUrVo1PfXUU5Lk8LntLrnkEocEuiS1a9dOHTt2VGZmplasWOH2OAAAAMQukugAAACIaQ8//HBOnelWrVpp8ODB2rlzp2rWrKnp06eraNGiLsdceumlfl1j//792rBhg+Lj43Xttdf6dMzixYslyW0SX5IqVaqks846S4cOHdKOHTv8isdkJrV//vln7dq1S0ePHtWiRYtUs2ZNtW7d2uNxGRkZWr16tSSj5Ig75ud0LlFjJu4//vhjh/5Dhw5p8eLFSk5OVo8ePXL6f//9d+3bt081a9ZU48aN3V7rvPPOkySPtdE7d+7s0nfWWWdJkhITE10eAti379u3L6cvKytLP/74oyS5fRAiSU2aNFHRokW1YcMGnTlzxmV7ly5d3B5nPkCxXw8AAAD5A+VcAAAAENNatGiRkzBNSkpS2bJllZKSok6dOikx0f0/d+2jln3x999/S5IqVKigEiVK+HSMWU6mf//+ue576NChnIlQ/dWrVy+tWrVKH330kSpUqKAzZ87ommuu8Vrj/ciRIzkJ4urVq7vdp0aNGpKMiVHtunfvrrFjx2rZsmXau3dvTumSTz75ROnp6brssstUqlSpnP3Nv4edO3c6lHhx59ChQ277q1at6tJnPhypUKGC259zsWLFJMmhJvqRI0dyauW7S8w7O3LkiCpVquTQV6VKFbf7mqVz3CXeAQAAENtIogMAACCm9e7dO2d0tK8KFy4comgsWVlZkqRu3bq5HQ1vV7p06YCvc+mll2rs2LGaP3++Spcurfj4eI+jy4OhaNGi6t69uz766CPNnz9ft912mySrlIvzz8IsVWNO+ulNmTJl3PbbS+H4s82Z+TORPI/At0tKSsrT9QAAAJA/kEQHAAAAcmGOPt6/f7/+/fdfn0ajV6lSRTt27NCtt96qJk2ahCw2M6n94Ycf6u+//1anTp1yRod7Urp0aSUnJystLU27du1yqVMuWSPInUdiS0ai/KOPPtLHH3+s2267Tb/99ps2bdqkypUru5RWMWMpXbq0xo8fH+jHDIoyZcqocOHCOn36tIYPH66yZctGNB4AAADEBoZRAAAAALmoUKGCGjZsqKysLIdJPL3p1KmTJGnhwoWhDE2SMRq/dOnSKl26tK677rpc909MTFTLli0ludY2N5mfs02bNi7bWrVqpbPPPls7duzQL7/8kjMK/eqrr3YZqd2kSROVKVNGW7Zs0R9//OHX5wq2hIQEtW/fXlJ4fi6SNZo9IyMjLNc
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"data": {
|
|||
|
|
"text/plain": [
|
|||
|
|
"<Figure size 1000x600 with 1 Axes>"
|
|||
|
|
],
|
|||
|
|
"image/png": "iVBORw0KGgoAAAANSUhEUgAAA1sAAAIlCAYAAAAjY+IAAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAAitxJREFUeJzs3Xd8VfX9x/HXndk7IWFvAoGwZchSBGdVhj9HEReK1lpt1bpqVayKWm2drYiI4kILqCjgFkQB2TvsvUIG2fve+/sj3iuRAMkluefe5P3sIw+Sc8/33s/95hTvm+84JpfL5UJERERERETqlNnoAkRERERERBoihS0REREREZF6oLAlIiIiIiJSDxS2RERERERE6oHCloiIiIiISD1Q2BIREREREakHClsiIiIiIiL1QGFLRERERESkHihsiYiIiIiI1AOr0QWIiBhl+PDhHDx40POzyWQiJCSEiIgIWrduTbdu3bjooovo3r37SZ9j/PjxLF++nBkzZtC/f39flH1K7vf07bff0qJFC89xf6sT4IEHHuDjjz9m8uTJjBkzxuhy6tx3333HG2+8wZYtWygsLATwqv8vu+wytm7dis1mY/HixcTExHgee/nll3nllVdqXZu7Dvfv4HgWi4WIiAg6duzIRRddxJVXXonNZqvR8zqdTkaMGMHBgwd57LHHuOaaa07b5g9/+APfffcd11xzDY899lit3wtAcnIyAFu3bvWqvYhIfVHYEpFGr3fv3rRu3RqAkpISjh07RlpaGsuXL+fNN9+kX79+PPXUU7Rs2bLeajhZSApUc+bM4cEHH2T06NE8/fTTRpfjc2lpadx55504nU4GDBhAQkICJpOJ+Pj4Wj3P+vXrPQGivLycuXPncv3113se79KlC6NHjz6h3eLFi8nMzKRz58506dLlhMd/W0erVq3o06cPAKWlpezatYsVK1awYsUKPv/8c6ZPn05wcPBp6zWbzYwZM4aXX36Z2bNnnzZsZWZm8sMPPwBwxRVXnPb5RUQCjcKWiDR6//d//3fCyIrL5eKHH37gqaeeYvny5Vx99dXMnDnzhMD1zDPPUFxcTLNmzXxZ8km99dZblJeXk5iYaHQpp3X33Xdzyy230KRJE6NLqXPffPMN5eXl3HbbbfzlL3/x+nlmzZoFQGJiIunp6cyaNatK2BoxYgQjRow4od348ePJ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
|
|||
|
|
},
|
|||
|
|
"metadata": {},
|
|||
|
|
"output_type": "display_data"
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"execution_count": 54
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"metadata": {
|
|||
|
|
"ExecuteTime": {
|
|||
|
|
"end_time": "2025-09-03T15:44:38.791997Z",
|
|||
|
|
"start_time": "2025-09-03T15:44:35.577884Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"source": [
|
|||
|
|
"import numpy as np\n",
|
|||
|
|
"import pandas as pd\n",
|
|||
|
|
"import talib\n",
|
|||
|
|
"from statsmodels.stats.diagnostic import het_arch\n",
|
|||
|
|
"from statsmodels.tsa.stattools import adfuller, acf\n",
|
|||
|
|
"\n",
|
|||
|
|
"\n",
|
|||
|
|
"def slice_atr_stats(df_raw: pd.DataFrame,\n",
|
|||
|
|
" n: int) -> pd.Series:\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" 计算滚动长度为 n 的 ATR 序列,并返回一组统计学指标\n",
|
|||
|
|
" 参数\n",
|
|||
|
|
" ----\n",
|
|||
|
|
" df_raw : 必须包含 high, low, close\n",
|
|||
|
|
" n : 回看窗口(=ATR 周期)\n",
|
|||
|
|
"\n",
|
|||
|
|
" 返回\n",
|
|||
|
|
" ----\n",
|
|||
|
|
" pd.Series : 各统计量\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" # 1. 计算 ATR\n",
|
|||
|
|
" atr = talib.ATR(df_raw['high'].values,\n",
|
|||
|
|
" df_raw['low'].values,\n",
|
|||
|
|
" df_raw['close'].values,\n",
|
|||
|
|
" timeperiod=n)\n",
|
|||
|
|
"\n",
|
|||
|
|
" atr = atr[~np.isnan(atr)]\n",
|
|||
|
|
" if len(atr) < 10:\n",
|
|||
|
|
" raise ValueError(f\"有效 ATR 样本数 {len(atr)} 不足\")\n",
|
|||
|
|
"\n",
|
|||
|
|
" # 2. 基本描述\n",
|
|||
|
|
" stats_dict = {\n",
|
|||
|
|
" 'n': n,\n",
|
|||
|
|
" 'mean': np.mean(atr),\n",
|
|||
|
|
" 'std': np.std(atr, ddof=1),\n",
|
|||
|
|
" 'sem': np.std(atr, ddof=1) / np.sqrt(len(atr)), # 标准误\n",
|
|||
|
|
" 'skew': pd.Series(atr).skew(),\n",
|
|||
|
|
" 'kurt': pd.Series(atr).kurtosis(),\n",
|
|||
|
|
" 'q05': np.percentile(atr, 5),\n",
|
|||
|
|
" 'q25': np.percentile(atr, 25),\n",
|
|||
|
|
" 'q50': np.percentile(atr, 50),\n",
|
|||
|
|
" 'q75': np.percentile(atr, 75),\n",
|
|||
|
|
" 'q95': np.percentile(atr, 95),\n",
|
|||
|
|
" }\n",
|
|||
|
|
"\n",
|
|||
|
|
" # 3. 自相关(滞后 1)\n",
|
|||
|
|
" stats_dict['acf_1'] = acf(atr, nlags=1, fft=False)[1]\n",
|
|||
|
|
"\n",
|
|||
|
|
" # 4. ARCH-LM 检验(是否存在波动聚集)\n",
|
|||
|
|
" arch_lm, arch_pval, _, _ = het_arch(atr, maxlag=5)\n",
|
|||
|
|
" stats_dict['arch_lm'] = arch_lm\n",
|
|||
|
|
" stats_dict['arch_pval'] = arch_pval\n",
|
|||
|
|
"\n",
|
|||
|
|
" # 5. ADF 单位根检验(是否平稳)\n",
|
|||
|
|
" adf_res = adfuller(atr, regression='c')\n",
|
|||
|
|
" stats_dict['adf_stat'] = adf_res[0]\n",
|
|||
|
|
" stats_dict['adf_pval'] = adf_res[1]\n",
|
|||
|
|
"\n",
|
|||
|
|
" # 6. 重尾指数(Hill 估计,尾部 5%)\n",
|
|||
|
|
" tail = sorted(atr)[-int(np.ceil(0.05 * len(atr))):]\n",
|
|||
|
|
" hill = len(tail) / np.sum(np.log(tail) - np.log(min(tail)))\n",
|
|||
|
|
" stats_dict['hill_tail'] = hill\n",
|
|||
|
|
"\n",
|
|||
|
|
" return pd.Series(stats_dict)\n",
|
|||
|
|
"\n",
|
|||
|
|
"\n",
|
|||
|
|
"# 假设 df_raw 已准备好\n",
|
|||
|
|
"for n in [10, 20, 30, 50]:\n",
|
|||
|
|
" try:\n",
|
|||
|
|
" res = slice_atr_stats(df_raw, n)\n",
|
|||
|
|
" print(res.to_frame().T.to_string(index=False))\n",
|
|||
|
|
" except ValueError as e:\n",
|
|||
|
|
" print(e)"
|
|||
|
|
],
|
|||
|
|
"id": "8b2055ca0d08510e",
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
" n mean std sem skew kurt q05 q25 q50 q75 q95 acf_1 arch_lm arch_pval adf_stat adf_pval hill_tail\n",
|
|||
|
|
"10.0 17.10203 8.924275 0.055944 1.424996 2.548368 7.386456 10.430459 14.681663 21.737424 34.709991 0.993183 24917.586967 0.0 -4.412162 0.000282 5.503068\n",
|
|||
|
|
" n mean std sem skew kurt q05 q25 q50 q75 q95 acf_1 arch_lm arch_pval adf_stat adf_pval hill_tail\n",
|
|||
|
|
"20.0 17.105253 8.65925 0.054293 1.297106 1.728398 7.605461 10.433439 14.749979 21.922722 34.407901 0.998188 25301.321873 0.0 -3.649219 0.004887 6.040336\n",
|
|||
|
|
" n mean std sem skew kurt q05 q25 q50 q75 q95 acf_1 arch_lm arch_pval adf_stat adf_pval hill_tail\n",
|
|||
|
|
"30.0 17.108363 8.552993 0.053638 1.249836 1.45459 7.662073 10.471382 14.714624 22.060539 34.168723 0.999162 25363.812059 0.0 -3.175812 0.021428 6.161612\n",
|
|||
|
|
" n mean std sem skew kurt q05 q25 q50 q75 q95 acf_1 arch_lm arch_pval adf_stat adf_pval hill_tail\n",
|
|||
|
|
"50.0 17.114221 8.440261 0.052952 1.203796 1.207939 7.791217 10.519432 14.735442 22.179905 33.70525 0.999676 25380.904971 0.0 -2.645793 0.083897 6.067103\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"execution_count": 55
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
"metadata": {
|
|||
|
|
"ExecuteTime": {
|
|||
|
|
"end_time": "2025-09-03T15:46:25.613295Z",
|
|||
|
|
"start_time": "2025-09-03T15:46:23.569085Z"
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
"cell_type": "code",
|
|||
|
|
"source": [
|
|||
|
|
"import numpy as np\n",
|
|||
|
|
"import pandas as pd\n",
|
|||
|
|
"from scipy.stats import jarque_bera, bootstrap\n",
|
|||
|
|
"\n",
|
|||
|
|
"def vol_momentum_events(df: pd.DataFrame,\n",
|
|||
|
|
" vol_window: int = 100,\n",
|
|||
|
|
" vol_q: float = 0.50,\n",
|
|||
|
|
" sign_window: int = 5):\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" 返回事件索引列表 [(idx, direction)]\n",
|
|||
|
|
" direction = 1 多头 -1 空头\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" ret = np.log(df['close'] / df['close'].shift(1))\n",
|
|||
|
|
" vol = ret.abs()\n",
|
|||
|
|
"\n",
|
|||
|
|
" # 滚动分位\n",
|
|||
|
|
" low_vol = vol < vol.rolling(vol_window).quantile(vol_q)\n",
|
|||
|
|
" # 同向漂移:最近 sign_window 根 K 线方向一致\n",
|
|||
|
|
" sign_sum = np.sign(ret).rolling(sign_window).sum()\n",
|
|||
|
|
" same_sign = np.abs(sign_sum) == sign_window\n",
|
|||
|
|
"\n",
|
|||
|
|
" mask = low_vol & same_sign\n",
|
|||
|
|
" events = [(df.index[i], int(np.sign(ret.iloc[i])))\n",
|
|||
|
|
" for i in np.where(mask)[0]]\n",
|
|||
|
|
" return events\n",
|
|||
|
|
"\n",
|
|||
|
|
"\n",
|
|||
|
|
"def backtest_vol_momentum(df: pd.DataFrame,\n",
|
|||
|
|
" events: list,\n",
|
|||
|
|
" hold_bars: int = 10):\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" 返回收益序列\n",
|
|||
|
|
" \"\"\"\n",
|
|||
|
|
" rets = []\n",
|
|||
|
|
" for ts, direction in events:\n",
|
|||
|
|
" idx = df.index.get_loc(ts)\n",
|
|||
|
|
" if idx + hold_bars >= len(df):\n",
|
|||
|
|
" continue\n",
|
|||
|
|
" start_price = df.at[ts, 'close']\n",
|
|||
|
|
" end_price = df.iloc[idx + hold_bars]['close']\n",
|
|||
|
|
" rets.append((end_price - start_price) * direction)\n",
|
|||
|
|
" return np.array(rets)\n",
|
|||
|
|
"\n",
|
|||
|
|
"\n",
|
|||
|
|
"def validate_vol_momentum(df_raw: pd.DataFrame,\n",
|
|||
|
|
" vol_window: int = 100,\n",
|
|||
|
|
" vol_q: float = 0.85,\n",
|
|||
|
|
" sign_window: int = 2,\n",
|
|||
|
|
" hold_bars: int = 10,\n",
|
|||
|
|
" bootstrap_resamples: int = 9999):\n",
|
|||
|
|
" events = vol_momentum_events(df_raw, vol_window, vol_q, sign_window)\n",
|
|||
|
|
" if len(events) < 20:\n",
|
|||
|
|
" return {'error': '事件不足 20 个'}\n",
|
|||
|
|
"\n",
|
|||
|
|
" rets = backtest_vol_momentum(df_raw, events, hold_bars)\n",
|
|||
|
|
"\n",
|
|||
|
|
" # 统计量\n",
|
|||
|
|
" win_rate = np.mean(rets > 0)\n",
|
|||
|
|
" mean_ret = np.mean(rets)\n",
|
|||
|
|
" jb_stat, jb_p = jarque_bera(rets)\n",
|
|||
|
|
"\n",
|
|||
|
|
" # Bootstrap 单侧检验 mean>0\n",
|
|||
|
|
" boot_res = bootstrap((rets,), np.mean, n_resamples=bootstrap_resamples,\n",
|
|||
|
|
" random_state=42)\n",
|
|||
|
|
" boot_p = np.mean(boot_res.bootstrap_distribution <= 0)\n",
|
|||
|
|
"\n",
|
|||
|
|
" return {\n",
|
|||
|
|
" 'events': len(rets),\n",
|
|||
|
|
" 'win_rate': win_rate,\n",
|
|||
|
|
" 'mean_ret': mean_ret,\n",
|
|||
|
|
" 'jb_stat': jb_stat,\n",
|
|||
|
|
" 'jb_p': jb_p,\n",
|
|||
|
|
" 'bootstrap_p_one_tail': boot_p\n",
|
|||
|
|
" }\n",
|
|||
|
|
"\n",
|
|||
|
|
"\n",
|
|||
|
|
"# ---------- 一键运行 ----------\n",
|
|||
|
|
"report = validate_vol_momentum(df_raw)\n",
|
|||
|
|
"if 'error' in report:\n",
|
|||
|
|
" print(report['error'])\n",
|
|||
|
|
"else:\n",
|
|||
|
|
" print(f\"事件数: {report['events']}\")\n",
|
|||
|
|
" print(f\"胜率: {report['win_rate']:.2%}\")\n",
|
|||
|
|
" print(f\"平均收益: {report['mean_ret']:.4f}\")\n",
|
|||
|
|
" print(f\"JB p-val: {report['jb_p']:.4f}\")\n",
|
|||
|
|
" print(f\"Bootstrap 单侧 p: {report['bootstrap_p_one_tail']:.4f}\")\n",
|
|||
|
|
" if report['bootstrap_p_one_tail'] < 0.05:\n",
|
|||
|
|
" print(\"→ 收益期望显著 > 0\")\n",
|
|||
|
|
" else:\n",
|
|||
|
|
" print(\"→ 收益期望不显著\")"
|
|||
|
|
],
|
|||
|
|
"id": "5a938217f439a86b",
|
|||
|
|
"outputs": [
|
|||
|
|
{
|
|||
|
|
"name": "stdout",
|
|||
|
|
"output_type": "stream",
|
|||
|
|
"text": [
|
|||
|
|
"事件数: 9191\n",
|
|||
|
|
"胜率: 49.62%\n",
|
|||
|
|
"平均收益: 0.3230\n",
|
|||
|
|
"JB p-val: 0.0000\n",
|
|||
|
|
"Bootstrap 单侧 p: 0.2335\n",
|
|||
|
|
"→ 收益期望不显著\n"
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"execution_count": 60
|
|||
|
|
}
|
|||
|
|
],
|
|||
|
|
"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
|
|||
|
|
}
|