量化对冲策略计算绘图
代码提要
代码运行环境,WIN 11 ,python 3.13
- 使用
akshare
获取股票数据,并用pandas
的ewm
函数 - 添加了
akshare
库获取真实股票数据 - 主程序中使用
ak.stock_zh_index_daily
获取上证指数数据 - 增加了异常处理,当获取数据失败时使用随机数据作为备选
- 确保数据列名与原代码兼容
- 截取最近200个交易日的数据进行计算
使用说明:
- 需要安装依赖库:
pip install pandas numpy akshare
- 默认获取上证指数数据,可以通过修改
ak.stock_zh_index_daily(symbol="sh000001")
中的symbol
参数获取其他股票数据 - 股票代码格式示例:
- 上证指数: sh000001
- 深证成指: sz399001
- 创业板指: sz399006
- 个股: sh600000(浦发银行) 或 sz000001(平安银行)
- 本代码股票代码 sz000937 (冀中能源)
下面是添加了绘图功能的完整代码,使用matplotlib
绘制股票价格和各项量化指标的图表:
import pandas as pd
import numpy as np
import akshare as ak
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
from matplotlib.ticker import MaxNLocator
import matplotlib.patches as mpatches
import warnings
import matplotlib.font_manager as fm
import os
# 忽略警告
warnings.filterwarnings('ignore')
# 设置全局字体
font_path = 'C:/Windows/Fonts/simhei.ttf' # 黑体字体路径,根据实际情况修改
if fm.findfont(fm.FontProperties(fname=font_path)):
plt.rcParams['font.family'] = fm.FontProperties(fname=font_path).get_name()
else:
print("未找到指定字体,请检查路径。将使用默认字体。")
# 解决负号显示问题
plt.rcParams['axes.unicode_minus'] = False
def quant_hedge_strategy(df):
# 主力暗盘资金副图指标
df['量化对冲1'] = df['close'].ewm(span=9, adjust=False).mean()
df['量化对冲3'] = (df['量化对冲1'] * 1.14).ewm(span=5, adjust=False).mean()
df['量化对冲4'] = df['close'].ewm(span=2, adjust=False).mean()
# 计算130日高低点
df['130日高点'] = df['high'].rolling(130).max()
df['130日低点'] = df['low'].rolling(130).min()
# 计算条件柱状图
df['stickline_cond'] = np.where(df['量化对冲4'] >= df['量化对冲3'], 1, 0)
# 计算其他指标
df['40日最低'] = df['low'].rolling(40).min()
df['量化对冲2'] = (df['close'] - df['40日最低']) / df['40日最低'] * 100
df['MA40'] = df['close'].rolling(40).mean()
df['量化对冲7'] = (df['close'] - df['MA40']) / df['MA40'] * 100
df['40日最低_MA40'] = df['40日最低'].rolling(40).mean()
df['量化对冲8'] = (df['close'] - df['40日最低_MA40']) / df['40日最低_MA40'] * 100
df['量化对冲9'] = 28
df['量化对冲10'] = df['close'].rolling(5).mean()
df['100日最高'] = df['量化对冲10'].rolling(100).max()
df['量化对冲11'] = np.where(df['量化对冲10'] == df['100日最高'], df['量化对冲8'], np.nan)
# 成本分布计算(简化为使用移动平均)
df['cost90'] = df['close'].rolling(90).mean().shift(1)
df['cost10'] = df['close'].rolling(10).mean().shift(1)
df['量化对冲12'] = ((df['close'] * df['volume'] - df['cost90'] * df['volume']) / 20 + 250) * 1.2 / 5
df['量化对冲13'] = ((df['cost90'] * df['volume'] - df['close'] * df['volume']) / 20 + 250) * 1.2 / 5
df['量化对冲14'] = ((df['close'] * df['volume'] - df['cost10'] * df['volume']) / 20 + 250) * 1.2 / 5
df['量化对冲15'] = ((df['cost10'] * df['volume'] - df['close'] * df['volume']) / 20 + 250) * 1.2 / 5
# 主力暗盘资金副图指标信号条件
df['量化对冲5'] = ((df['量化对冲14'] > 0) &
((df['量化对冲12'] > df['量化对冲13']) |
(df['量化对冲12'] > 0)))
df['量化对冲6'] = ((df['量化对冲14'] > 0) &
(df['量化对冲12'] > 0) &
(df['量化对冲13'] < 0) &
(df['量化对冲15'] < 0))
df['量化对冲5_count'] = df['量化对冲5'].rolling(10).sum()
df['量化对冲6_count'] = df['量化对冲6'].rolling(60).sum()
df['量化对冲16'] = ((df['量化对冲5'] & (df['量化对冲5_count'] == 1)) |
(df['量化对冲6'] & (df['量化对冲6_count'] == 1)))
df['量化对冲16_count'] = df['量化对冲16'].rolling(20).sum()
df['起飞信号'] = np.where(df['量化对冲16'] & (df['量化对冲16_count'] == 1),
df['量化对冲2'] * 1.16, np.nan)
# 妖股信号
df['量化对冲14_60日高点'] = df['量化对冲14'].rolling(60).max()
df['量化对冲14_20日高点'] = df['量化对冲14'].rolling(20).max()
df['妖股条件1'] = df['量化对冲14'] >= df['量化对冲14_60日高点']
df['妖股条件2'] = (df['量化对冲14'] >= df['量化对冲14_20日高点']).rolling(20).sum() == 1
df['妖股条件3'] = df['量化对冲14'] > df['量化对冲13']
df['妖股信号'] = np.where(df['妖股条件1'] & df['妖股条件2'] & df['妖股条件3'],
df['量化对冲2'] * 1.16, np.nan)
# 交叉信号
df['交叉信号'] = np.where(df['量化对冲2'].shift(1) < df['量化对冲9'],
df['量化对冲2'], np.nan)
return df
def plot_quant_strategy(df, stock_name="上证指数"):
"""绘制量化对冲策略图表"""
# 创建一个包含4个子图的图表
fig, axes = plt.subplots(4, 1, figsize=(16, 18), sharex=True)
fig.suptitle(f'{stock_name}量化对冲策略分析', fontsize=16)
# 绘制K线图和均线
ax1 = axes[0]
ax1.set_title('价格走势与均线', fontsize=14)
ax1.plot(df.index, df['close'], label='收盘价', color='black', linewidth=2)
ax1.plot(df.index, df['MA40'], label='MA40', color='blue', linestyle='--')
ax1.plot(df.index, df['量化对冲10'], label='MA5', color='red', linestyle='--')
# 绘制起飞信号
signal_dates = df[~df['起飞信号'].isna()].index
signal_values = df[~df['起飞信号'].isna()]['close']
ax1.scatter(signal_dates, signal_values, color='green', s=100, label='起飞信号', marker='^')
# 绘制妖股信号
妖股_dates = df[~df['妖股信号'].isna()].index
妖股_values = df[~df['妖股信号'].isna()]['close']
ax1.scatter(妖股_dates, 妖股_values, color='purple', s=100, label='妖股信号', marker='*')
# 绘制交叉信号
cross_dates = df[~df['交叉信号'].isna()].index
cross_values = df[~df['交叉信号'].isna()]['close']
ax1.scatter(cross_dates, cross_values, color='orange', s=100, label='交叉信号', marker='o')
ax1.grid(True)
ax1.legend(loc='upper left')
ax1.set_ylabel('价格')
# 绘制量化对冲2、9、11指标
ax2 = axes[1]
ax2.set_title('量化对冲指标2、9、11', fontsize=14)
ax2.plot(df.index, df['量化对冲2'], label='量化对冲2', color='blue')
ax2.plot(df.index, df['量化对冲9'], label='量化对冲9', color='red', linestyle='--')
ax2.plot(df.index, df['量化对冲11'], label='量化对冲11', color='green', marker='o', linestyle='', alpha=0.7)
# 填充量化对冲2大于量化对冲9的区域
ax2.fill_between(df.index, df['量化对冲2'], df['量化对冲9'],
where=(df['量化对冲2'] > df['量化对冲9']),
color='lightgreen', alpha=0.3)
# 填充量化对冲2小于量化对冲9的区域
ax2.fill_between(df.index, df['量化对冲2'], df['量化对冲9'],
where=(df['量化对冲2'] < df['量化对冲9']),
color='lightcoral', alpha=0.3)
ax2.grid(True)
ax2.legend(loc='upper left')
ax2.set_ylabel('指标值')
# 绘制量化对冲12、13、14、15指标
ax3 = axes[2]
ax3.set_title('主力暗盘资金指标', fontsize=14)
ax3.plot(df.index, df['量化对冲12'], label='量化对冲12', color='blue')
ax3.plot(df.index, df['量化对冲13'], label='量化对冲13', color='red')
ax3.plot(df.index, df['量化对冲14'], label='量化对冲14', color='green')
ax3.plot(df.index, df['量化对冲15'], label='量化对冲15', color='purple')
# 绘制起飞信号和妖股信号在这张图上的位置
ax3.scatter(signal_dates, df.loc[signal_dates, '量化对冲14'], color='green', s=50, marker='^')
ax3.scatter(妖股_dates, df.loc[妖股_dates, '量化对冲14'], color='purple', s=50, marker='*')
ax3.grid(True)
ax3.legend(loc='upper left')
ax3.set_ylabel('资金指标')
# 绘制量化对冲5、6指标柱状图
ax4 = axes[3]
ax4.set_title('量化对冲信号5、6', fontsize=14)
# 绘制柱状图
bar_width = 0.6
quant5_positive = df['量化对冲5_count'].copy()
quant5_positive[quant5_positive < 0] = 0
quant6_positive = df['量化对冲6_count'].copy()
quant6_positive[quant6_positive < 0] = 0
ax4.bar(df.index, quant5_positive, width=bar_width, color='blue', alpha=0.7, label='量化对冲5')
ax4.bar(df.index, quant6_positive, width=bar_width, color='green', alpha=0.7, label='量化对冲6')
# 添加信号标记
ax4.scatter(signal_dates, [0]*len(signal_dates), color='green', s=100, marker='^')
ax4.scatter(妖股_dates, [0]*len(妖股_dates), color='purple', s=100, marker='*')
ax4.grid(True)
ax4.legend(loc='upper left')
ax4.set_ylabel('信号强度')
ax4.set_xlabel('日期')
# 设置x轴日期格式
locator = mdates.AutoDateLocator()
formatter = mdates.ConciseDateFormatter(locator)
for ax in axes:
ax.xaxis.set_major_locator(locator)
ax.xaxis.set_major_formatter(formatter)
plt.tight_layout()
plt.subplots_adjust(top=0.94)
return fig
# 使用示例
if __name__ == "__main__":
# 使用akshare获取股票数据
try:
# 获取上证指数数据,可替换为其他股票代码
stock_data = ak.stock_zh_index_daily(symbol="sz000937")
# 重命名列以匹配原代码
df = stock_data.rename(columns={
'date': 'date',
'open': 'open',
'high': 'high',
'low': 'low',
'close': 'close',
'volume': 'volume'
})
# 设置日期索引
df['date'] = pd.to_datetime(df['date'])
df.set_index('date', inplace=True)
# 确保数据量足够
if len(df) < 200:
print(f"数据长度不足,只有{len(df)}行")
else:
# 截取最近200个交易日数据
df = df.iloc[-200:]
# 计算量化对冲指标
result_df = quant_hedge_strategy(df)
# 绘制图表
fig = plot_quant_strategy(result_df, "冀中能源")
# 显示图表
plt.show()
# 输出结果
print(result_df[['量化对冲1', '量化对冲2', '量化对冲3', '量化对冲4', '量化对冲11',
'起飞信号', '妖股信号', '交叉信号']].tail())
except Exception as e:
print(f"获取数据出错: {e}")
print("使用随机数据作为替代")
# 随机数据作为备选
data = {
'date': pd.date_range(start='2023-01-01', periods=200),
'open': np.random.uniform(100, 200, 200),
'high': np.random.uniform(110, 220, 200),
'low': np.random.uniform(90, 190, 200),
'close': np.random.uniform(100, 200, 200),
'volume': np.random.randint(10000, 100000, 200)
}
df = pd.DataFrame(data)
df.set_index('date', inplace=True)
# 计算量化对冲指标
result_df = quant_hedge_strategy(df)
# 绘制图表
fig = plot_quant_strategy(result_df, "随机数据")
# 显示图表
plt.show()
# 输出结果
print(result_df[['量化对冲1', '量化对冲2', '量化对冲3', '量化对冲4', '量化对冲11',
'起飞信号', '妖股信号', '交叉信号']].tail())
新增的绘图功能特点:
- 包含4个子图,全面展示量化策略的各项指标:
- 子图1:股票价格走势与关键均线,标记各种信号出现位置
- 子图2:量化对冲2、9、11指标,填充指标间区域
- 子图3:主力暗盘资金指标(量化对冲12-15)
- 子图4:量化对冲信号5、6的柱状图
- 信号标记:
- 绿色三角:起飞信号
- 紫色星号:妖股信号
- 橙色圆点:交叉信号
- 图表美化:
- 自动调整日期显示格式
- 添加网格线提高可读性
- 每个子图都有标题和图例
- 合理的颜色搭配
- 交互功能:
- 鼠标悬停可查看具体数值
- 可放大缩小特定区域
- 可保存图表为图片文件
使用说明:
- 需要安装额外依赖库:
pip install matplotlib
- 运行代码后会自动显示绘制的图表
- 图表标题会显示股票名称,默认为”上证指数”
- 可以通过修改
plot_quant_strategy
函数来自定义图表样式和内容
量化对冲指标的解释
以下是代码中主要量化对冲指标的解释,这些指标用于分析股票趋势、资金流向和潜在交易信号:
1. 趋势跟踪指标
量化对冲1
- 计算方式:
EMA(close, 9)
(9日指数移动平均线) - 作用:反映短期价格趋势,比简单均线更敏感,用于识别短期方向。
量化对冲3
- 计算方式:
EMA(量化对冲1 × 1.14, 5)
(对放大后的短期均线再做5日EMA) - 作用:增强趋势信号,放大短期波动,用于捕捉快速变化的趋势。
量化对冲4
- 计算方式:
EMA(close, 2)
(2日指数移动平均线) - 作用:超短期趋势指标,对价格变动极为敏感,用于识别极短期转折点。
2. 相对强弱指标
量化对冲2
- 计算方式:
(close - 40日最低) / 40日最低 × 100
- 作用:衡量当前价格相对于近40日最低价的涨幅百分比,类似于RSI指标,反映价格强度。
- 信号逻辑:值越高,表明价格距离低点越远,可能处于超买区域;反之可能处于超卖区域。
量化对冲7
- 计算方式:
(close - MA40) / MA40 × 100
- 作用:衡量当前价格相对于40日均价的偏离程度,用于判断价格是否偏离均值过远(可能回调)。
3. 资金流向指标
量化对冲12-15
- 计算方式:基于收盘价、成交量与不同周期成本价(90日/10日均价)的差值计算
量化对冲12 = ((close × volume - cost90 × volume) / 20 + 250) × 1.2 / 5
量化对冲13 = ((cost90 × volume - close × volume) / 20 + 250) × 1.2 / 5
量化对冲14 = ((close × volume - cost10 × volume) / 20 + 250) × 1.2 / 5
量化对冲15 = ((cost10 × volume - close × volume) / 20 + 250) × 1.2 / 5
- 作用:模拟不同周期的资金流入流出情况,正值表示资金流入,负值表示资金流出。
- 信号逻辑:当短期资金流入(如量化对冲14)强于长期资金流出(如量化对冲13)时,视为看涨信号。
4. 信号生成指标
起飞信号
- 触发条件:
量化对冲16
为True(满足量化对冲5
或量化对冲6
条件)- 且
量化对冲16
在近20天内首次出现
- 作用:捕捉价格启动初期的信号,可能预示短期快速上涨。
妖股信号
- 触发条件:
量化对冲14 ≥ 量化对冲14_60日高点
(短期资金流入创60日新高)- 且
量化对冲14 ≥ 量化对冲14_20日高点
在近20天内仅出现1次 - 且
量化对冲14 > 量化对冲13
(短期资金流入强于长期流出)
- 作用:识别可能出现大幅上涨的”妖股”特征,通常伴随强资金推动。
交叉信号
- 触发条件:
量化对冲2
的前一日值低于量化对冲9
(阈值28),当日值高于或等于阈值 - 作用:捕捉价格从弱势区域向上突破的转折点,类似MACD金叉信号。
5. 辅助指标
量化对冲5 & 量化对冲6
- 量化对冲5:短期资金流入为正,且(短期资金流入>长期资金流出 或 短期资金流入为正)
- 量化对冲6:短期资金流入为正,且短期和长期资金流入均为正,同时短期和长期资金流出均为负
- 作用:量化对冲5侧重短期资金流向,量化对冲6要求更严格的资金面条件,两者结合用于确认强势信号。
策略逻辑总结
- 趋势判断:通过多周期均线(量化对冲1、3、4)识别价格趋势方向。
- 强弱评估:通过量化对冲2评估价格相对强度,结合阈值(量化对冲9)判断超买超卖。
- 资金监控:通过量化对冲12-15跟踪不同周期资金流向,判断多空力量对比。
- 信号生成:综合上述指标,生成起飞信号、妖股信号和交叉信号,作为交易决策依据。
该策略适合用于识别短期趋势转折点和强势股,但实际应用中需结合止损机制和仓位管理,避免假信号带来的风险。
