生生不息

生生不息

AI生成交易信号

2025-06-30
AI生成交易信号

提示词

price = pd.read_excel('../数据/601001策略回测实验数据.xlsx',
                     index_col='日期')

# 将index转换成datetime格式 
price.index = pd.to_datetime(price.index)

# 保留收盘价
price = price[['收盘']]

在名叫price的DataFrame中,保存了某只股票的每日收盘价。现在需要基于长短期均线生成交易信号,具体为当20日均线上穿100日均线时买入,否则卖出,最后将收盘价、长短期均线和根据交易信号发生的仓位变化进行可视化,仓位变化需要以子图的形式显示在下方,请给出示例代码。

答案

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from matplotlib.ticker import MaxNLocator

# 计算移动平均线
price['MA20'] = price['收盘'].rolling(window=20).mean()    # 20日均线
price['MA100'] = price['收盘'].rolling(window=100).mean()  # 100日均线

# 生成交易信号 (1:买入, 0:卖出)
price['Signal'] = np.where(price['MA20'] > price['MA100'], 1, 0)

# 计算仓位变化 (当信号从0变1时买入,从1变0时卖出)
price['Position'] = price['Signal'].diff()

# 创建可视化图表
plt.figure(figsize=(14, 10))
plt.suptitle('双均线交易策略 (20日 vs 100日)', fontsize=16, fontweight='bold')

# 价格和均线图 (主图)
ax1 = plt.subplot(2, 1, 1)
ax1.plot(price.index, price['收盘'], label='收盘价', color='black', linewidth=1.5, alpha=0.8)
ax1.plot(price.index, price['MA20'], label='20日均线', color='blue', linewidth=1.5)
ax1.plot(price.index, price['MA100'], label='100日均线', color='red', linewidth=2)

# 标记买入卖出信号
buy_signals = price[price['Position'] == 1]
sell_signals = price[price['Position'] == -1]
ax1.scatter(buy_signals.index, buy_signals['收盘'], 
            marker='^', s=80, color='green', alpha=0.9, label='买入信号')
ax1.scatter(sell_signals.index, sell_signals['收盘'], 
            marker='v', s=80, color='red', alpha=0.9, label='卖出信号')

# 填充均线之间的区域
ax1.fill_between(price.index, price['MA20'], price['MA100'], 
                 where=price['MA20'] >= price['MA100'], 
                 facecolor='lightgreen', alpha=0.3, interpolate=True)
ax1.fill_between(price.index, price['MA20'], price['MA100'], 
                 where=price['MA20'] < price['MA100'], 
                 facecolor='lightcoral', alpha=0.3, interpolate=True)

ax1.set_title('价格走势与均线交叉信号', fontsize=14)
ax1.set_ylabel('价格', fontsize=12)
ax1.legend(loc='upper left')
ax1.grid(True, linestyle='--', alpha=0.6)

# 仓位变化图 (子图)
ax2 = plt.subplot(2, 1, 2, sharex=ax1)
ax2.plot(price.index, price['Signal'], 
         label='持仓状态', color='purple', linewidth=2)
ax2.fill_between(price.index, 0, price['Signal'], 
                 where=price['Signal'] >= 0, 
                 facecolor='purple', alpha=0.3)

# 标记仓位变化点
ax2.scatter(buy_signals.index, np.ones(len(buy_signals)), 
            marker='^', s=80, color='green', alpha=0.9, label='建仓点')
ax2.scatter(sell_signals.index, np.zeros(len(sell_signals)), 
            marker='v', s=80, color='red', alpha=0.9, label='平仓点')

ax2.set_title('仓位变化', fontsize=14)
ax2.set_ylabel('仓位状态\n(1=持有, 0=空仓)', fontsize=12)
ax2.set_xlabel('日期', fontsize=12)
ax2.legend(loc='upper left')
ax2.yaxis.set_major_locator(MaxNLocator(integer=True))  # Y轴只显示整数
ax2.grid(True, linestyle='--', alpha=0.6)

# 添加策略说明
stats_text = f"策略统计:\n- 总买入信号: {len(buy_signals)}\n- 总卖出信号: {len(sell_signals)}\n- 最大持仓天数: {price['Signal'].sum()}"
plt.figtext(0.75, 0.25, stats_text, bbox=dict(facecolor='white', alpha=0.5), fontsize=10)

