# %pip install tushare pandas numpy matplotlib
import tushare as ts
# ts.set_token('YourTokenHere')
def get_tushare_token(file_path='tushare_token.txt'):
"""
从文件读取 Tushare Token
:param file_path: Token文件路径
:return: Token字符串
"""
try:
with open(file_path, 'r') as file:
# 读取第一行并去除可能的空白字符
token = file.readline().strip()
return token
except FileNotFoundError:
print(f"Error: Token文件 {file_path} 未找到")
return None
except Exception as e:
print(f"读取Token时发生错误: {e}")
return None
# 在代码中使用
token = get_tushare_token()
ts.set_token(token)
ver5
import tushare as ts
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from itertools import product
class MAStrategy:
def __init__(self, short_window=20, long_window=50):
self.short_window = short_window
self.long_window = long_window
def get_data(self, stock_code, start_date, end_date):
"""
数据获取方法
"""
pro = ts.pro_api()
df = pro.daily(
ts_code=stock_code,
start_date=start_date.replace('-', ''),
end_date=end_date.replace('-', '')
)
# 数据预处理
df['date'] = pd.to_datetime(df['trade_date'], format='%Y%m%d')
df = df.sort_values('date').set_index('date')
return df
def generate_signals(self, df, short_window, long_window):
"""
生成交易信号
"""
# 计算移动平均线
df['short_ma'] = df['close'].rolling(window=short_window).mean()
df['long_ma'] = df['close'].rolling(window=long_window).mean()
# 初始化信号列
df['signal'] = 0
# 生成交易信号
# 买入信号: 上穿判断
df.loc[(df['short_ma'] > df['long_ma']) &
(df['short_ma'].shift(1) <= df['long_ma'].shift(1)), 'signal'] = 1
# 卖出信号: 下穿判断
df.loc[(df['short_ma'] < df['long_ma']) &
(df['short_ma'].shift(1) >= df['long_ma'].shift(1)), 'signal'] = -1
return df
def backtest(self, df, initial_capital=100000, commission_rate=0.002):
"""
回测分析
"""
# 计算每日收益率
df['returns'] = df['close'].pct_change()
# 初始化账户
df['position'] = df['signal'].shift(1) # 根据前一日信号决定当日仓位
df['portfolio_returns'] = df['returns'] * df['position']
# 扣除交易成本
df['net_returns'] = df['portfolio_returns'] - abs(df['position'].diff()) * commission_rate
# 计算累计收益
df['cumulative_returns'] = (1 + df['net_returns']).cumprod()
# 生成交易列表
trades = df.reset_index()
trade_points = trades[trades['position'] != trades['position'].shift(1)].copy()
trade_points['trade_type'] = np.where(trade_points['position'] == 1, 'Buy', 'Sell')
# 性能指标
performance = {
'total_return': df['cumulative_returns'].iloc[-1] - 1,
'annual_return': (df['cumulative_returns'].iloc[-1] ** (252/len(df)) - 1),
'sharpe_ratio': df['net_returns'].mean() / df['net_returns'].std() * np.sqrt(252),
'max_drawdown': (df['cumulative_returns'] / df['cumulative_returns'].cummax() - 1).min()
}
return df, trade_points, performance
def visualize_strategy(self, df, trades, stock_code, period):
"""
可视化策略:
1. 均线图
2. 策略收益率曲线
"""
# 创建两个子图
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(15, 10),
gridspec_kw={'height_ratios': [2, 1]})
# 第一个子图:均线图
ax1.plot(df.index, df['close'], label='股价', color='black')
ax1.plot(df.index, df['short_ma'], label=f'短期均线({self.short_window}日)', color='red')
ax1.plot(df.index, df['long_ma'], label=f'长期均线({self.long_window}日)', color='blue')
# 标注买卖点
buy_points = trades[trades['trade_type'] == 'Buy']
sell_points = trades[trades['trade_type'] == 'Sell']
ax1.scatter(buy_points['date'], buy_points['close'],
color='red', marker='^', label='买入点')
ax1.scatter(sell_points['date'], sell_points['close'],
color='green', marker='v', label='卖出点')
ax1.set_title(f'{stock_code} 均线策略 ({period})')
ax1.set_xlabel('日期')
ax1.set_ylabel('股价')
ax1.legend()
# 第二个子图:策略收益率曲线
ax2.plot(df.index, df['cumulative_returns'], label='策略累计收益', color='purple')
ax2.set_title('策略累计收益率')
ax2.set_xlabel('日期')
ax2.set_ylabel('累计收益率')
ax2.legend()
plt.tight_layout()
plt.show()
def parameter_optimization(self, stock_code, start_date, end_date):
"""
参数网格搜索优化
"""
short_windows = [5, 10, 15, 20]
long_windows = [20, 30, 40, 50, 60]
optimization_results = []
for short_window, long_window in product(short_windows, long_windows):
if short_window >= long_window:
continue
df = self.get_data(stock_code, start_date, end_date)
df = self.generate_signals(df, short_window, long_window)
_, trades, performance = self.backtest(df)
optimization_results.append({
'short_window': short_window,
'long_window': long_window,
'performance': performance
})
optimization_results.sort(key=lambda x: x['performance']['total_return'], reverse=True)
return optimization_results
def performance_evaluation(self, stock_codes, periods):
"""
多标的、多时期性能评估
"""
comprehensive_results = {}
for stock_code in stock_codes:
stock_performance = []
for start_date, end_date in periods:
try:
df = self.get_data(stock_code, start_date, end_date)
# 参数优化
optimization_results = self.parameter_optimization(stock_code, start_date, end_date)
best_params = optimization_results[0]
# 使用最佳参数
df = self.generate_signals(
df,
best_params['short_window'],
best_params['long_window']
)
result_df, trades, performance = self.backtest(df)
# 可视化
self.visualize_strategy(result_df, trades, stock_code, f'{start_date} to {end_date}')
stock_performance.append({
'period': f'{start_date} to {end_date}',
'best_params': best_params,
'performance': performance,
'trades': trades
})
except Exception as e:
print(f"股票 {stock_code} 在 {start_date} 到 {end_date} 期间回测失败: {e}")
comprehensive_results[stock_code] = stock_performance
return comprehensive_results
def main():
# 实例化策略
strategy = MAStrategy()
# 设置回测参数
stock_codes = ['600036.SH', '000001.SZ'] # 招商银行、平安银行
periods = [
('2020-01-01', '2020-12-31'), # 牛市
('2021-01-01', '2021-12-31'), # 震荡市
('2022-01-01', '2022-12-31') # 熊市
]
# 执行性能评估
results = strategy.performance_evaluation(stock_codes, periods)
# 输出结果
for stock_code, stock_performance in results.items():
print(f"\n股票 {stock_code} 表现:")
for period_result in stock_performance:
print(f"\n期间: {period_result['period']}")
print("最佳参数:")
print(f"短期均线: {period_result['best_params']['short_window']}")
print(f"长期均线: {period_result['best_params']['long_window']}")
print("\n性能指标:")
for key, value in period_result['performance'].items():
print(f"{key}: {value:.4f}")
print("\n交易列表:")
trades = period_result['trades']
print(trades[['date', 'close', 'trade_type']])
if __name__ == "__main__":
main()
Duplicate key in file WindowsPath('d:/Users/wukek/anaconda3/Lib/site-packages/matplotlib/mpl-data/matplotlibrc'), line 412 ('axes.unicode_minus: True # use Unicode for the minus symbol rather than hyphen. See')
股票 600036.SH 表现: 期间: 2020-01-01 to 2020-12-31 最佳参数: 短期均线: 5 长期均线: 20 性能指标: total_return: 0.1293 annual_return: 0.1344 sharpe_ratio: 1.1791 max_drawdown: -0.0318 交易列表: date close trade_type 0 2020-01-02 38.88 Sell 1 2020-01-03 39.40 Sell 62 2020-04-08 32.22 Buy 63 2020-04-09 32.30 Sell 93 2020-05-26 33.43 Sell 94 2020-05-27 33.43 Sell 99 2020-06-03 35.22 Buy 100 2020-06-04 35.15 Sell 109 2020-06-17 34.20 Sell 110 2020-06-18 33.89 Sell 120 2020-07-06 40.41 Buy 121 2020-07-07 41.06 Sell 131 2020-07-21 36.84 Sell 132 2020-07-22 36.44 Sell 144 2020-08-07 36.61 Buy 145 2020-08-10 37.00 Sell 162 2020-09-02 37.20 Sell 163 2020-09-03 36.98 Sell 172 2020-09-16 38.06 Buy 173 2020-09-17 37.87 Sell 181 2020-09-29 36.31 Sell 182 2020-09-30 36.00 Sell 188 2020-10-16 39.78 Buy 189 2020-10-19 39.99 Sell 228 2020-12-11 42.68 Sell 229 2020-12-14 44.37 Sell 期间: 2021-01-01 to 2021-12-31 最佳参数: 短期均线: 10 长期均线: 60 性能指标: total_return: 0.0812 annual_return: 0.0843 sharpe_ratio: 1.1397 max_drawdown: -0.0310 交易列表: date close trade_type 0 2021-01-04 43.17 Sell 1 2021-01-05 42.18 Sell 61 2021-04-07 50.43 Sell 62 2021-04-08 50.50 Sell 79 2021-05-06 53.67 Buy 80 2021-05-07 54.18 Sell 123 2021-07-08 50.35 Sell 124 2021-07-09 49.90 Sell 169 2021-09-10 54.12 Buy 170 2021-09-13 53.92 Sell 182 2021-10-08 50.93 Sell 183 2021-10-11 53.00 Sell 184 2021-10-12 53.09 Buy 185 2021-10-13 53.03 Sell 209 2021-11-16 52.50 Sell 210 2021-11-17 51.84 Sell 213 2021-11-22 51.69 Buy 214 2021-11-23 51.75 Sell 218 2021-11-29 49.82 Sell 219 2021-11-30 49.49 Sell 231 2021-12-16 51.20 Buy 232 2021-12-17 50.77 Sell 234 2021-12-21 50.63 Sell 235 2021-12-22 50.01 Sell 期间: 2022-01-01 to 2022-12-31 最佳参数: 短期均线: 10 长期均线: 40 性能指标: total_return: 0.0503 annual_return: 0.0524 sharpe_ratio: 0.9230 max_drawdown: -0.0168 交易列表: date close trade_type 0 2022-01-04 48.35 Sell 1 2022-01-05 49.05 Sell 106 2022-06-16 39.96 Buy 107 2022-06-17 39.85 Sell 127 2022-07-15 34.87 Sell 128 2022-07-18 35.63 Sell 170 2022-09-15 36.01 Buy 171 2022-09-16 34.95 Sell 183 2022-10-11 31.40 Sell 184 2022-10-12 30.95 Sell 212 2022-11-21 31.87 Buy 213 2022-11-22 32.45 Sell 股票 000001.SZ 表现: 期间: 2020-01-01 to 2020-12-31 最佳参数: 短期均线: 5 长期均线: 60 性能指标: total_return: 0.1094 annual_return: 0.1137 sharpe_ratio: 1.1159 max_drawdown: -0.0054 交易列表: date close trade_type 0 2020-01-02 16.87 Sell 1 2020-01-03 17.18 Sell 82 2020-05-11 14.00 Buy 83 2020-05-12 13.79 Sell 86 2020-05-15 13.23 Sell 87 2020-05-18 13.20 Sell 101 2020-06-05 13.59 Buy 102 2020-06-08 13.62 Sell 109 2020-06-17 12.85 Sell 110 2020-06-18 12.76 Sell 120 2020-07-06 15.68 Buy 121 2020-07-07 15.48 Sell 138 2020-07-30 13.37 Sell 139 2020-07-31 13.34 Sell 143 2020-08-06 13.90 Buy 144 2020-08-07 13.70 Sell 期间: 2021-01-01 to 2021-12-31 最佳参数: 短期均线: 15 长期均线: 60 性能指标: total_return: 0.0799 annual_return: 0.0830 sharpe_ratio: 1.2971 max_drawdown: -0.0020 交易列表: date close trade_type 0 2021-01-04 18.60 Sell 1 2021-01-05 18.17 Sell 80 2021-05-07 24.05 Buy 81 2021-05-10 23.86 Sell 119 2021-07-02 21.81 Sell 120 2021-07-05 22.06 Sell 193 2021-10-25 20.12 Buy 194 2021-10-26 20.05 Sell 205 2021-11-10 17.40 Sell 206 2021-11-11 18.35 Sell 期间: 2022-01-01 to 2022-12-31 最佳参数: 短期均线: 5 长期均线: 30 性能指标: total_return: 0.1162 annual_return: 0.1213 sharpe_ratio: 1.7134 max_drawdown: -0.0164 交易列表: date close trade_type 0 2022-01-04 16.66 Sell 1 2022-01-05 17.15 Sell 59 2022-04-06 16.39 Buy 60 2022-04-07 16.28 Sell 76 2022-04-29 15.32 Sell 77 2022-05-05 15.32 Sell 109 2022-06-21 14.37 Buy 110 2022-06-22 14.08 Sell 111 2022-06-23 14.20 Sell 112 2022-06-24 14.23 Sell 116 2022-06-30 14.98 Buy 117 2022-07-01 14.92 Sell 126 2022-07-14 13.37 Sell 127 2022-07-15 13.24 Sell 160 2022-08-31 12.75 Buy 161 2022-09-01 12.61 Sell 176 2022-09-23 12.29 Sell 177 2022-09-26 12.00 Sell 208 2022-11-15 12.01 Buy 209 2022-11-16 11.82 Sell
# 实例化策略
strategy = MAStrategy()
# 设置回测参数
# stock_codes = ['600036.SH', '000001.SZ'] # 招商银行、平安银行
# periods = [
# ('2020-01-01', '2020-12-31'), # 牛市
# ('2021-01-01', '2021-12-31'), # 震荡市
# ('2022-01-01', '2022-12-31') # 熊市
# ]
stock_codes = ['600036.SH'] # 招商银行、平安银行
periods = [
('2020-01-01', '2020-12-31')
]
# 执行性能评估
results = strategy.performance_evaluation(stock_codes, periods)
# 输出结果
for stock_code, stock_performance in results.items():
print(f"\n股票 {stock_code} 表现:")
for period_result in stock_performance:
print(f"\n期间: {period_result['period']}")
print("最佳参数:")
print(f"短期均线: {period_result['best_params']['short_window']}")
print(f"长期均线: {period_result['best_params']['long_window']}")
print("\n性能指标:")
for key, value in period_result['performance'].items():
print(f"{key}: {value:.4f}")
print("\n交易列表:")
trades = period_result['trades']
print(trades[['date', 'close', 'trade_type']])
股票 600036.SH 表现: 期间: 2020-01-01 to 2020-12-31 最佳参数: 短期均线: 5 长期均线: 20 性能指标: total_return: 0.1293 annual_return: 0.1344 sharpe_ratio: 1.1791 max_drawdown: -0.0318 交易列表: date close trade_type 0 2020-01-02 38.88 Sell 1 2020-01-03 39.40 Sell 62 2020-04-08 32.22 Buy 63 2020-04-09 32.30 Sell 93 2020-05-26 33.43 Sell 94 2020-05-27 33.43 Sell 99 2020-06-03 35.22 Buy 100 2020-06-04 35.15 Sell 109 2020-06-17 34.20 Sell 110 2020-06-18 33.89 Sell 120 2020-07-06 40.41 Buy 121 2020-07-07 41.06 Sell 131 2020-07-21 36.84 Sell 132 2020-07-22 36.44 Sell 144 2020-08-07 36.61 Buy 145 2020-08-10 37.00 Sell 162 2020-09-02 37.20 Sell 163 2020-09-03 36.98 Sell 172 2020-09-16 38.06 Buy 173 2020-09-17 37.87 Sell 181 2020-09-29 36.31 Sell 182 2020-09-30 36.00 Sell 188 2020-10-16 39.78 Buy 189 2020-10-19 39.99 Sell 228 2020-12-11 42.68 Sell 229 2020-12-14 44.37 Sell