Files
NewQuant/data/ analysis/Volume.ipynb

1013 lines
1.2 MiB
Plaintext
Raw Normal View History

{
"cells": [
{
2025-07-10 15:07:31 +08:00
"cell_type": "code",
"id": "b93c7ca1",
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-28T05:56:00.461676Z",
"start_time": "2025-07-28T05:56:00.200161Z"
}
},
"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@DCE_c/KQ_m@DCE_c_min60.csv'\n"
],
"outputs": [],
"execution_count": 1
},
{
"cell_type": "code",
"id": "60a48bac",
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-28T05:56:00.484493Z",
"start_time": "2025-07-28T05:56:00.475015Z"
}
},
"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",
"# --- 2. Stationary Indicator Calculation Function ---\n",
"def calculate_stationary_indicators(df, volume_window=10, price_lag=5):\n",
" \"\"\"\n",
" Calculates stationary indicators based on volume and price.\n",
"\n",
" Parameters:\n",
" df (pd.DataFrame): K-line data containing 'close' and 'volume' columns.\n",
" volume_window (int): Window size for calculating volume indicators (e.g., 10 for the past 10 periods' average volume).\n",
" price_lag (int): Lag period for calculating future returns (e.g., 5 for future 5 periods' returns).\n",
" \"\"\"\n",
" df_processed = df.copy()\n",
"\n",
" # --- Stationary Volume Indicators ---\n",
" # 1. Volume Rate of Change (VROC)\n",
" df_processed['volume_roc'] = df_processed['volume'].pct_change(volume_window) * 100\n",
"\n",
" # 2. Volume to Moving Average Ratio\n",
" df_processed['volume_ma_ratio'] = df_processed['volume'] / df_processed['volume'].rolling(window=volume_window).mean()\n",
"\n",
" # 3. Normalized Volume (Z-score standardization)\n",
" # Using rolling mean and rolling standard deviation to avoid look-ahead bias and ensure local stationarity\n",
" rolling_mean_vol = df_processed['volume'].rolling(window=volume_window).mean()\n",
" rolling_std_vol = df_processed['volume'].rolling(window=volume_window).std()\n",
" # Avoid division by zero\n",
" df_processed['volume_normalized_zscore'] = (df_processed['volume'] - rolling_mean_vol) / rolling_std_vol.replace(0, np.nan)\n",
"\n",
" # --- Stationary Price Indicators ---\n",
" # 1. Current Period Log Return\n",
" df_processed['log_return'] = np.log(df_processed['close'] / df_processed['close'].shift(1))\n",
"\n",
" # 2. Future N-period Log Return (Our target variable for research)\n",
" # shift(-price_lag) moves future data up to align with the current row for future return calculation\n",
" df_processed['future_log_return'] = np.log(df_processed['close'].shift(-price_lag) / df_processed['close'])\n",
"\n",
" # 3. MACD Histogram Difference (Measures momentum change rate, potentially capturing trend initiation)\n",
" try:\n",
" macd, macdsignal, macdhist = ta.MACD(df_processed['close'], fastperiod=12, slowperiod=26, signalperiod=9)\n",
" df_processed['macd_hist_diff'] = macdhist.diff(1)\n",
" except Exception as e:\n",
" print(f\"TA-Lib MACD calculation failed, possibly due to installation or data issues: {e}. 'macd_hist_diff' will contain NaN.\")\n",
" df_processed['macd_hist_diff'] = np.nan\n",
"\n",
" # Drop rows with NaN values resulting from rolling windows and shift operations\n",
" df_processed.dropna(inplace=True)\n",
" if df_processed.empty:\n",
" print(\"Warning: Data is empty after indicator calculation. Check original data volume or adjust window parameters.\")\n",
" else:\n",
" print(f\"Indicators calculated. {len(df_processed)} rows of data remaining for analysis.\")\n",
" return df_processed\n",
"\n",
"# --- 3. Analysis and Visualization Function ---\n",
"def analyze_and_visualize(processed_df):\n",
" if processed_df.empty:\n",
" print(\"No data available for analysis. Please check data loading and indicator calculation steps.\")\n",
" return\n",
"\n",
" print(\"\\n--- Statistical Description of Indicators ---\")\n",
" print(processed_df[['volume_roc', 'volume_ma_ratio', 'volume_normalized_zscore',\n",
" 'log_return', 'future_log_return']].describe())\n",
"\n",
" # --- Correlation Analysis ---\n",
" print(\"\\n--- Correlation between Volume Indicators and Future Returns ---\")\n",
" volume_indicators = ['volume_roc', 'volume_ma_ratio', 'volume_normalized_zscore']\n",
" for indicator in volume_indicators:\n",
" if indicator in processed_df.columns and 'future_log_return' in processed_df.columns:\n",
" correlation = processed_df[indicator].corr(processed_df['future_log_return'])\n",
" print(f\"Correlation between '{indicator}' and 'future_log_return': {correlation:.4f}\")\n",
" else:\n",
" print(f\"Column '{indicator}' or 'future_log_return' does not exist. Skipping correlation calculation.\")\n",
"\n",
" # Plot correlation heatmap\n",
" plt.figure(figsize=(9, 7))\n",
" sns.heatmap(processed_df[volume_indicators + ['future_log_return']].corr(), annot=True, cmap='coolwarm', fmt=\".2f\")\n",
" plt.title('Correlation Matrix: Volume Indicators vs. Future Log Returns', fontsize=16)\n",
" plt.show()\n",
"\n",
" # --- Conditional Analysis: Future Returns based on Volume Anomaly ---\n",
" # Define thresholds for abnormal volume (using quantiles of Z-score to adapt dynamically)\n",
" if 'volume_normalized_zscore' in processed_df.columns:\n",
" low_vol_threshold = processed_df['volume_normalized_zscore'].quantile(0.2)\n",
" high_vol_threshold = processed_df['volume_normalized_zscore'].quantile(0.8)\n",
"\n",
" def categorize_volume(zscore):\n",
" if zscore <= low_vol_threshold:\n",
" return 'Low Volume'\n",
" elif zscore >= high_vol_threshold:\n",
" return 'High Volume'\n",
" else:\n",
" return 'Normal Volume'\n",
"\n",
" processed_df['volume_category'] = processed_df['volume_normalized_zscore'].apply(categorize_volume)\n",
"\n",
" print(\"\\n--- Statistics of Future Log Returns by Volume Category ---\")\n",
" print(processed_df.groupby('volume_category')['future_log_return'].describe())\n",
"\n",
" # Plot box plot of future returns by volume category\n",
" plt.figure(figsize=(10, 6))\n",
" sns.boxplot(x='volume_category', y='future_log_return', data=processed_df, order=['Low Volume', 'Normal Volume', 'High Volume'], palette='viridis')\n",
" plt.title('Distribution of Future Log Returns by Volume Category', fontsize=16)\n",
" plt.xlabel('Volume Category', fontsize=12)\n",
" plt.ylabel('Future Log Return', fontsize=12)\n",
" plt.grid(True, linestyle='--', alpha=0.7)\n",
" plt.show()\n",
"\n",
" # Plot histogram of future returns, categorized by volume\n",
" plt.figure(figsize=(12, 7))\n",
" sns.histplot(data=processed_df, x='future_log_return', hue='volume_category', kde=True, bins=70,\n",
" palette={'Low Volume': 'red', 'Normal Volume': 'blue', 'High Volume': 'green'},\n",
" alpha=0.6, line_kws={'linewidth':2})\n",
" plt.title('Distribution of Future Log Returns by Volume Category', fontsize=16)\n",
" plt.xlabel('Future Log Return', fontsize=12)\n",
" plt.ylabel('Frequency', fontsize=12)\n",
" plt.grid(True, linestyle='--', alpha=0.7)\n",
" plt.show()\n",
" else:\n",
" print(\"Column 'volume_normalized_zscore' not found. Skipping volume category analysis.\")\n",
"\n",
"\n",
" # --- Price Chart with Indicator Overlay (Simplified to line plot; consider mplfinance for OHLC charts) ---\n",
" print(\"\\n--- Price Chart with Volume Indicator Overlay ---\")\n",
" # Select a segment of data for visualization, ensuring sufficient data points\n",
" if len(processed_df) > 100: # Need at least 100 data points to select a segment\n",
" sample_size = min(200, len(processed_df) // 2) # Show max 200 data points or half of data\n",
" plot_df = processed_df.sample(n=sample_size, random_state=42).sort_index() # Randomly sample and sort to maintain time continuity\n",
" else:\n",
" plot_df = processed_df.copy() # If data volume is small, plot all\n",
"\n",
" if not plot_df.empty:\n",
" fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(15, 12), sharex=True, gridspec_kw={'height_ratios': [3, 1, 1]})\n",
"\n",
" # Subplot 1: Price Trend\n",
" ax1.plot(plot_df.index, plot_df['close'], label='Close Price', color='blue', linewidth=1.5)\n",
" ax1.set_title(f'Futures Price Trend, Normalized Volume, and Future Returns (Sample Period: {plot_df.index.min().strftime(\"%Y-%m-%d %H:%M\")} to {plot_df.index.max().strftime(\"%Y-%m-%d %H:%M\")})', fontsize=16)\n",
" ax1.set_ylabel('Price', fontsize=12)\n",
" ax1.grid(True, linestyle='--', alpha=0.6)\n",
" ax1.legend()\n",
"\n",
" # Subplot 2: Normalized Volume Indicator\n",
" ax2.bar(plot_df.index, plot_df['volume_normalized_zscore'], color='grey', alpha=0.7, label='Normalized Volume (Z-score)')\n",
" if 'volume_normalized_zscore' in processed_df.columns:\n",
" ax2.axhline(high_vol_threshold, color='green', linestyle='--', linewidth=0.8, label=f'High Vol Threshold ({high_vol_threshold:.2f})')\n",
" ax2.axhline(low_vol_threshold, color='red', linestyle='--', linewidth=0.8, label=f'Low Vol Threshold ({low_vol_threshold:.2f})')\n",
" ax2.set_ylabel('Normalized Volume', fontsize=12)\n",
" ax2.grid(True, linestyle='--', alpha=0.6)\n",
" ax2.legend()\n",
"\n",
" # Subplot 3: Future Log Return\n",
" ax3.plot(plot_df.index, plot_df['future_log_return'], label='Future Log Return', color='purple', linewidth=1.5)\n",
" ax3.axhline(0, color='black', linestyle='--', linewidth=0.8) # Zero return line\n",
" ax3.set_ylabel('Future Log Return', fontsize=12)\n",
" ax3.set_xlabel('Time', fontsize=12)\n",
" ax3.grid(True, linestyle='--', alpha=0.6)\n",
" ax3.legend()\n",
"\n",
" plt.tight_layout()\n",
" plt.show()\n",
" else:\n",
" print(\"Selected plot time range has no data. Adjust time range or check data volume.\")\n",
"\n"
],
"outputs": [],
"execution_count": 2
},
{
"cell_type": "code",
"id": "9ab3d054",
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-28T05:56:01.440166Z",
"start_time": "2025-07-28T05:56:00.666866Z"
}
},
"source": [
"\n",
"df_raw = load_and_preprocess_data(file_path)\n",
"\n",
"if df_raw is not None and not df_raw.empty:\n",
" # 您可以在这里调整 volume_window 和 price_lag 参数\n",
" # volume_window: 用于计算成交量移动平均的周期例如5分钟K线设置为5表示过去5分钟的平均成交量\n",
" # price_lag: 用于计算未来收益率的周期例如5分钟K线设置为5表示未来5分钟的收益率\n",
" processed_data = calculate_stationary_indicators(\n",
" df_raw, volume_window=10, price_lag=5\n",
" )\n",
" analyze_and_visualize(processed_data)\n",
"else:\n",
" print(\"无法进行分析,请检查数据加载是否成功。\")"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Successfully loaded 7650 rows of data.\n",
"First 5 rows of data:\n",
" open high low close volume open_oi \\\n",
"datetime \n",
"2020-12-31 14:00:00 2735.0 2747.0 2735.0 2742.0 121244.0 1055815.0 \n",
"2021-01-04 09:00:00 2755.0 2779.0 2754.0 2766.0 263228.0 1061496.0 \n",
"2021-01-04 10:00:00 2766.0 2769.0 2758.0 2760.0 128679.0 1090445.0 \n",
"2021-01-04 11:00:00 2760.0 2762.0 2756.0 2760.0 43784.0 1085368.0 \n",
"2021-01-04 13:00:00 2760.0 2760.0 2744.0 2750.0 80449.0 1077012.0 \n",
"\n",
" close_oi underlying_symbol \n",
"datetime \n",
"2020-12-31 14:00:00 1061496.0 DCE.c2105 \n",
"2021-01-04 09:00:00 1090445.0 DCE.c2105 \n",
"2021-01-04 10:00:00 1085368.0 DCE.c2105 \n",
"2021-01-04 11:00:00 1077012.0 DCE.c2105 \n",
"2021-01-04 13:00:00 1063889.0 DCE.c2105 \n",
"Indicators calculated. 7611 rows of data remaining for analysis.\n",
"\n",
"--- Statistical Description of Indicators ---\n",
" volume_roc volume_ma_ratio volume_normalized_zscore log_return \\\n",
"count 7611.000000 7611.000000 7611.000000 7611.000000 \n",
"mean inf 0.987852 -0.015433 -0.000026 \n",
"std NaN 0.620060 0.907105 0.002949 \n",
"min -100.000000 0.000000 -2.086150 -0.029732 \n",
"25% -58.227358 0.517468 -0.717136 -0.001256 \n",
"50% -0.104108 0.828873 -0.258750 0.000000 \n",
"75% 111.716977 1.316306 0.521281 0.001236 \n",
"max inf 4.564579 2.782799 0.040765 \n",
"\n",
" future_log_return \n",
"count 7611.000000 \n",
"mean -0.000136 \n",
"std 0.006511 \n",
"min -0.032185 \n",
"25% -0.003685 \n",
"50% 0.000000 \n",
"75% 0.003544 \n",
"max 0.044555 \n",
"\n",
"--- Correlation between Volume Indicators and Future Returns ---\n",
"Correlation between 'volume_roc' and 'future_log_return': nan\n",
"Correlation between 'volume_ma_ratio' and 'future_log_return': -0.0073\n",
"Correlation between 'volume_normalized_zscore' and 'future_log_return': -0.0120\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 900x700 with 2 Axes>"
],
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- Statistics of Future Log Returns by Volume Category ---\n",
" count mean std min 25% 50% \\\n",
"volume_category \n",
"High Volume 1523.0 -0.000177 0.005880 -0.030478 -0.003314 -0.00036 \n",
"Low Volume 1523.0 0.000155 0.006734 -0.028681 -0.003800 0.00000 \n",
"Normal Volume 4565.0 -0.000220 0.006634 -0.032185 -0.003786 0.00000 \n",
"\n",
" 75% max \n",
"volume_category \n",
"High Volume 0.003328 0.029915 \n",
"Low Volume 0.004136 0.022700 \n",
"Normal Volume 0.003462 0.044555 \n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1000x600 with 1 Axes>"
],
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/plain": [
"<Figure size 1200x700 with 1 Axes>"
],
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- Price Chart with Volume Indicator Overlay ---\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1500x1200 with 3 Axes>"
],
"image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAASlCAYAAABHkZBpAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XdYU9f/B/B3wlAQFUXAhda9QEXco7ZqXa1WrXVUrVpbR+tqtdZV69621tE6vkrdrXXX2WqddW9RceDCiaKICMhIfn/c3w0EErgnk8D79Tw+uV4yzk3euUk+99xzVFqtVgsiIiIiIiIiIiIiIkpHbe8GEBERERERERERERFlVSyiExEREREREREREREZwSI6EREREREREREREZERLKITERERERERERERERnBIjoRERERERERERERkREsohMRERERERERERERGcEiOhERERERERERERGRESyiExEREREREREREREZwSI6EREREREREREREZERzvZuAGUdTZo0wYMHDzK8zqhRo9CrVy/bNCgbOnHiBD799NN0693d3eHn54e3334bvXv3hpeXl/B9V6hQAQBw7do1s9tpKUoylVaxYsXw77//WqlFltejRw+cPHkSK1euRJ06dcy+v/nz52PBggUAgHfeeQeLFy82eL2tW7dixIgRqF27NlatWmX24zoa+b1kaPuz4ntBtmnTJowaNQrt27fH9OnTM7yuRqNBs2bN8ODBA4wfPx5du3bN9P4HDBiAf//9F127dsX48eNNamNWfv5IIu8nBg4ciEGDBim6jbHPn7ROnTqFfPnymdvEbCOjz+2iRYuiXr166NWrF4oXL26H1mVvsbGxaNWqFfLnz48tW7ZArdbv+3P58mWsXr0ap0+fxpMnT6BSqVCwYEH4+voiMDAQDRs2RIMGDezUeusx5f1v7mOlplarkTdvXpQtWxatWrVCly5d4OLiYtV2yGz5+XT//n00bdrUot9LHz58iEOHDuHIkSO4fPkynj59ChcXF/j5+eGdd95Br169ULBgQaO3j4mJwZIlS7Bnzx48evQIbm5uqFatGnr37o169eqlu35cXByOHz+Ow4cP4/Tp0wgPD0diYiK8vLxQo0YNdO/eHUFBQQYf69atW/jvv/9w+fJlXL58GWFhYUhOTsaQIUPw5Zdfmv1chISEYMmSJTh9+jRevXoF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
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 3
},
{
"cell_type": "code",
"id": "f13d0294",
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-28T05:56:01.470406Z",
"start_time": "2025-07-28T05:56:01.464039Z"
}
},
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"\n",
"# 无需数据加载和指标计算函数,假设 processed_df 已经传入并包含所需列\n",
"\n",
"def analyze_trend_continuation_probability(processed_df, return_threshold=0.0001, num_bins=20):\n",
" \"\"\"\n",
" Analyzes the probability of trend continuation (direction consistency) as volume change rate (Z-score) varies.\n",
" This version ignores the specific direction (Up/Down) of the trend, focusing only on whether it continues.\n",
" It plots the individual bin probabilities and a bar chart showing the number of data points in each bin.\n",
"\n",
" Parameters:\n",
" processed_df (pd.DataFrame): DataFrame with calculated indicators.\n",
" return_threshold (float): Minimum absolute log_return to classify current/future as 'significant' move.\n",
" num_bins (int): Number of bins to divide the volume_normalized_zscore range.\n",
" \"\"\"\n",
" if processed_df.empty:\n",
" print(\"Processed data is empty. Cannot perform volume-trend analysis.\")\n",
" return\n",
" \n",
" required_cols = ['log_return', 'future_log_return', 'volume_normalized_zscore']\n",
" if not all(col in processed_df.columns for col in required_cols):\n",
" print(f\"Error: Missing one or more required columns: {required_cols}. Please ensure they are calculated.\")\n",
" return\n",
"\n",
" print(\"\\n--- Analyzing Trend Continuation Probability by Volume Z-score (Ignoring Overall Trend Bias) ---\")\n",
"\n",
" # 1. Define Current K-line Direction (significant move)\n",
" # 1 if significant UP, -1 if significant DOWN, 0 if Flat\n",
" def get_direction_sign(log_ret):\n",
" if log_ret > return_threshold:\n",
" return 1 # Up\n",
" elif log_ret < -return_threshold:\n",
" return -1 # Down\n",
" else:\n",
" return 0 # Flat\n",
" \n",
" processed_df['current_direction_sign'] = processed_df['log_return'].apply(get_direction_sign)\n",
" processed_df['future_direction_sign'] = processed_df['future_log_return'].apply(get_direction_sign)\n",
"\n",
" # 2. Define 'Is Continuation' (Target Variable)\n",
" # A continuation occurs if current_direction_sign is not 0 AND future_direction_sign is the same\n",
" processed_df['is_continuation'] = np.nan # Initialize with NaN\n",
" \n",
" # Cases where current move is Up and future is also Up\n",
" processed_df.loc[(processed_df['current_direction_sign'] == 1) & (processed_df['future_direction_sign'] == 1), 'is_continuation'] = 1\n",
" # Cases where current move is Down and future is also Down\n",
" processed_df.loc[(processed_df['current_direction_sign'] == -1) & (processed_df['future_direction_sign'] == -1), 'is_continuation'] = 1\n",
" # Cases where current move is significant but future is not in the same direction (e.g., flat, reverse)\n",
" processed_df.loc[((processed_df['current_direction_sign'] != 0) & (processed_df['is_continuation'].isna())), 'is_continuation'] = 0\n",
"\n",
" # Filter out rows where current K-line was flat, as there's no trend to \"continue\"\n",
" df_for_analysis = processed_df[processed_df['current_direction_sign'] != 0].copy()\n",
" \n",
" if df_for_analysis.empty:\n",
" print(\"No significant current moves (Up/Down) to analyze for continuation.\")\n",
" return\n",
"\n",
" # --- REMOVED: Filtering out 1% and 99% Z-score outliers ---\n",
" # Now using the full range of df_for_analysis for binning\n",
"\n",
" # 3. Binning Volume Normalized Z-score (using unfiltered data)\n",
" min_z = df_for_analysis['volume_normalized_zscore'].min()\n",
" max_z = df_for_analysis['volume_normalized_zscore'].max()\n",
" \n",
" if pd.isna(min_z) or pd.isna(max_z) or (max_z - min_z < 0.001):\n",
" print(\"Warning: Volume Z-score range is too small or contains NaNs for binning.\")\n",
" # Fallback for very small ranges to prevent errors\n",
" if pd.isna(min_z) or pd.isna(max_z):\n",
" min_z = -5\n",
" max_z = 5\n",
" elif (max_z - min_z < 0.001):\n",
" max_z = min_z + 0.001\n",
"\n",
" bins = np.linspace(min_z, max_z, num_bins + 1)\n",
" labels = [f'{bins[i]:.2f} to {bins[i+1]:.2f}' for i in range(num_bins)]\n",
" # Use pd.cut for binning\n",
" df_for_analysis['volume_zscore_bin'] = pd.cut(df_for_analysis['volume_normalized_zscore'], bins=bins, labels=labels, include_lowest=True)\n",
"\n",
" # 4. Calculate Continuation Probability for each bin\n",
" continuation_prob = df_for_analysis.groupby('volume_zscore_bin')['is_continuation'].mean()\n",
" \n",
" continuation_df = pd.DataFrame({\n",
" 'Volume Z-score Bin': continuation_prob.index,\n",
" 'Trend Continuation Probability': continuation_prob.values\n",
" }).dropna() # Drop NA if a bin has no data\n",
"\n",
" if continuation_df.empty:\n",
" print(\"No data points for trend continuation within the bins. Adjust thresholds or data range.\")\n",
" return\n",
" \n",
" print(\"\\nTrend Continuation Probabilities by Volume Z-score Bin (Direction Agnostic, All Data):\")\n",
" print(continuation_df)\n",
"\n",
" # 5. Visualization - Individual Bin Probabilities (Original Plot, without filtering)\n",
" plt.figure(figsize=(14, 8))\n",
" \n",
" plt.plot(continuation_df['Volume Z-score Bin'], continuation_df['Trend Continuation Probability'],\n",
" marker='o', linestyle='-', color='purple', label='Trend Continuation Probability')\n",
" \n",
" plt.title('Trend Continuation Probability vs. Volume Z-score (Direction Agnostic, All Data)', fontsize=18)\n",
" plt.xlabel('Volume Z-score Bins', fontsize=14)\n",
" plt.ylabel('Continuation Probability', fontsize=14)\n",
" plt.xticks(rotation=45, ha='right') # Rotate labels for readability\n",
" plt.ylim(0, 1) # Probability range\n",
" plt.axhline(0.5, color='gray', linestyle=':', linewidth=1, label='Random (0.5)') # Reference line for 0.5 probability\n",
" plt.grid(True, linestyle='--', alpha=0.7)\n",
" plt.legend(fontsize=12)\n",
" plt.tight_layout()\n",
" plt.show()\n",
"\n",
" # print(\"\\nThis plot shows the probability of ANY trend (up or down) continuing, across different levels of volume change rate.\")\n",
" # print(\"Peaks above 0.5 indicate where current direction is more likely to be followed by the same direction in the future.\")\n",
" # print(\"No Z-score outliers have been removed in this plot.\")\n",
"\n",
"\n",
" # --- NEW PLOT: Number of Data Points per Bin ---\n",
" print(\"\\n--- Plotting Number of Data Points per Volume Z-score Bin ---\")\n",
"\n",
" # Count the number of data points in each bin\n",
" bin_counts = df_for_analysis['volume_zscore_bin'].value_counts().sort_index()\n",
"\n",
" # Ensure the order of bins in bin_counts matches that in continuation_df\n",
" # (pd.cut with labels usually handles this, but explicitly reindexing can ensure consistency)\n",
" bin_counts = bin_counts.reindex(continuation_df['Volume Z-score Bin'])\n",
" \n",
" # Convert to DataFrame for plotting\n",
" bin_counts_df = pd.DataFrame({\n",
" 'Volume Z-score Bin': bin_counts.index,\n",
" 'Number of Data Points': bin_counts.values\n",
" }).dropna()\n",
"\n",
" if bin_counts_df.empty:\n",
" print(\"No data points found for plotting bin counts.\")\n",
" return\n",
"\n",
" plt.figure(figsize=(14, 8))\n",
" sns.barplot(x='Volume Z-score Bin', y='Number of Data Points', data=bin_counts_df, palette='viridis')\n",
" plt.title('Number of Data Points per Volume Z-score Bin', fontsize=18)\n",
" plt.xlabel('Volume Z-score Bins', fontsize=14)\n",
" plt.ylabel('Number of Data Points', fontsize=14)\n",
" plt.xticks(rotation=45, ha='right')\n",
" plt.grid(axis='y', linestyle='--', alpha=0.7)\n",
" plt.tight_layout()\n",
" plt.show()\n",
"\n",
" # print(\"\\nThis plot shows the raw count of data points falling into each Volume Z-score bin.\")\n",
" # print(\"Bins with very low counts might produce less reliable trend continuation probability estimates.\")\n"
],
"outputs": [],
"execution_count": 4
},
{
"cell_type": "code",
"id": "74770a30",
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-28T05:56:01.722601Z",
"start_time": "2025-07-28T05:56:01.496951Z"
}
},
"source": [
"\n",
"df_raw = load_and_preprocess_data(file_path)\n",
"if df_raw is not None and not df_raw.empty:\n",
" # volume_window and price_lag parameters for indicator calculation\n",
" # price_lag defines \"N\" in \"next N K-lines\"\n",
" processed_data = calculate_stationary_indicators(df_raw, volume_window=30, price_lag=5)\n",
" \n",
" # Analyze the impact of volume change rate and current K-line direction on future trend\n",
" # return_threshold: set a small threshold to define 'significant' up/down move, otherwise it's 'flat'\n",
" analyze_trend_continuation_probability(processed_data, return_threshold=0.0001, num_bins=10)\n",
"else:\n",
" print(\"Analysis cannot proceed. Please check if data loading was successful.\") "
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Successfully loaded 7650 rows of data.\n",
"First 5 rows of data:\n",
" open high low close volume open_oi \\\n",
"datetime \n",
"2020-12-31 14:00:00 2735.0 2747.0 2735.0 2742.0 121244.0 1055815.0 \n",
"2021-01-04 09:00:00 2755.0 2779.0 2754.0 2766.0 263228.0 1061496.0 \n",
"2021-01-04 10:00:00 2766.0 2769.0 2758.0 2760.0 128679.0 1090445.0 \n",
"2021-01-04 11:00:00 2760.0 2762.0 2756.0 2760.0 43784.0 1085368.0 \n",
"2021-01-04 13:00:00 2760.0 2760.0 2744.0 2750.0 80449.0 1077012.0 \n",
"\n",
" close_oi underlying_symbol \n",
"datetime \n",
"2020-12-31 14:00:00 1061496.0 DCE.c2105 \n",
"2021-01-04 09:00:00 1090445.0 DCE.c2105 \n",
"2021-01-04 10:00:00 1085368.0 DCE.c2105 \n",
"2021-01-04 11:00:00 1077012.0 DCE.c2105 \n",
"2021-01-04 13:00:00 1063889.0 DCE.c2105 \n",
"Indicators calculated. 7611 rows of data remaining for analysis.\n",
"\n",
"--- Analyzing Trend Continuation Probability by Volume Z-score (Ignoring Overall Trend Bias) ---\n",
"\n",
"Trend Continuation Probabilities by Volume Z-score Bin (Direction Agnostic, All Data):\n",
" Volume Z-score Bin Trend Continuation Probability\n",
"0 -1.73 to -1.09 0.481203\n",
"1 -1.09 to -0.45 0.483454\n",
"2 -0.45 to 0.19 0.484419\n",
"3 0.19 to 0.83 0.462946\n",
"4 0.83 to 1.47 0.487805\n",
"5 1.47 to 2.11 0.460843\n",
"6 2.11 to 2.75 0.479769\n",
"7 2.75 to 3.39 0.414634\n",
"8 3.39 to 4.03 0.500000\n",
"9 4.03 to 4.67 0.588235\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1400x800 with 1 Axes>"
],
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"--- Plotting Number of Data Points per Volume Z-score Bin ---\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1400x800 with 1 Axes>"
],
"image/png": "iVBORw0KGgoAAAANSUhEUgAABW0AAAMWCAYAAACKoqSLAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQAA0yVJREFUeJzs3XmcXfP9P/DXTCYJkYWIJJJQtcSaCBJEpWopbWkrol+UWosiqqhaW/vSfu1rUKLUUmoprbV2YoklFFE7WWSTkEQi29zfH35zv5lsJpNJcpjn8/HweOSe87n3vt9zzpzE637u51SUSqVSAAAAAAAohMqlXQAAAAAAAP9HaAsAAAAAUCBCWwAAAACAAhHaAgAAAAAUiNAWAAAAAKBAhLYAAAAAAAUitAUAAAAAKBChLQAAAABAgQhtAQAAAAAKRGgLAF9zv/jFL7L22mvnkksuWdqlLFVTp07NhRdemB/+8Ifp3r171l577ay99toZOnTo0i6NOnIuMy81v8vPPffc0i4FlgjnPABJUrW0CwCAxeGSSy7JpZdemiRZZpll8uCDD6ZDhw7zHDt8+PBsu+22SZLrr78+m2222RKrk4Zz5JFH5tFHH03y5TFv165dkqSqqm7/3PnFL36R559/vta2Jk2aZLnllkurVq2y2mqrZd11183WW2+dnj17Nmzxsxk6dGj+/e9/p1WrVtl3330X2/ssyOy/E7OrrKxMq1at8u1vfzt9+vTJz3/+87Rt23YpVLjwaoLgvn37pkuXLku5mq+Pu+++O8ccc0yS5O9//3u6detWp+edcMIJuf3227P88svnySefTLNmzRZnmY3OHXfckeOPP75ez/3vf//bwNVQY37XziRp2rRp2rRpk65du2aHHXZIv3790rRp0yVcIQBfJ0JbAL7xvvjii1x22WU57bTTlnYpLCbvvvtuObC94IIL8qMf/ajer1XzP9Y1Pv/884wYMSIjRozI008/nT//+c9ZY401csopp2TTTTdd5NrnNHTo0Fx66aXp3LnzUgttZ9eyZcsss8wySZIZM2bks88+y5AhQzJkyJDcdNNNufLKK+sc5H2VlVdeOd/+9rezwgorNMjrza7mQ5xNN91UaLsQdthhh5x++umZOHFibr/99jod6ylTpuS+++5Lkvz0pz8V2C4Gs38w9VVmzZqVCRMmJIljsQTNfu1MksmTJ2fcuHEZN25cBg0alFtvvTXXXnttll9++bme++1vfztJsuyyyy6pcgEoIKEtAI3C7bffnv3226/8P0J8s7z11ltJkuWXX36RAtsk2WijjXLDDTfU2vbFF1/kjTfeyIMPPpjbbrst7777bvbee++cfPLJ2WOPPRbp/YruxBNPzC677FJ+/Nlnn+Wmm27KZZddlk8++SSHH354HnjggTRv3nyR3+tPf/rTIr8GDat58+bZaaedctNNN+Vf//pXjj/++K881vfff3+mTJmSJOnXr9+SKLPR+dGPflTna90ZZ5xRvqadfPLJi7MsZjPntTNJRo8enWuuuSZ/+ctf8vrrr+f888+f5wfK999//5IqE4ACs6YtAN9oK6+8ctZee+3MnDkzF1xwwdIuh8Xkiy++SJIst9xyi+X1l1lmmWy88cY57rjjcvfdd2fttddOqVTK6aefnhdeeGGxvGdRtWnTJoccckh++ctfJkk+/vjjPPzww0u5KhanXXfdNUkyceLEPPTQQ185/vbbb0+SdOvWLWuvvfZirY0Fu+OOO8qB7V577VU+liwdHTp0yAknnJDevXsnSf79738v5YoAKDIzbQH4RqusrMzRRx+dgw46KA888EBeffXVdO/evc7Pn319uocffni+X6veZpttMmLEiJx99tm1ZtbM+fyKiopcccUVeeqpp/LJJ5+kQ4cO2XHHHXPwwQenRYsWSb6cNXrVVVdl8ODBGT9+fFZeeeXsvPPOOfDAA79y/bvp06fnuuuuyz333JNhw4aladOm2WCDDbLvvvtmq622WuBz33rrrdxwww157rnnMnr06FRWVqZLly7ZZpttss8++8xz7dKatYM33XTT3HDDDXnggQfyt7/9LUOHDs2ECRNy2GGH5fDDD1/g+85u2rRpufnmm3P//ffn3XffzRdffJF27dqlV69e2W+//bLuuuvO8/1rjBgxolZI1Ldv35xzzjl1fv+66Ny5cy6//PL8+Mc/zpQpU3L++efnpptuqjVm6tSpefjhh/PEE0/kv//9b0aPHp3Jkydn+eWXT/fu3bPbbrvN83jMXvucvSRJ//79yz/P+r5HQ/npT3+aK664Iknyn//8p9asv4U9jjVq1hWevc8as/+O7bTTTrn++utz991356OPPkqTJk2y/vrr55e//GW++93v1nrecccdlzvvvLP8eO+99661v3PnznnkkUfKj0eNGpVrr702Tz/9dEaMGJGZM2dm+eWXT/v27dOzZ8/stNNOi3QNmTlzZgYMGJBBgwZl/PjxadeuXb773e/msMMOm++620lSXV2df/7zn7nnnnvy+uuvZ+LEiWnZsmXWW2+97LLLLtlxxx1TUVEx1/Nm/7ntsMMO+fOf/5x///vfGT58eKZMmbLA61qN9ddfP+uuu26GDh2a22+/PTvttNN8x3744YflDzLmDAjHjh2ba6+9Nk888URGjBiR5Muf/1ZbbZX999+/zl/3r/Hcc8+Vj+eC1mmt+T2ac83yOZ//5ptv5qqrrsrzzz+fiRMnpnPnztl1112zzz77lNfGfvHFF3PNNdfk1VdfzWeffZZvfetb2WOPPfLzn/98nj//GvW5vi6qV199tTyzdtNNN633GrhJcu+99+aOO+7IG2+8kc8++yzLLrts2rZtm9VXXz19+vTJrrvuOs8Z2BMmTMiNN96Yxx9/PB9++GGmTp2alVZaKauttlq22267/PjHP06rVq3met6DDz6Y22+/Pf/5z38yceLEtG7dOt26dcuuu+6a73//+/OsseZ3vW/fvjn77LPz97//PXfccUfee++9fPrpp/P8+/kvf/lLBg0alJEjR6a6ujorr7xyttxyy+y///7p1KlTvX9eX2XdddfNM888U56RPqf5nbNzXk+WWWaZDBgwII888kjGjh2bVq1aZbPNNkv//v2zxhprLLb6AVgyhLYAfONttdVW2XTTTfP888/n3HPPzfXXX79U6njjjTdy4oknlsOWWbNmZdiwYRkwYEBeeOGFXHfddXn66afzm9/8JlOnTk2rVq0yY8aMfPjhh7nooovy9ttvL3C28IwZM7LffvvlhRdeSFVVVVq0aJGJEydm0KBBGTRo0DyDsBpXX311zj///FRXVyf5ch29GTNm5K233spbb72V22+/PVdddVXWW2+9+b7/Oeeck4EDB6aioiKtW7dOZeXCfaFn9OjR+eUvf1le6qBp06ZZZpllMnLkyPzjH//IPffckxNOOCG/+MUvys9p0aJF2rVrly+++CKTJ09OZWVlrfCjZcuWC1VDXXXp0iV9+/bNjTfemBdffDHDhg3LKqusUt5/3333lQOSioqKtGzZMlVVVRk7dmwefvjhPPzww9l///1z7LHH1nrdBfVS0++ivkdD6dixY/nPkydPLv+5PsdxYUyZMiV77bVXXnnllTRt2jRNmzbN5MmT89xzz+X555/PGWecUSssbNmyZdq1a5dx48Yl+XKm8Owffsy+fu6bb76ZvffeO5999lmSL29E17Jly4wbNy5jx44th6ULE9rO7tVXX81JJ52Uzz//PC1atEiTJk3y8ccf529/+1seeOCBXHvttVl//fXnet6nn36a/v37Z/DgweVtrVq1yoQJE/L000/n6aefzr/+9a9cdNFF812z9NNPP80uu+ySDz74IE2bNl3otTJ33XXXnH766Xn22WczcuTI+QZad9xxR5IvZ6fPHu4+//zzOeywwzJx4sQk/3cuv/POO3nnnXfy97//PZdffvlivcnfgjz++OM5/PDDM23atLRq1SrTp0/Pe++9lz/96U/lr7HfdtttOfnkk1NdXZ2WLVtm+vTpefvtt3Paaafl448/zm9/+9t5vnZDXF8X1rh
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 5
},
{
"cell_type": "code",
"id": "8fa62ad6",
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-28T05:56:02.110526Z",
"start_time": "2025-07-28T05:56:01.737618Z"
}
},
"source": [
"\n",
"\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",
" \n",
" # --- NEW: Optional filtering for typical trading hours ---\n",
" # If your data includes non-trading hours (e.g., overnight, weekends in daily data)\n",
" # and you only want to analyze main trading sessions, uncomment and adjust.\n",
" # Example for typical daytime futures trading:\n",
" # df = df[(df.index.hour >= 9) & (df.index.hour < 15)] # Filter 9:00 to 14:59 for example\n",
" # df = df[df.index.dayofweek < 5] # Exclude Saturday (5) and Sunday (6) if using daily data with weekends\n",
"\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",
" 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",
"# --- 2. Stationary Indicator Calculation Function (与之前代码相同) ---\n",
"def calculate_stationary_indicators(df, volume_window=10, price_lag=5):\n",
" \"\"\"\n",
" Calculates stationary indicators based on volume and price.\n",
" \"\"\"\n",
" df_processed = df.copy() \n",
"\n",
" df_processed['volume_roc'] = df_processed['volume'].pct_change(volume_window) * 100\n",
" df_processed['volume_ma_ratio'] = df_processed['volume'] / df_processed['volume'].rolling(window=volume_window).mean()\n",
" rolling_mean_vol = df_processed['volume'].rolling(window=volume_window).mean()\n",
" rolling_std_vol = df_processed['volume'].rolling(window=volume_window).std()\n",
" df_processed['volume_normalized_zscore'] = (df_processed['volume'] - rolling_mean_vol) / rolling_std_vol.replace(0, np.nan)\n",
"\n",
" df_processed['log_return'] = np.log(df_processed['close'] / df_processed['close'].shift(1))\n",
" df_processed['future_log_return'] = np.log(df_processed['close'].shift(-price_lag) / df_processed['close'])\n",
"\n",
" try:\n",
" macd, macdsignal, macdhist = ta.MACD(df_processed['close'], fastperiod=12, slowperiod=26, signalperiod=9)\n",
" df_processed['macd_hist_diff'] = macdhist.diff(1) \n",
" except Exception as e:\n",
" # print(f\"TA-Lib MACD calculation failed, possibly due to installation or data issues: {e}. 'macd_hist_diff' will contain NaN.\")\n",
" df_processed['macd_hist_diff'] = np.nan \n",
"\n",
" df_processed.dropna(inplace=True)\n",
" if df_processed.empty:\n",
" print(\"Warning: Data is empty after indicator calculation. Check original data volume or adjust window parameters.\")\n",
" else:\n",
" print(f\"Indicators calculated. {len(df_processed)} rows of data remaining for analysis.\")\n",
" return df_processed\n",
"\n",
"# --- 3. 价格变化率分析与可视化函数 (横轴调整为连续索引) ---\n",
"def analyze_price_change_rate_for_trend(processed_df, rolling_vol_window=30):\n",
" if 'log_return' not in processed_df.columns or processed_df['log_return'].isnull().all():\n",
" print(\"Error: 'log_return' column not found or contains only NaN values. Cannot analyze price change rate for trend.\")\n",
" return\n",
"\n",
" print(\"\\n--- Analyzing Price Change Rate (Log Returns) for Trend Characteristics ---\")\n",
"\n",
" # Add a continuous index for plotting, ignoring date gaps\n",
" processed_df['continuous_index'] = range(len(processed_df))\n",
"\n",
" # 1. Distribution of Log Returns (Price Change Rate)\n",
" plt.figure(figsize=(10, 6))\n",
" sns.histplot(processed_df['log_return'], bins=100, kde=True, color='purple', alpha=0.7)\n",
" plt.title('Distribution of Log Returns (Price Change Rate)', fontsize=16)\n",
" plt.xlabel('Log Return', fontsize=12)\n",
" plt.ylabel('Frequency', fontsize=12)\n",
" plt.grid(True, linestyle='--', alpha=0.6)\n",
" plt.show()\n",
"\n",
" print(\"\\nStatistical summary of Log Returns:\")\n",
" print(processed_df['log_return'].describe())\n",
" print(f\"Kurtosis of Log Returns: {processed_df['log_return'].kurtosis():.4f}\")\n",
" print(f\"Skewness of Log Returns: {processed_df['log_return'].skew():.4f}\")\n",
"\n",
" # 2. Time Series of Log Returns and Rolling Volatility\n",
" processed_df['rolling_volatility'] = processed_df['log_return'].rolling(window=rolling_vol_window).std()\n",
"\n",
" fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(15, 10), sharex=True)\n",
"\n",
" # Subplot 1: Log Returns Over Continuous Trading Periods\n",
" # Use 'continuous_index' for the x-axis to remove date gaps\n",
" ax1.plot(processed_df['continuous_index'], processed_df['log_return'], label='Log Returns', color='blue', alpha=0.7, linewidth=0.8)\n",
" ax1.set_title('Log Returns Over Trading Periods (Continuous Index)', fontsize=16)\n",
" ax1.set_ylabel('Log Return', fontsize=12)\n",
" ax1.grid(True, linestyle='--', alpha=0.6)\n",
" ax1.legend()\n",
"\n",
" # Subplot 2: Rolling Volatility Over Continuous Trading Periods\n",
" # Use 'continuous_index' for the x-axis\n",
" ax2.plot(processed_df['continuous_index'], processed_df['rolling_volatility'], label=f'Rolling Volatility ({rolling_vol_window} periods)', color='red', linewidth=1.5)\n",
" ax2.set_title(f'Rolling Volatility ({rolling_vol_window}-period Std Dev of Log Returns) Over Trading Periods', fontsize=16)\n",
" ax2.set_ylabel('Volatility', fontsize=12)\n",
" ax2.set_xlabel('Trading Period Index', fontsize=12) # Changed x-axis label\n",
" ax2.grid(True, linestyle='--', alpha=0.6)\n",
" ax2.legend()\n",
"\n",
" plt.tight_layout()\n",
" plt.show()\n",
"\n",
" print(\"\\nAnalysis focused on price change rate and its dynamics over continuous trading periods.\")\n",
" print(\"Higher volatility periods often provide more opportunities for trend-following strategies.\")\n",
"\n",
"\n",
"# --- Main Execution Flow ---\n",
"if __name__ == \"__main__\":\n",
" df_raw = load_and_preprocess_data(file_path)\n",
"\n",
" if df_raw is not None and not df_raw.empty:\n",
" processed_data = calculate_stationary_indicators(df_raw, volume_window=10, price_lag=5)\n",
" analyze_price_change_rate_for_trend(processed_data, rolling_vol_window=30)\n",
" else:\n",
" print(\"Analysis cannot proceed. Please check if data loading was successful.\")"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Successfully loaded 7650 rows of data.\n",
"Indicators calculated. 7611 rows of data remaining for analysis.\n",
"\n",
"--- Analyzing Price Change Rate (Log Returns) for Trend Characteristics ---\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1000x600 with 1 Axes>"
],
"image/png": "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JAwdOhS9evXCcccdh1GjRuGOO+7AypUrm/9l1vLf//4XADB27NgW/RwAWK1WfSli7etfa4+lJUuW4IorrsDxxx8f8HdBc7/bHTt24MEHH8QZZ5yBfv36YeDAgTjrrLPw4IMP4o8//qh3fnl5OZ577jmcd955GDBgAPr164dzzjkH//73v+F0OlvUr7feegsVFRVIS0sL6rqd4447rtHHWvJ3SX5+Pl555RVcccUV+Nvf/obevXtj8ODBuPTSS/Huu+9CVdV6P7N7927k5uZixIgREELgvffew7hx49C/f38MGjQIV199NVavXt1o+/744w9MnjwZQ4YM0X9nb7zxBlRVbfLzwOv14v3338fEiRP1v2NHjBiBBx54AHv27Gn2d9YSzY39r776Cvfeey/OPvtsHHfccejTp4/+OfLnn3/WOz83N1efkXrhhRcCxv60adPC0s+xY8dCVVW88847IfSc2gIuOySKgFNOOQWpqakoKyvDsmXL0Lt37ybPV1UV119/PX7++WckJydj8ODBaN++PUpLS7Fr1y689tprOOecc5CamorDDjsMY8eOxZdffonq6mqcccYZSExM1J+r7jVCNTU1mDhxIrZv347BgwejZ8+eeiEXjK+//hpvvfUWjjzySAwbNgxFRUX49ddfcdddd2Hz5s31Prha44wzzsCaNWvw22+/4bDDDsOgQYP0x4488shmf14IgWnTpuGjjz6CzWbD4MGDkZaWhg0bNmDBggX4/PPP8dxzz+GUU05p8Oc//PBDvPTSSzj22GNx8sknIz8/H2vWrMFdd92FsrIyXHXVVUH3xe1244YbbsCyZcsQHx+PIUOGIDk5GatXr8abb76Jzz77DK+99hp69eoFADjmmGMwduxY/Prrr/jrr78CricMpu+tsXbtWlx33XUoKytDVlYWRo4ciQMHDmDFihVYvXo1vv76a7z00kuIi4sL+LmHHnoI77zzDiwWCwYPHoz09HT88ccfmDBhQtiuy6hNVVVUV1cDQL1ZCK/XizvuuAOff/454uLi0KtXL2RmZmLnzp349NNP8fXXX+P555/XM9d+zwsXLgRQv/CoPYZCEcx4O3DgAC655BLs2bMHgwYNwtFHH401a9bgo48+wsqVK/Hxxx8HzO4uXLhQ/3Kib9++GDJkCFwuFwoLC7F48WJ06NChyWKgbvt+/PFH2O32oH+mrsrKSgCo9/4AgLlz5+K///2vXggXFRXBarU2+5yffvop7rnnHrjdbmRlZWH48OFQVRV5eXl49913kZaWhpycHP38bdu24dprr8WePXuQnp6OQYMGwWazYd26dZg1axa++uorvPnmm0HPkn/zzTcAgLPOOqvBfgWrpX+XfPzxx5g1axa6deuGww8/HAMHDkRxcTFWr16N3377DT/99BOee+45KIrS4Ovdfffd+OyzzzBo0CD87W9/w6ZNm/DTTz9h5cqV+O9//4t+/foFnL9ixQpcd911cLlcOOyww3DSSSehrKwMTz/9NH7//fdG+1VZWYkbb7wRK1asQGJiInr37o0OHTrgjz/+wLvvvosvvvgCc+fOxbHHHtvq311tTY19APjHP/6BuLg49OjRAyeccAK8Xi+2bt2KBQsW4IsvvsBrr72GgQMH6uePHTsWmzZtwubNmwOuZQQQ8HkTSj9POuk
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Statistical summary of Log Returns:\n",
"count 7611.000000\n",
"mean -0.000026\n",
"std 0.002949\n",
"min -0.029732\n",
"25% -0.001256\n",
"50% 0.000000\n",
"75% 0.001236\n",
"max 0.040765\n",
"Name: log_return, dtype: float64\n",
"Kurtosis of Log Returns: 15.1149\n",
"Skewness of Log Returns: 0.0586\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1500x1000 with 2 Axes>"
],
"image/png": "iVBORw0KGgoAAAANSUhEUgAABdEAAAPeCAYAAADj01PlAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xl81GT+B/BPpi0UKJflEATXXVREQK5VVsV1FVZRf3iAuq7conggioIc3qgICgJyiKuIiuIBSEEUUJAVRG7klvuQUgotlBZaWnpMfn9kM81kkplkJjPzzPTzfr14AZmZ5HmSb65vnjyPJMuyDCIiIiIiIiIiIiIi8uGKdgGIiIiIiIiIiIiIiETFJDoRERERERERERERkQkm0YmIiIiIiIiIiIiITDCJTkRERERERERERERkgkl0IiIiIiIiIiIiIiITTKITEREREREREREREZlgEp2IiIiIiIiIiIiIyAST6EREREREREREREREJphEJyIiIiIiIiIiIiIykRjtAhAREZG5m2++GRkZGRg9ejS6du0a7eLY0rRpU59plStXRp06ddCqVSt0794df/3rX6NQsviXmZmJzz//HL/++isyMjJQWFiICy64AC1btsTtt9+O22+/HZIkRbuYQVP3CzsuuugiLF++PEwlMjdv3jyMGDEC99xzD8aMGeOZvm7dOvTq1QvXXHMNPvvss4iXy6rhw4cjLS3Na1pCQgJq1KiBZs2a4a677sJdd90VkXg6evQoOnbsGLFtGeltNGDAAPzyyy/48ccfceGFFxp+Z+vWrZg3bx42btyIEydOoKioCNWrV0eTJk1w3XXX4e6770bDhg3DXtZQ9OzZE+vXr8fMmTPRvn37aBcnJqnr8Mknn8TAgQPDvrxI73tG+vTpg23btuGHH35A3bp1o1IGIiKq2JhEJyIiorDq0KGD54b39OnT2LFjBxYtWoTFixdjxIgR6N27tyPLUZP2e/bscWR+sWrWrFkYM2YMiouLUatWLbRr1w5Vq1bFkSNH8NNPP2HZsmX4+OOPMXXqVNSvXz/axQ3KrbfeitOnT3tNO3fuHH744QfP51WrVvX6vHbt2hErXzy6+OKL0a5dOwDA+fPnsW/fPqxevRqrV6/GTz/9hIkTJyIhISHKpYxdq1evxrJly/DQQw8ZJtALCwvx4osv4rvvvgMA1K1bF+3atUNKSgpyc3Oxbds2bNiwAe+99x4mTpyITp06RboKAIDJkydjypQpEUvuUsUxePBg3HvvvRg/fjxGjx4d7eIQEVEFxCQ6ERERhVX//v29WhsWFhZi6NCh+PHHHzF27Fh07tw5ZpO5ovn000/x5ptvwuVyYfDgwejbty+SkpI8nx84cADPPfcctm/fju7du2PevHmoUaNGFEscnGHDhvlMO3r0qCeJPnToUDRq1CjSxbLlqquuwqJFi1ClSpVoF8WSdu3aebWkB4AvvvgCI0eOxI8//oi0tDTce++9YS1D/fr1sWjRIq+YjhejR49G5cqV0b9/f5/PSkpK0K9fP2zatAl169bFyJEj0bFjR6/vlJaWYunSpZgwYQKOHj0aqWIH5a233kJhYaHwLeZJLC1btsRNN92EtLQ09O7dG1dccUW0i0RERBUM+0QnIiKiiKpSpQpefPFFAEpy6JdffolyieLD/v37MXbsWADAiBEj0L9/f59kY5MmTfDpp5/i4osvRnp6Ol5//fVoFJWg7AdNmjSJ6UTigw8+iGuuuQYAsHjx4rAvLykpCU2aNMHFF18c9mVF0q+//oq9e/eiU6dOhm9MvPfee9i0aRNq1KiBL7/80ieBDgCJiYm47bbbkJaW5tkmomrYsCGaNGkSMw+QSBz33nsvZFnGp59+Gu2iEBFRBcQkOhERUZw5fvw4Xn/9ddxyyy1o2bIl2rVrhwceeABfffUVysrKDH8jyzLmzp2Lrl27olWrVmjfvj0efvhh/Pbbb1i3bh2aNm2Knj17OlbG+vXro1atWgCAU6dOGX5nyZIl6NevH/72t7+hRYsWuOGGGzBkyBDs37/f63uTJ0/26n+9adOmXn/UVpnq9yZPnmy4PLN6aqcXFhbi3XffxW233YZWrVrh5ptvBqD0e920aVMMHz4c586dwzvvvIN//vOfaNGiBa6//noMGzYMJ06cMFzu6tWr8dhjj+G6665D8+bNcfXVV+OWW27BkCFDsGHDhsAr838++ugjlJSUBNxW1atXx9ChQwEA33//PdLT0wEordSbNm2Kq6++GufPnzf9fdeuXdG0aVMsW7bMa3ppaSnmzJmDnj174pprrkGLFi1w880345VXXkFmZqbPfKysV6dot09ubi5GjRqFTp06oUWLFl7ravXq1Xj99ddx1113oX379mjRogX+/ve/Y9CgQdi2bZvp/EtLS/HJJ5+gS5cuaNmyJf72t79h4MCBfrsWMou3o0ePomnTprj55pshyzK+/vprdO3aFa1bt0a7du3w0EMPYfPmzabz3bt3LwYOHIj27dujVatW6NKlCz755BO43W7cfPPNXvuEE5o3bw4APn3U5+XlYdKkSbjrrrvQpk0bT1nee+89FBYW+sxHu38eO3YMzz//PG688UY0b94cw4cP91k3RoI59gHA/Pnz0a1bN7Rq1QrXXHMN+vXrh40bN/qtt1P7LQB8/vnnAIB77rnH57P8/HzMnDkTgNJneuPGjf3Oq1q1arjyyit9pv/yyy949NFHce2116JFixbo0KEDBg0ahO3btxvOp2fPnmjatCnWrVuHXbt24cknn/TsE7fffjtmzJgBWZa9ftO0aVNMmTIFADBlyhSv47C6DfXz1ho+fDiaNm2KefPmIT09Hc899xyuv/56tGjRAp06dcKECRNQXFzsU1bt74xo938jdtdNoP3IrDzFxcWYPn06unbtijZt2njOD926dcPbb7+N3Nxcw/nZEcq5CAD++9//okePHmjTpg3atWuHBx980OdYb8TO/j5jxgw0bdoUt956K/Lz833mNXv2bDRt2hQ33ngjcnJyvD678cYbUbt2bXz//feOrC8iIiI72J0LERFRHNm2bRseeeQR5ObmomHDhujUqRPOnj2L9evXY/PmzVi6dCmmTZuGSpUqef1u5MiR+PLLL+FyufDXv/4VdevWxd69e9GjRw/H+izXcrvdOHfuHAAgNTXV67PS0lIMGTIEixcvRqVKldC8eXPUr18fhw8fxsKFC7F06VJMnjwZf//73wEAzZo1wz333OMZ/FCfiNL3jR2s8+fPo2fPnjhw4AD++te/4oorrvC5iT979iweeOABZGZmol27drjsssuwZcsWzJ8/Hxs2bMCCBQtQvXp1z/fT0tIwYsQIAEr3Hu3bt0dRURFOnDiBRYsWoXbt2rj66qsDlk2WZc9gb1YGebzppptQo0YNnDlzBj///DN69uyJJk2aoE2bNti8eTOWLVuGO+64w+d3e/bswc6dO1GnTh384x//8EzPz8/H448/jvXr16Nq1apo0aIFateujb179+Krr77CkiVL8PHHHxsm96ysV6ecPn0a3bp1w9mzZ9GuXTs0b97cq7W+mvC/7LLL0LZtWyQmJuLgwYNYvHgxli5divHjx+PWW2/1mqfb7cbTTz+NZcuWISkpCe3bt0eNGjWwdetW3HfffejWrVvQ5R0xYgS+++47tGvXDv/4xz+wa9cu/Prrr9iwYQM+//xztGrVyuv769evxyOPPIKioiJcfPHFuP7665Gbm4tx48Zh69atQZfDHzUJpj2m7N+/Hw8//DAyMzM9fXcnJiZi+/btePfdd/Hjjz/is88+89oXVIcPH8Y999yDpKQktG3bFrIsW+rPPthj3xtvvIHPPvsMLpcL7dq1Q7169bBnzx707NkTPXr0MFyWU/stoMT/qlWrkJSUZPibdevWIT8/H5Ik4a677rI0T72JEydi2rRpkCQJbdq0QcOGDXHgwAEsXrwYP/74I1577TXTrnhWrVqFjz/+2BNP2dnZ2LRpE9566y1kZmbihRde8Hz3nnvuwa5du7B7925cccUVaNasmecztT99K3bt2oVRo0ahZs2
},
"metadata": {},
"output_type": "display_data"
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"Analysis focused on price change rate and its dynamics over continuous trading periods.\n",
"Higher volatility periods often provide more opportunities for trend-following strategies.\n"
]
}
],
"execution_count": 6
},
{
"cell_type": "code",
"id": "20c278fde79da68a",
"metadata": {
"ExecuteTime": {
"end_time": "2025-07-28T05:56:02.444041Z",
"start_time": "2025-07-28T05:56:02.141429Z"
}
},
"source": [
"import pandas as pd\n",
"import numpy as np\n",
"import matplotlib.pyplot as plt\n",
"import seaborn as sns\n",
"\n",
"def analyze_price_change_autocorrelation(df: pd.DataFrame,\n",
" price_col: str = 'close',\n",
" max_lags: int = 50,\n",
" plot_specific_lag: int = 1):\n",
" \"\"\"\n",
" 分析时间序列价格变化的自相关性,并绘制图表。\n",
"\n",
" Args:\n",
" df (pd.DataFrame): 包含行情数据的DataFrame必须有日期索引或排好序。\n",
" price_col (str): 用于计算价格变化的列名,默认为 'close'。\n",
" max_lags (int): 要计算的最大滞后期数,默认为 50。\n",
" plot_specific_lag (int): 要单独绘制散点图的特定滞后期,默认为 1。\n",
" \"\"\"\n",
" # --- 1. 数据准备和计算 ---\n",
" if price_col not in df.columns:\n",
" print(f\"错误: DataFrame中找不到列 '{price_col}'\")\n",
" return\n",
"\n",
" # 创建一个副本以避免修改原始DataFrame\n",
" df_analysis = df.copy()\n",
"\n",
" # 计算价格变化百分比\n",
2025-07-15 22:45:51 +08:00
" df_analysis['pct_change'] = (df_analysis['high'] - df_analysis['low'])\n",
" # df_analysis['pct_change'] = df[price_col].pct_change().abs()\n",
"\n",
" # 移除第一个NaN值\n",
" df_analysis = df_analysis.dropna(subset=['pct_change'])\n",
"\n",
" if df_analysis.empty:\n",
" print(\"错误: 计算'pct_change'后DataFrame为空无法进行分析。\")\n",
" return\n",
"\n",
" print(f\"已计算 'pct_change',共 {len(df_analysis)} 条有效数据。\")\n",
"\n",
" # --- 2. 计算自相关性 ---\n",
" lags = range(1, max_lags + 1)\n",
" try:\n",
" autocorrs = [df_analysis['pct_change'].autocorr(lag=n) for n in lags]\n",
" except Exception as e:\n",
" print(f\"计算自相关性时出错: {e}\")\n",
" return\n",
"\n",
" autocorr_df = pd.DataFrame({'Lag': lags, 'Autocorrelation': autocorrs})\n",
" print(\"\\n自相关性计算结果 (前5期):\")\n",
" print(autocorr_df.head())\n",
"\n",
" # --- 3. 可视化 ---\n",
"\n",
" # a) 绘制自相关图 (ACF Plot)\n",
" plt.style.use('seaborn-v0_8-whitegrid') # 使用一个好看的样式\n",
" fig, axes = plt.subplots(2, 1, figsize=(14, 12)) # 创建一个包含两个子图的画布\n",
" fig.suptitle('Price Change Autocorrelation Analysis', fontsize=16)\n",
"\n",
" ax1 = axes[0]\n",
" ax1.stem(autocorr_df['Lag'], autocorr_df['Autocorrelation'])\n",
" ax1.set_title(f'Autocorrelation of Daily Price Changes (Lags 1-{max_lags})')\n",
" ax1.set_xlabel('Lag (Number of Previous K-lines)')\n",
" ax1.set_ylabel('Correlation Coefficient')\n",
" # ax1.axhline(y=0, color='grey', linestyle='--')\n",
"\n",
" # 添加置信区间\n",
" conf_interval = 1.96 / np.sqrt(len(df_analysis))\n",
" # ax1.axhline(y=conf_interval, color='red', linestyle='--', label='95% Confidence Interval')\n",
" # ax1.axhline(y=-conf_interval, color='red', linestyle='--')\n",
" ax1.legend()\n",
" ax1.grid(True)\n",
"\n",
" # b) 绘制特定滞后期的散点图\n",
" ax2 = axes[1]\n",
" if plot_specific_lag is not None and 1 <= plot_specific_lag <= max_lags:\n",
" lag_col_name = f'pct_change_lag{plot_specific_lag}'\n",
" df_analysis[lag_col_name] = df_analysis['pct_change'].shift(plot_specific_lag)\n",
" df_scatter = df_analysis.dropna()\n",
"\n",
" sns.regplot(x=lag_col_name, y='pct_change', data=df_scatter, ax=ax2,\n",
" scatter_kws={'alpha': 0.5, 's': 20},\n",
" line_kws={'color': 'red', 'linestyle': '--'})\n",
" ax2.set_title(f'Current vs. Lag-{plot_specific_lag} Price Change')\n",
" ax2.set_xlabel(f'Previous K-line\\'s pct_change (t-{plot_specific_lag})')\n",
" ax2.set_ylabel('Current K-line\\'s pct_change (t)')\n",
" ax2.grid(True)\n",
" ax2.axhline(0, color='grey', lw=0.5)\n",
" ax2.axvline(0, color='grey', lw=0.5)\n",
" else:\n",
" ax2.text(0.5, 0.5, 'No specific lag plot requested or lag is out of range.',\n",
" ha='center', va='center', transform=ax2.transAxes)\n",
" ax2.set_axis_off()\n",
"\n",
"\n",
" plt.tight_layout() # 调整布局以适应主标题\n",
" plt.show()\n",
"\n",
"\n",
"if df_raw is not None and not df_raw.empty:\n",
" processed_data = calculate_stationary_indicators(df_raw, volume_window=10, price_lag=5)\n",
" analyzed_df = analyze_price_change_autocorrelation(processed_data, plot_specific_lag=50)\n",
"else:\n",
" print(\"Analysis cannot proceed. Please check if data loading was successful.\")\n"
],
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Indicators calculated. 7611 rows of data remaining for analysis.\n",
"已计算 'pct_change',共 7611 条有效数据。\n",
"\n",
"自相关性计算结果 (前5期):\n",
" Lag Autocorrelation\n",
"0 1 0.194314\n",
"1 2 0.187014\n",
"2 3 0.054923\n",
"3 4 0.057865\n",
"4 5 0.191254\n"
]
},
{
"data": {
"text/plain": [
"<Figure size 1400x1200 with 2 Axes>"
],
"image/png": "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
},
"metadata": {},
"output_type": "display_data"
}
],
"execution_count": 7
}
],
"metadata": {
"kernelspec": {
"display_name": "quant",
"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.11"
}
},
"nbformat": 4,
"nbformat_minor": 5
}