plt.tight_layout(rect=[0, 0, 1, 0.96])  # 为标题留出空间
plt.savefig('双均线交易策略可视化.png', dpi=300, bbox_inches='tight')
plt.show()

# 输出交易信号详情
print("交易信号详情:")
print(price[['收盘', 'MA20', 'MA100', 'Signal', 'Position']].dropna().tail(10))

结果:

计算交易策略的收益

简化代码加描述

提示词

# Python代码
# 计算20日均线和100日均线
price['short_mavg'] = price['收盘'].rolling(window=20, min_periods=1).mean()
price['long_mavg'] = price['收盘'].rolling(window=100, min_periods=1).mean()

# 生成交易信号
price['signal'] = 0.0
price['signal'][price['short_mavg'] > price['long_mavg']] = 1.0
# 绘制收盘价和均线
plt.figure(dpi=300)

plt.subplot(2, 1, 1)
plt.plot(price['收盘'], label='收盘价',lw=1)
plt.plot(price['short_mavg'], label='20日均线',lw=1)
plt.plot(price['long_mavg'], label='100日均线',lw=1)
plt.title('收盘价和均线')
plt.legend()

# 绘制仓位变化
plt.subplot(2, 1, 2)
plt.fill_between(price.index, price['signal'], 
                 color='green', #alpha=0.5, 
                 where=(price['signal'] == 1.0))
plt.fill_between(price.index, price['signal'], 
                 color='red', #alpha=0.5, 
                 where=(price['signal'] == 0.0))
plt.title('仓位变化')
plt.tight_layout()
plt.savefig('图12-1.jpg', dpi=300)
plt.show()

基于上面的代码,将持仓的收益与该股票被动持有的收益进行对比,并将对比结果可视化,请给出示例代码。

答案

import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.dates as mdates
import matplotlib.ticker as mtick

# 计算移动平均线
price['short_mavg'] = price['收盘'].rolling(window=20, min_periods=1).mean()
price['long_mavg'] = price['收盘'].rolling(window=100, min_periods=1).mean()

# 生成交易信号
price['signal'] = 0.0
price['signal'] = np.where(price['short_mavg'] > price['long_mavg'], 1.0, 0.0)

# 计算每日收益率
price['daily_return'] = price['收盘'].pct_change()

# 计算策略收益
price['strategy_position'] = price['signal'].shift(1)  # 使用前一天的信号避免未来函数
price['strategy_return'] = price['strategy_position'] * price['daily_return']
price['strategy_cumulative'] = (1 + price['strategy_return']).cumprod()

# 计算被动持有收益
price['buy_hold_return'] = price['daily_return']
price['buy_hold_cumulative'] = (1 + price['buy_hold_return']).cumprod()

# 计算累计收益
initial_value = 10000  # 假设初始投资10000元
price['strategy_value'] = initial_value * price['strategy_cumulative']
price['buy_hold_value'] = initial_value * price['buy_hold_cumulative']

# 设置绘图风格
plt.style.use('seaborn-v0_8')
#指定字体,防止中文出现乱码,windows系统指定为‘SimHei’
plt.rcParams['font.sans-serif'] = ['SimHei']
#这行代码让中文的负号“-”可以正常显示
plt.rcParams["axes.unicode_minus"]=False

# 创建图表
fig, (ax1, ax2, ax3) = plt.subplots(3, 1, figsize=(12, 12), dpi=300, sharex=True)
fig.suptitle('双均线策略 vs 被动持有收益对比', fontsize=16, fontweight='bold')

# 绘制价格和均线
ax1.plot(price.index, price['收盘'], label='收盘价', lw=1.2, color='black')
ax1.plot(price.index, price['short_mavg'], label='20日均线', lw=1.2, color='blue')
ax1.plot(price.index, price['long_mavg'], label='100日均线', lw=1.5, color='red')
ax1.set_title('价格走势与均线', fontsize=12)
ax1.set_ylabel('价格', fontsize=10)
ax1.legend(loc='upper left', fontsize=8)
ax1.grid(True, alpha=0.4)

# 标记金叉死叉点
gold_cross = price[(price['short_mavg'] > price['long_mavg']) & 
                  (price['short_mavg'].shift(1) <= price['long_mavg'].shift(1))]
death_cross = price[(price['short_mavg'] < price['long_mavg']) & 
                   (price['short_mavg'].shift(1) >= price['long_mavg'].shift(1))]

ax1.scatter(gold_cross.index, gold_cross['short_mavg'], 
           marker='^', s=80, color='green', alpha=0.9, label='金叉(买入)')
ax1.scatter(death_cross.index, death_cross['short_mavg'], 
           marker='v', s=80, color='red', alpha=0.9, label='死叉(卖出)')

# 绘制仓位变化
ax2.fill_between(price.index, price['signal'], 
                 color='green', alpha=0.3, 
                 where=(price['signal'] == 1.0), label='持仓')
ax2.fill_between(price.index, price['signal'], 
                 color='red', alpha=0.3, 
                 where=(price['signal'] == 0.0), label='空仓')
ax2.set_title('仓位变化', fontsize=12)
ax2.set_ylabel('仓位状态', fontsize=10)
ax2.set_yticks([0, 1])
ax2.set_yticklabels(['空仓', '持仓'])
ax2.legend(loc='upper left', fontsize=8)

# 绘制收益对比
ax3.plot(price.index, price['strategy_value'], 
         label='双均线策略', lw=1.8, color='darkgreen')
ax3.plot(price.index, price['buy_hold_value'], 
         label='被动持有', lw=1.8, color='blue', alpha=0.7)
ax3.set_title('收益对比 (初始投资: ¥10,000)', fontsize=12)
ax3.set_ylabel('账户价值 (¥)', fontsize=10)
ax3.set_xlabel('日期', fontsize=10)
ax3.legend(loc='upper left', fontsize=8)
ax3.grid(True, alpha=0.4)

# 添加最终收益标注
final_strategy = price['strategy_value'].iloc[-1]
final_buy_hold = price['buy_hold_value'].iloc[-1]
strategy_return_pct = (final_strategy - initial_value) / initial_value * 100
buy_hold_return_pct = (final_buy_hold - initial_value) / initial_value * 100

ax3.annotate(f'策略最终收益: ¥{final_strategy:,.2f} ({strategy_return_pct:.1f}%)',
            xy=(price.index[-1], final_strategy),
            xytext=(price.index[-1] - pd.DateOffset(months=6), final_strategy * 0.8),
            arrowprops=dict(facecolor='black', shrink=0.05),
            fontsize=9)

ax3.annotate(f'被动持有收益: ¥{final_buy_hold:,.2f} ({buy_hold_return_pct:.1f}%)',
            xy=(price.index[-1], final_buy_hold),
            xytext=(price.index[-1] - pd.DateOffset(months=6), final_buy_hold * 1.2),
            arrowprops=dict(facecolor='black', shrink=0.05),
            fontsize=9)

# 设置x轴日期格式
ax3.xaxis.set_major_locator(mdates.MonthLocator(interval=3))
ax3.xaxis.set_major_formatter(mdates.DateFormatter('%Y-%m'))
plt.xticks(rotation=45, ha='right')

# 调整布局
plt.tight_layout(rect=[0, 0, 1, 0.96])  # 为总标题留空间
plt.subplots_adjust(hspace=0.2)  # 调整子图间距

# 添加统计信息表格
stats_data = [
    ["累计收益率", f"{strategy_return_pct:.1f}%", f"{buy_hold_return_pct:.1f}%"],
    ["年化收益率", 
     f"{(price['strategy_cumulative'].iloc[-1] ** (252/len(price)) - 1) * 100:.1f}%",
     f"{(price['buy_hold_cumulative'].iloc[-1] ** (252/len(price)) - 1) * 100:.1f}%"],
    ["最大回撤", 
     f"{((price['strategy_value'].cummax() - price['strategy_value']).max() / initial_value * 100):.1f}%",
     f"{((price['buy_hold_value'].cummax() - price['buy_hold_value']).max() / initial_value * 100):.1f}%"],
    ["交易次数", f"{len(gold_cross) + len(death_cross)}次", "-"],
    ["持仓时间占比", f"{price['signal'].mean() * 100:.1f}%", "100%"]
]

table = plt.table(cellText=stats_data,
                 colLabels=['指标', '双均线策略', '被动持有'],
                 loc='bottom',
                 bbox=[0, -0.5, 1, 0.3])

table.auto_set_font_size(False)
table.set_fontsize(10)
table.scale(1, 1.5)

# 保存和显示
plt.savefig('收益对比分析.jpg', dpi=300, bbox_inches='tight')
plt.show()

结果:

交易策略最后收益还不如一直持有,要调整。