1013 lines
1.2 MiB
Plaintext
1013 lines
1.2 MiB
Plaintext
|
|
{
|
|||
|
|
"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": "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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 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": "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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": "iVBORw0KGgoAAAANSUhEUgAABdIAAASlCAYAAACspitqAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3Xd8E/X/B/BX0l0KFAoFCkVZKZuWaWXJxgHKBgEBBzhQ9lQRBAQFFQEV5MtGRGWpTAVlyR5lFsq0hRYohQKlLW2T+/1xv0ubZt1ltQmv5+PBI+GyPpd75XJ953Ofj0oQBAFERERERERERERERGSSuqAbQERERERERERERERUmLGQTkRERERERERERERkAQvpREREREREREREREQWsJBORERERERERERERGQBC+lERERERERERERERBawkE5EREREREREREREZAEL6UREREREREREREREFrCQTkRERERERERERERkAQvpREREREREREREREQWeBd0A6hwad26NW7cuGHxPhMmTMDAgQNd0yAPdOjQIbz22mtGywMDAxEeHo4WLVpg0KBBCAkJUfzcERERAIALFy7Y3U5HkZOp/MqXL4+///7bSS1yvP79++Pw4cNYsWIFmjRpYvfzzZs3D/PnzwcAPPfcc1i4cKHJ+/32228YO3YsGjdujJUrV9r9uu5G+iyZWv/C+FmQrF+/HhMmTECXLl0wc+ZMi/fV6XRo27Ytbty4gcmTJ6NPnz5Wn/+dd97B33//jT59+mDy5Mk2tbEwv38kkvYTQ4cOxfvvvy/rMea+f/I7cuQIihUrZm8TPYal7+2wsDBER0dj4MCBqFChQgG0zrOlp6fj+eefR/HixbFx40ao1YZ9gM6ePYtVq1bh6NGjuHXrFlQqFUqWLIkyZcogKioKzZo1Q9OmTQuo9c5jy+ff3tfKS61Wo2jRoqhatSqef/559O7dGz4+Pk5th8SV30/Xr19HmzZtHHpcmpiYiD179mDfvn04e/YskpOT4ePjg/DwcDz33HMYOHAgSpYsafbxaWlp+OGHH7B9+3YkJSUhICAA9erVw6BBgxAdHW10/4yMDBw8eBB79+7F0aNHkZCQgOzsbISEhKB+/fro168fGjRoYPK1fv/9d+zbtw/nz59HcnIyHjx4AH9/f1SqVAnt2rVDv379UKRIEZvfizNnzuCHH37A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
|
|||
|
|
},
|
|||
|
|
"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": "iVBORw0KGgoAAAANSUhEUgAABW0AAAMWCAYAAACKoqSLAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XdcU1f/B/BPEoYg4kBFcaI2aFXEvbd1W1HrFqu2rlpHax+r3bZW21pr3dVqXdXWBa46qrbubd2zTgQUVEREREhyf3/wy+3NSYAQRiL5vF+v5/WUm5vcc3I/uYnfnJyjkiRJAhERERERERERERE5BLW9G0BERERERERERERE/2HRloiIiIiIiIiIiMiBsGhLRERERERERERE5EBYtCUiIiIiIiIiIiJyICzaEhERERERERERETkQFm2JiIiIiIiIiIiIHAiLtkREREREREREREQOhEVbIiIiIiIiIiIiIgfCoi0RERERERERERGRA2HRlojsLiQkBAEBAZgzZ469m+KwJk6ciICAAEycONHeTbGLVq1aISAgAKGhofZuSp4RERGBgIAABAQEICIiItePbzz2sWPHsv3+tt5GL4djx47J55HoZbJu3ToEBATg888/z9HjhIaGIiAgAK1atcrR49iLs38mInNz5sxBQEAAQkJC7N2UbJVe1vk6sE5UVBSqVauG1157DcnJyfZuDlGmudi7AUSELP3Dc9q0aejevXs2tubllZCQgLCwMBw6dAhXr17F48ePYTAYUKhQIQQEBKBRo0bo3LkzihUrZu+myo4dO4bjx4+jVKlSTnkeQ0NDERkZiXr16qF+/fr2bk62mzhxIsLCwsy2u7u7o2jRoggMDESPHj3QtGlTO7SOlOLj47F8+XIAwJtvvglvb287t8hxffLJJ1i3bh0KFSqEAwcOwM3Nzar7tW3bFnfu3EHLli3x008/5XArnU9a15uM1KtXDytXrsyBFpHSs2fPMGvWLLi5uWHkyJFmt1s6f2q1Gp6enihQoADKlCmDKlWqoEmTJmjSpAnU6rw39sbZPhPp9Xq0aNECMTExAIBffvkFjRs3tnOrHMvly5exe/duFChQAIMGDbJ3c7Lkxo0b6NixIwAgX758OHToELy8vOzSljlz5mDu3Lkm21QqFTw9PeHl5QU/Pz9UqVIF9evXR6tWrax+n8+s3Pjs5efnh+7du2PNmjVYvXr1S58jcj4s2hI5gKJFi1rcnpiYiMTExHT3yZcvX46162Wybt06TJ8+HU+ePJG35cuXD25uboiOjkZ0dDT279+PmTNnYuTIkRb/wWQPx48fx9y5c1GvXr10/4FSrFgx+Pv7O1TBOTuEhYXh+PHjePfdd9Mt2pYpUwZubm4oUKBALrYu+6jVahQpUkT++8mTJ4iMjERkZCS2b9+ON954A1OmTIFKpbJjK18u/v7+AAAPD49suV98fLz8D5hu3bqxaJuON954A+vWrUNcXBx2794t/yM0PcePH8edO3fk+1P28/LySvOzgigpKQkJCQkAkGP/GCdTS5YswYMHD9C/f3+UKFEizf3E94vExETcu3cP9+7dw/Hjx7F8+XKULFkSkyZNQrt27Sw+RoECBeDv7w9fX99s70dOcrbPRPv375cLtgCwYcMGFm0Fly9fxty5c1GqVKl0i22FCxeGv78/SpYsmXuNy6T169fL/52UlIStW7eiT58+dmxRKuX7RlJSEmJiYhAdHY3Tp09j9erVKFSoEMaNG4e+fftm+7Fz67PXiBEjEBoaigULFqB79+78jEcvFRZtiRzAoUOHLG5Xfgua1j4EzJw5Ux61VbFiRQwbNgxNmzaFj48PgNQPICdPnsSmTZvwxx9/YMeOHQ5TtLXW+PHjMX78eHs3w26M38K/rEqWLIm//vpL/lun0+HixYv48ssvceHCBaxfvx6vvvoq+vfvb8dWvlx27NiRq/ej/wQFBaFSpUq4fv06QkNDrSraGqc2KVq0KFq0aJHDLXROn3zyCT755JMM90tOTka/fv1w/vx5eHp6YsKECbnQOueWlJQkj2bOqPAhvl8Aqefs6tWr2LdvH3777Tfcu3cPY8aMwfDhw/H++++bPcZrr72G1157Lfs64GDyymciYxGvf//+WL16NXbt2oW4uDgUKlTIvg17CQ0YMAADBgywdzPSlJKSgk2bNgFInRZu5cqVWL9+vUMUbcV/Y+r1ely/fh2HDx/Gr7/+ioiICHzxxRc4efIkvv/++5dygIGfnx+aNWuGPXv2YN26dXjrrbfs3SQiq+W939UQkVPZtm2bXLDt0KEDNm7ciODgYLlgC6SOuG3SpAmmT5+OjRs3olKlSvZqLhEAwMXFBTVq1MCiRYvkf5z9+uuv9m0UUSYYR8seOnQI0dHR6e6bkJCAnTt3AgC6du0KFxeOGbCnzz//HOfPnweQOsUS5wbOedu2bUN8fDyqVKmCV155JdP3d3NzQ/Xq1fHuu+9i69at8i9TFi5ciC1btmR3cykXPHz4EHv37oVGo8Hw4cNRt25dJCcn83zmUX///TcePXqEihUr4v3334enpyfOnz+Pa9eu2btpZjQaDQICAjB48GBs3boVnTp1AgBs3boVixYtsnPrbPf6668DANauXQtJkuzcGiLrsWhL9BJTLqjz6NEjTJs2De3atUONGjUs/iNs7969GD16NJo2bYpq1aqhbt268rf7aU3MrlwkTJIkrF27Fj179kStWrVQs2ZN9O7dW/7mOC16vR4rV65Et27dEBQUhHr16iEkJCTLI96Sk5Px3XffAQAqVaqEb7/9NsOfeWq1WkyfPt3ibZcuXcKECRPQsmVLVK9eHXXr1kWfPn2wbNmyNJ8fcbGPCxcuYOzYsWjSpAmqVauG1q1bY9q0aSbTNgD/LQJlHEl9/Phx+Xwa/6dcdCu9xQayeo6sWZgpvcXi7t69i0WLFuGtt95Cu3btEBQUhJo1a6Jjx474+uuvERUVlebzdvz4cQDA3LlzzfqvXBwro4XI9Ho91q9fj4EDB6J+/fqoVq0amjZtijFjxljdL1vznRU+Pj5o0qQJAODmzZt49uwZAPOFli5duoTx48ejWbNmqFq1qtlCGw8ePMC3336LTp06ISgoCEFBQejUqRO+++47PHz40Kq23L59GxMnTkSzZs1QrVo1tGjRAp999lm6BbkzZ85g+vTp6Nevn/y6qVOnDnr16oVFixbJ/cnIgwcP8OWXX6JVq1aoXr06GjdujPHjx+PGjRtp3sfWBcUs3S8kJAStW7eW/27durVJFo3P93vvvYeAgAAMHTo03WPcuXMHlStXtrp9KSkpqF+/PgICArBixYp0912/fj0CAgJQq1YtPH/+XN6u0+mwZs0ahISEoH79+qhatSrq16+Pdu3aYdy4cVi3bl2G7ciMrl27wtXVFQaDIcMFArdv3y5P9dOjRw+T22y57qbHmsVo0lvITLz/nj178Oabb6J+/fqoVasW+vTpg927d5vcZ+PGjejTpw/q1q2LmjVron///jhy5EiGbbXlPTmrVqxYIZ+vESNGoH379jY9TlJSEpYsWYLevXujbt26qFq1Kho0aICOHTviww8/lIv0lty4cQOTJ09Gx44dUbNmTdSsWRPt2rXDe++9h507d8JgMJjd58WLF1i2bJn8PFevXh0tW7bEhAkTcPny5TSPpXzvMM4p26VLF9SsWdPiIoynTp3CBx98IOexdu3aeOONNzJ1PbNk7dq1AIDOnTvb/BhGhQsXxty5c+WpD3788UekpKSY7JPeQmRixnfu3IkhQ4agYcOGqFy5stn7fGxsLGbOnIng4GDUrl0b1atXR+vWrfHRRx/h33//TbetBoMB27ZtwzvvvCPnvEGDBujevTumT58uF6yy8zOR0bFjxzBmzBj5uPXr18ebb76JDRs2QK/XW7yP+NwcOXIEw4YNQ4MGDVC9enV06NABc+fOxYsXL9LttzU2btwInU6Hhg0bwtfXF926dQOQOkWCNU6cOIERI0agfv36CAwMRLt27TBz5kw8e/Ys3fMvPnc7duxASEgI6tWrhxo1aqBr165Yvny5xde
|
|||
|
|
},
|
|||
|
|
"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+naQAAz2lJREFUeJzs3XeYVeW9Pvx7hgEUKYoICGiMBbGAqKjRaIwlmkSTiJhjibEejUaMscYWe0tONBobVoweS6z5xd57R1GjYqyogHQUEKTNfv/gnX0YmsMwA0vn87kur4v9rLX3/n5nrVkz3vPsZ1WUSqVSAAAAAAAohMqlXQAAAAAAAP9HaAsAAAAAUCBCWwAAAACAAhHaAgAAAAAUiNAWAAAAAKBAhLYAAAAAAAUitAUAAAAAKBChLQAAAABAgQhtAQAAAAAKRGgLAN9wv/71r7P22mvn4osvXtqlLFVTp07NhRdemJ/85Cfp1atX1l577ay99toZMmTI0i6NOnIuMz8138svvvji0i4FlgjnPABJUrW0CwCAxnDxxRfnkksuSZIss8wyeeihh9KpU6f57jts2LBst912SZLrr78+m2222RKrk4Zz5JFH5vHHH08y+5h36NAhSVJVVbdfd37961/npZdeqjXWrFmzLLfccmnTpk1WW221rLPOOtlmm23Sp0+fhi1+DkOGDMkjjzySNm3aZL/99mu091mYOb8n5lRZWZk2bdrku9/9brbaaqvstddead++/VKocNHVBMF9+/ZNt27dlnI13xz/+te/cuyxxyZJbr/99vTs2bNOzzvxxBNzxx13ZPnll8/TTz+dFi1aNGaZTc6dd96ZE044oV7P/c9//tPA1VBjQdfOJGnevHnatWuX7t27Z8cdd0y/fv3SvHnzJVwhAN8kQlsAvvW++uqrXHrppTnjjDOWdik0kg8++KAc2P71r3/NT3/603q/Vs3/WNf48ssvM3z48AwfPjzPPvtsrr766qyxxho57bTTsummmy527XMbMmRILrnkknTt2nWphbZzat26dZZZZpkkyYwZM/LFF1/ktddey2uvvZabbropV1xxRZ2DvK+z8sor57vf/W5WWGGFBnm9OdX8EWfTTTcV2i6CHXfcMWeeeWYmTpyYO+64o07HesqUKbn//vuTJL/4xS8Eto1gzj9MfZ1Zs2ZlwoQJSeJYLEFzXjuTZPLkyRk7dmzGjh2b5557LrfeemuuvfbaLL/88vM897vf/W6SZNlll11S5QJQQEJbAJqEO+64I/vvv3/5f4T4dnn33XeTJMsvv/xiBbZJsuGGG+aGG26oNfbVV1/l7bffzkMPPZTbbrstH3zwQfbZZ5+ceuqp2XPPPRfr/YrupJNOyq677lp+/MUXX+Smm27KpZdemnHjxuXwww/Pgw8+mJYtWy72e/35z39e7NegYbVs2TI777xzbrrpptx777054YQTvvZYP/DAA5kyZUqSpF+/fkuizCbnpz/9aZ2vdWeddVb5mnbqqac2ZlnMYe5rZ5KMGjUq11xzTf7+97/nrbfeygUXXDDfPyg/8MADS6pMAArMmrYAfKutvPLKWXvttTNz5sz89a9/Xdrl0Ei++uqrJMlyyy3XKK+/zDLLZKONNsrxxx+ff/3rX1l77bVTKpVy5plnZtCgQY3ynkXVrl27HHroofnv//7vJMlnn32WRx99dClXRWPabbfdkiQTJ07Mww8//LX733HHHUmSnj17Zu21127U2li4O++8sxzY7r333uVjydLRqVOnnHjiidl8882TJI888shSrgiAIjPTFoBvtcrKyhx99NE5+OCD8+CDD+aNN95Ir1696vz8Odene/TRRxf4septt902w4cPz7nnnltrZs3cz6+oqMjll1+eZ555JuPGjUunTp2y00475Te/+U1atWqVZPas0SuvvDIvv/xyxo8fn5VXXjm77LJLDjrooK9d/2769Om57rrrcvfdd+fTTz9N8+bNs/7662e//fbL1ltvvdDnvvvuu7nhhhvy4osvZtSoUamsrEy3bt2y7bbbZt99953v2qU1awdvuummueGGG/Lggw/mH//4R4YMGZIJEybksMMOy+GHH77Q953TtGnTcvPNN+eBBx7IBx98kK+++iodOnTIJptskv333z/rrLPOfN+/xvDhw2uFRH379s15551X5/evi65du+ayyy7Lz372s0yZMiUXXHBBbrrpplr7TJ06NY8++mieeuqp/Oc//8moUaMyefLkLL/88unVq1d23333+R6POWufu5ck6d+/f/nrWd/3aCi/+MUvcvnllydJ/v3vf9ea9beox7FGzbrCc/ZZY87vsZ133jnXX399/vWvf+WTTz5Js2bNst566+W///u/84Mf/KDW844//vjcdddd5cf77LNPre1du3bNY489Vn48cuTIXHvttXn22WczfPjwzJw5M8svv3w6duyYPn36ZOedd16sa8jMmTMzYMCAPPfccxk/fnw6dOiQH/zgBznssMMWuO52klRXV+eee+7J3XffnbfeeisTJ05M69ats+6662bXXXfNTjvtlIqKinmeN+fXbccdd8zVV1+dRx55JMOGDcuUKVMWel2rsd5662WdddbJkCFDcscdd2TnnXde4L4ff/xx+Q8ZcweEY8aMybXXXpunnnoqw4cPTzL767/11lvngAMOqPPH/Wu8+OKL5eO5sHVaa76P5l6zfO7nv/POO7nyyivz0ksvZeLEienatWt222237LvvvuW1sV955ZVcc801eeONN/LFF1/kO9/5Tvbcc8/stdde8/3616jP9XVxvfHGG+WZtZtuumm918BNkvvuuy933nln3n777XzxxRdZdtll0759+6y++urZaqutsttuu813BvaECRNy44035sknn8zHH3+cqVOnZqWVVspqq62W7bffPj/72c/Spk2beZ730EMP5Y477si///3vTJw4MW3btk3Pnj2z22675Uc/+tF8a6z5Xu/bt2/OPffc3H777bnzzjvz4Ycf5vPPP5/vz+e///3vee655zJixIhUV1dn5ZVXzpZbbpkDDjggXbp0qffX6+uss846ef7558sz0ue2oHN27uvJMssskwEDBuSxxx7LmDFj0qZNm2y22Wbp379/1lhjjUarH4AlQ2gLwLfe1ltvnU033TQvvfRS/vKXv+T6669fKnW8/fbbOemkk8phy6xZs/Lpp59mwIABGTRoUK677ro8++yz+f3vf5+pU6emTZs2mTFjRj7++ONcdNFFee+99xY6W3jGjBnZf//9M2jQoFRVVaVVq1aZOHFinnvuuTz33HPzDcJqXHXVVbngggtSXV2dZPY6ejNmzMi7776bd999N3fccUeuvPLKrLvuugt8//POOy8DBw5MRUVF2rZtm8rKRftAz6hRo/Lf//3f5aUOmjdvnmWWWSYjRozI//t//y933313TjzxxPz6178uP6dVq1bp0KFDvvrqq0yePDmVlZW1wo/WrVsvUg111a1bt/Tt2zc33nhjXnnllXz66adZZZVVytvvv//+ckBSUVGR1q1bp6qqKmPGjMmjjz6aRx99NAcccED+8Ic/1HrdhfVS0+/ivkdD6dy5c/nfkydPLv+7PsdxUUyZMiV77713Xn/99TRv3jzNmzfP5MmT8+KLL+all17KWWedVSssbN26dTp06JCxY8cmmT1TeM4/fsy5fu4777yTffbZJ1988UWS2Teia926dcaOHZsxY8aUw9JFCW3n9MYbb+Tkk0/Ol19+mVatWqVZs2b57LPP8o9//CMPPvhgrr322qy33nrzPO/zzz9P//798/LLL5fH2rRpkwkTJuTZZ5/Ns88+m3vvvTcXXXTRAtcs/fzzz7Prrrtm6NChad68+SKvlbnbbrvlzDPPzAsvvJARI0YsMNC68847k8yenT5nuPvSSy/lsMMOy8SJE5P837n8/vvv5/3338/tt9+eyy67rFFv8rcwTz75ZA4//PBMmzYtbdq0yfTp0/Phhx/mz3/+c/lj7LfddltOPfXUVFdXp3Xr1pk+fXree++9nHHGGfnss89yzDHHzPe1G+L6uqj
|
|||
|
|
},
|
|||
|
|
"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": "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98UXX+DKK6/EkUceiQkTJuCwww7DiSeeiBtuuAFr1qzp+5vZwfPPPw8AmDt3bkhfBwAmk8lYKtjx+tOOY2nFihW44IILcPjhhwf8Lujre7tz507ceeedOOmkkzB58mRMmzYNp5xyCu68805s3bq1y/MbGxvx6KOP4owzzsDUqVMxefJknHbaafjrX/8Kh8MRUr9eeOEFNDU1ITs7O6jrZg477LAeHwvld0lFRQWeeuopXHDBBfjpT3+KkpISHHroofjFL36Bl19+GZqmdfma8vJyFBcX49hjj4UQAq+88grOOussTJkyBYcccgguvvhirF27tsf2bd26Fddeey2mT59ufM/++c9/QtO0Xv8eeL1evPbaa5g/f77xO/bYY4/FHXfcgaqqqj6/Z6Hoa+z/5z//we9//3uceuqpOOywwzBx4kTj78gPP/zQ5fnFxcXGjNLjjz8eMPYXLlwYkX7OnTsXmqbhpZdeCqPnNFBwWSBRiH7yk58gKysLDQ0N+OKLL1BSUtLr8zVNw+WXX45Vq1YhPT0dhx56KDIyMlBfX49du3Zh8eLFOO2005CVlYWRI0di7ty5+Pe//422tjacdNJJSE1NNc7V+Rodl8uF+fPnY8eOHTj00EMxbtw4o1ALxvLly/HCCy/gwAMPxMyZM1FdXY1vvvkGCxYswObNm7v8YeqPk046CaWlpfj2228xcuRIHHLIIcZjBx54YJ9fL4TAwoUL8fbbb8NsNuPQQw9FdnY2Nm7ciDfffBMffPABHn30UfzkJz/p9uvfeOMN/O1vf8P48eNx9NFHo6KiAqWlpViwYAEaGhpw0UUXBd0Xt9uNK664Al988QVSUlIwffp0pKenY+3atXjuuefw/vvvY/HixZgwYQIA4OCDD8bcuXPxzTffYPfu3QHX8wXT9/5Yt24dLrvsMjQ0NKCgoADHH388mpubsXr1aqxduxbLly/H3/72N1it1oCvu+uuu/DSSy9BVVUceuihyM3NxdatW3H++edH7LqIjjRNQ1tbGwB0mUXwer244YYb8MEHH8BqtWLChAnIz8/Hjz/+iPfeew/Lly/HY489ZmSuf5/feustAF0Li45jKBzBjLfm5macd955qKqqwiGHHIKDDjoIpaWlePvtt7FmzRq88847AbOzb731lvHhw6RJkzB9+nQ4nU7s3bsXy5Ytw+DBg3t9s9+5fZ9//jksFkvQX9NZS0sLAHT5+QCAZ599Fs8//7xR6FZXV8NkMvV5zvfeew+33HIL3G43CgoKMGvWLGiahrKyMrz88svIzs5GUVGR8fzt27fj0ksvRVVVFXJzc3HIIYfAbDZj/fr1eOSRR/Cf//wHzz33XNCz3B999BEA4JRTTum2X8EK9XfJO++8g0ceeQQjRozAAQccgGnTpqGmpgZr167Ft99+i//973949NFHoShKt69388034/3338chhxyCn/70p9i0aRP+97//Yc2aNXj++ecxefLkgOevXr0al112GZxOJ0aOHImjjjoKDQ0NePDBB/Hdd9/12K+Wlhb86le/wurVq5GamoqSkhIMHjwYW7duxcsvv4wPP/wQzz77LMaPH9/v711HvY19APj1r38Nq9WKMWPG4IgjjoDX68W2bdvw5ptv4sMPP8TixYsxbdo04/lz587Fpk2bsHnz5oBrCQE
|
|||
|
|
},
|
|||
|
|
"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": "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
|
|||
|
|
},
|
|||
|
|
"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": "iVBORw0KGgoAAAANSUhEUgAABW0AAAScCAYAAADwLq27AAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAAPYQAAD2EBqD+naQABAABJREFUeJzs3XlcVOX7//H3gCCuKChKmmYquID7vhZpmku5pKaiuWTlJ1Nb1eqT8tWyvVzKFrdcKs2dNDX1Y6mhaGruWS65hBu4oILiML8/+M3EyCAzODAz8Ho+Hj2Sc87c93XOnDPLNfe5boPJZDIJAAAAAAAAAOAWvFwdAAAAAAAAAADgXyRtAQAAAAAAAMCNkLQFAAAAAAAAADdC0hYAAAAAAAAA3AhJWwAAAAAAAABwIyRtAQAAAAAAAMCNkLQFAAAAAAAAADdC0hYAAAAAAAAA3AhJWwAAAAAAAABwIyRtAQBwExEREQoNDbX6LywsTA888IBGjhypHTt2ZKvd0aNHKzQ0VEuWLHFyxM518eJFffHFF+rXr5+aN2+usLAw1atXT506ddIbb7yhmJiYDI8xHydkz4ULFxQWFqbQ0FB169bN1eHAhm3btik0NFT9+vXLtT495TUjvVmzZlleD+bOnevqcKxMmTJFoaGhmjJlSr7qGwAA3B2StgAAuJl69eqpa9eu6tq1q1q1aqXU1FT9+OOPioyM1KxZs1wdXo5YtmyZIiIi9NFHH2n37t2677779PDDD6tJkya6deuWvv/+ew0YMEAjRoxwdah5yrJly5SSkiJJ2r9/vw4dOpQj/fTr10+hoaHatm1bjrQP+y1ZskShoaEaPXq0q0NxqkWLFln+vXjxYhdGAgAA4BwFXB0AAACw1qNHD6tRjzdu3NCbb76pZcuW6f3339cDDzygSpUq2d3eiy++qCFDhigoKCgnwr1r3377rcaNGyeDwaAhQ4bo2WefVdGiRa22+euvvzRlyhT9/fffLooybzInt8qUKaOzZ89q0aJFeuONN1wcFVzN3V8zbrd792799ddfKl68uG7duqWDBw9q//79qlmzpqtDc7m+ffuqQ4cOKlmypKtDAQAADmKkLQAAbq5gwYJ68803VbhwYRmNRv30008OPT4oKEiVK1dWsWLFcijC7Dty5IjeeustSWm3ZL/88ssZEraSVKVKFU2aNEmvv/56boeYZ/322286evSo/P399fbbb0uSoqOjdfPmTRdHBldz59cMW8yjbDt27Kj27dtbLcvvAgICVLlyZQUEBLg6FAAA4CCStgAAeIAiRYpYRteeOnXKsjx9TdfFixerV69eql+/vkJDQy3bZVWfct++fRo1apQiIiIUHh6uRo0a6dFHH9W7776r06dPZ9j+7Nmzmjhxoh555BHVrl1bdevWVffu3TVv3jzdunXLof2aPn26UlJSVK1aNT355JNZbt+wYcNM161Zs0a9e/dWvXr1VKdOHT3xxBP6+eefbW77119/afLkyXriiSfUsmVLhYWFqXHjxhowYIBWrVpl8zHpa4umpKToyy+/VMeOHVWrVi01btxYw4YN05EjRzKNb8eOHRo8eLAaNGhgOWbLli2TdOfavMnJyZo5c6Z69uypBg0aKDw8XO3atdN7772nixcvZtpfVsxJrc6dO6t58+aqWLGiLl26lOmPAqdOnVJoaKgiIiIybdNcl9l87pmPWWxsrCSpf//+VjWbbz8njxw5ojFjxujBBx9UWFiYGjVqpCeffDLT58TM0XN4z549GjFihFq0aKGwsDA1bdpUzz77rLZs2WKz/fTX0OHDhzVy5Ei1aNFC1atXt9QKTV8CYseOHXr22WfVpEkTVatWzWo/nfl8/vrrrxo/frwee+wxNW7cWGFhYWrVqpVGjhypPXv2ZNg+IiJCY8aMkSQtXbrU6rlIXzM3q9eMlStX6sknn1SjRo0UFhamBx98UGPGjNGxY8dsbp/+vNi6dasGDRqkhg0bqlatWuratavlOsiO69evW86Pxx9/XN27d5ck/fDDD7px44bNx6QvEXH9+nV9+OGHatu2rcLCwtS8eXONGjVKZ8+etfnYtWvX6vXXX1enTp3UsGFDhYeHW47r0aNH7Y578uTJCg0N1ZtvvpnpNnv27FFoaKhatmxp9dr666+/6tlnn1WzZs1Us2ZNNWzYUA8//LBefvllbd++3aqNO9W0/fHHHzVgwAA1btxYNWvWVOPGjdWhQwe98cYbOVYqBQAA2I+kLQAAHuLq1auSJF9f3wzrxo8frzfeeEPe3t564IEHVLt2bRkMhizbnD59unr06KFly5bJx8dHDz30kOrVq6dbt25p5syZGWqQbt++XZ07d9bs2bN148YNNWvWTPXq1dPJkyc1fvx4PfPMM5YaqVkxmUzasGGDJKlLly52xZuZyZMnW+rdtm7dWhUrVtSuXbv0zDPP2ExCzpo1S59++qkuX76skJAQtW3bVpUqVdK2bdv0wgsvaOLEiZn2lZKSoqefflqfffaZgoOD9cADD6hQoUL66aef9MQTT1gl1c1Wrlypfv36afPmzQoODlZERIQKFSqkMWPG6IMPPsi0r7Nnz6pHjx5699139ffffys8PFytW7dWSkqKZsyYoe7du9tMSmbl6tWrWr16tSSpe/fuMhgMlpIczqwHWqpUKXXt2lWlSpWSJLVo0cJSr7lr166qUKGCZduNGzeqa9euWrJkiQoWLKiHH35Y1atX1/bt2/XCCy/otddes9mHo+fwwoUL1atXL61evVqlS5dWu3btVLFiRf3vf//ToEGDNHXq1Ez3Z9euXerevbv27NmjBg0aqHXr1ipSpIjVNqtXr1a/fv108uRJNWvWTM2bN7dcs85+PseOHasFCxbIy8tL9erV04MPPqhixYrpxx9/VO/evbVmzRqr7du1a6d69epJkipUqGD1XLRs2TLL/kwmk0aNGqUXX3xRO3bsUPXq1fXwww/L19dXS5YsUdeuXfXLL79k+vjFixdrwIABunTpklq2bKnq1avrwIEDGjVqlGbPnm33fqe3atUqXbt2zTJxY4MGDXTffffpypUrWrt27R0fm5iYqCeeeELfffedKleurFatWslkMmnZsmXq3bu3EhMTMzxm5MiRWrlypQoWLKgmTZqoRYsW8vLy0pIlS9S9e3ft3LnTrrh79+4tHx8fRUdH68qVKza3mT9/viSpV69eKlAgrard0qVLNWjQIG3cuFHly5fXww8/rAYNGqho0aJatWpVlvtsNnXqVI0cOVLbt29X1apV1b59e9WuXVve3t5atGiRtm7dalc7AAAgB5kAAIBbePDBB00hISGmxYsXZ1h38OBBU7Vq1UwhISGmRYsWWZaHhISYQkJCTPXq1TPt2rXLZrujRo2y2e66detMISEhpvDwcNPKlSszPO7PP/80/fXXX5a/z507Z2rUqJEpNDTUNH/+fJPRaLSsS0hIMPXv398UEhJimjJlil37e+LECUv827dvt+sxtzM/vkGDBqbdu3dbrZs8ebIpJCTE9PDDD2d43LZt20wnTpzIsPzIkSOmVq1amUJCQky///671bqtW7da+uvSpYvp3LlzlnXJycmmQYMGmUJCQkz//e9/rR535swZU506dUwhISGmr7/+2mpdbGysZV1ISIjVutTUVNMTTzxhCgkJMb322mumxMREy7qUlBTTO++8YwoJCTH169cvi6OU0YIFC0whISGmxx57zCrO6tWrm6pVq2Y6depUhsecPHnSFBISYnrwwQczbdd8Dp88edJqeWRkpCkkJMS0detWm487f/68qX79+qaQkBDTZ599ZkpNTbWs27Nnj6lhw4amkJAQ04IFC6we5+g5fOjQIVONGjVMoaGhpqVLl1ptu3HjRlPNmjVNISEhps2bN1utM19DISEhpg8++MDq3L99H0NCQkzz5s3LsD67z6f5vIuMjMzQ5k8//WS6dOmSzeU1atQwNWrUyJSUlGS1bvHixaaQkBDTqFGjMjzu9v29/TXjm2++MYWEhJgaN25sOnDggNW+ma+3Bg0amOLj460eZz4vatasadqwYYPNeOr
|
|||
|
|
},
|
|||
|
|
"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
|
|||
|
|
}
|