{
 "cells": [
  {
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-21T18:08:34.210364Z",
     "start_time": "2025-06-21T18:08:33.967513Z"
    }
   },
   "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",
    "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_i@SHFE_rb/KQ_i@SHFE_rb_min60.csv'\n"
   ],
   "id": "b93c7ca1",
   "outputs": [],
   "execution_count": 1
  },
  {
   "cell_type": "code",
   "id": "60a48bac",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-21T18:08:34.354678Z",
     "start_time": "2025-06-21T18:08:34.306963Z"
    }
   },
   "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-06-21T18:08:35.089167Z",
     "start_time": "2025-06-21T18:08:34.428402Z"
    }
   },
   "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 3902 rows of data.\n",
      "First 5 rows of data:\n",
      "                       open    high     low   close    volume    open_oi  \\\n",
      "datetime                                                                   \n",
      "2022-12-30 14:00:00  4113.0  4118.0  4091.0  4099.0  386597.0  2793890.0   \n",
      "2023-01-03 09:00:00  4087.0  4087.0  4012.0  4032.0  882749.0  2759241.0   \n",
      "2023-01-03 10:00:00  4032.0  4050.0  4032.0  4037.0  208523.0  2713076.0   \n",
      "2023-01-03 11:00:00  4037.0  4048.0  4032.0  4046.0   96128.0  2717795.0   \n",
      "2023-01-03 13:00:00  4049.0  4059.0  4040.0  4052.0  143108.0  2719770.0   \n",
      "\n",
      "                      close_oi  \n",
      "datetime                        \n",
      "2022-12-30 14:00:00  2759241.0  \n",
      "2023-01-03 09:00:00  2713076.0  \n",
      "2023-01-03 10:00:00  2717795.0  \n",
      "2023-01-03 11:00:00  2719770.0  \n",
      "2023-01-03 13:00:00  2723678.0  \n",
      "Indicators calculated. 3863 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  3863.000000      3863.000000               3863.000000  3863.000000   \n",
      "mean           inf         0.998015                 -0.008718    -0.000071   \n",
      "std            NaN         0.467965                  0.937661     0.004022   \n",
      "min    -100.000000         0.000000                 -2.216503    -0.027787   \n",
      "25%     -40.224708         0.643299                 -0.740525    -0.002158   \n",
      "50%      -2.259478         0.924949                 -0.164875     0.000000   \n",
      "75%      64.149292         1.247852                  0.582903     0.001947   \n",
      "max            inf         4.054781                  2.809615     0.038023   \n",
      "\n",
      "       future_log_return  \n",
      "count        3863.000000  \n",
      "mean           -0.000363  \n",
      "std             0.009122  \n",
      "min            -0.050010  \n",
      "25%            -0.005788  \n",
      "50%            -0.000296  \n",
      "75%             0.004854  \n",
      "max             0.068530  \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.0115\n",
      "Correlation between 'volume_normalized_zscore' and 'future_log_return': 0.0161\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 900x700 with 2 Axes>"
      ],
      "image/png": "[encoded data, 64328 characters]"
     },
     "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       773.0 -0.000170  0.008692 -0.039407 -0.005085  0.000000   \n",
      "Low Volume        773.0 -0.000510  0.008661 -0.027622 -0.005910 -0.000307   \n",
      "Normal Volume    2317.0 -0.000378  0.009411 -0.050010 -0.006051 -0.000519   \n",
      "\n",
      "                      75%       max  \n",
      "volume_category                      \n",
      "High Volume      0.004926  0.034253  \n",
      "Low Volume       0.004851  0.030201  \n",
      "Normal Volume    0.004840  0.068530  \n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 1000x600 with 1 Axes>"
      ],
      "image/png": "[encoded data, 57052 characters]"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 1200x700 with 1 Axes>"
      ],
      "image/png": "[encoded data, 104488 characters]"
     },
     "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": "[encoded data, 270780 characters]"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 3
  },
  {
   "cell_type": "code",
   "id": "f13d0294",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-21T18:08:35.137491Z",
     "start_time": "2025-06-21T18:08:35.131334Z"
    }
   },
   "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-06-21T18:08:35.362961Z",
     "start_time": "2025-06-21T18:08:35.152984Z"
    }
   },
   "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 3902 rows of data.\n",
      "First 5 rows of data:\n",
      "                       open    high     low   close    volume    open_oi  \\\n",
      "datetime                                                                   \n",
      "2022-12-30 14:00:00  4113.0  4118.0  4091.0  4099.0  386597.0  2793890.0   \n",
      "2023-01-03 09:00:00  4087.0  4087.0  4012.0  4032.0  882749.0  2759241.0   \n",
      "2023-01-03 10:00:00  4032.0  4050.0  4032.0  4037.0  208523.0  2713076.0   \n",
      "2023-01-03 11:00:00  4037.0  4048.0  4032.0  4046.0   96128.0  2717795.0   \n",
      "2023-01-03 13:00:00  4049.0  4059.0  4040.0  4052.0  143108.0  2719770.0   \n",
      "\n",
      "                      close_oi  \n",
      "datetime                        \n",
      "2022-12-30 14:00:00  2759241.0  \n",
      "2023-01-03 09:00:00  2713076.0  \n",
      "2023-01-03 10:00:00  2717795.0  \n",
      "2023-01-03 11:00:00  2719770.0  \n",
      "2023-01-03 13:00:00  2723678.0  \n",
      "Indicators calculated. 3863 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.98 to -1.32                        0.488889\n",
      "1     -1.32 to -0.65                        0.485437\n",
      "2      -0.65 to 0.02                        0.473290\n",
      "3       0.02 to 0.69                        0.504926\n",
      "4       0.69 to 1.36                        0.531863\n",
      "5       1.36 to 2.03                        0.493213\n",
      "6       2.03 to 2.70                        0.505155\n",
      "7       2.70 to 3.37                        0.590909\n",
      "8       3.37 to 4.04                        0.375000\n",
      "9       4.04 to 4.71                        0.400000\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 1400x800 with 1 Axes>"
      ],
      "image/png": "[encoded data, 120376 characters]"
     },
     "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": "[encoded data, 70996 characters]"
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "execution_count": 5
  },
  {
   "cell_type": "code",
   "id": "8fa62ad6",
   "metadata": {
    "ExecuteTime": {
     "end_time": "2025-06-21T18:08:35.708913Z",
     "start_time": "2025-06-21T18:08:35.375716Z"
    }
   },
   "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 3902 rows of data.\n",
      "Indicators calculated. 3863 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": "[encoded data, 69076 characters]"
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "\n",
      "Statistical summary of Log Returns:\n",
      "count    3863.000000\n",
      "mean       -0.000071\n",
      "std         0.004022\n",
      "min        -0.027787\n",
      "25%        -0.002158\n",
      "50%         0.000000\n",
      "75%         0.001947\n",
      "max         0.038023\n",
      "Name: log_return, dtype: float64\n",
      "Kurtosis of Log Returns: 5.8709\n",
      "Skewness of Log Returns: 0.2133\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 1500x1000 with 2 Axes>"
      ],
      "image/png": "[encoded data, 261832 characters]"
     },
     "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-06-21T18:08:35.997351Z",
     "start_time": "2025-06-21T18:08:35.773406Z"
    }
   },
   "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",
    "    df_analysis['pct_change'] = ((df_analysis['high'] - df_analysis['low'])).abs()\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. 3863 rows of data remaining for analysis.\n",
      "已计算 'pct_change'，共 3863 条有效数据。\n",
      "\n",
      "自相关性计算结果 (前5期):\n",
      "   Lag  Autocorrelation\n",
      "0    1         0.297475\n",
      "1    2         0.168512\n",
      "2    3         0.193238\n",
      "3    4         0.205100\n",
      "4    5         0.157244\n"
     ]
    },
    {
     "data": {
      "text/plain": [
       "<Figure size 1400x1200 with 2 Axes>"
      ],
      "image/png": "[encoded data, 177304 characters]"
     },
     "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
}
