【策略】中证红利低吸高抛
最终资产状况:
策略最终总资产: 231336.85元
策略最终仓位: 100.00%
策略最终持仓市值: 231336.85元
策略最终现金: 0.00元
基准(红利低波)最终总资产: 195888.16元
沪深300最终总资产: 113946.27元

# -*- coding: utf-8 -*-
"""
Created on Wed Jun 25 08:29:48 2025
@author: win10
"""
import akshare as ak
import pandas as pd
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
# 获取基金历史数据
#红利 512890
#黄金 518880
#豆粕 159985
#美国 513300
fund_etf_hist_em_df = ak.fund_etf_hist_em(symbol="512890", period="daily",
start_date="20200115", end_date="20260201", adjust="qfq")
# 获取沪深300指数数据
index_hs300_df = ak.stock_zh_index_daily(symbol="sh000300")
index_hs300_df['date'] = pd.to_datetime(index_hs300_df['date'])
index_hs300_df.set_index('date', inplace=True)
index_hs300_df = index_hs300_df.sort_index()
# 设置中文字体,确保图表中的中文能正常显示
try:
font = FontProperties(fname=r"C:\Windows\Fonts\simhei.ttf") # Windows系统
except:
font = FontProperties() # 使用默认字体
# 策略参数设置
min_position = 0.10 # 最低仓位10%
initial_position = 0.90 # 初始仓位90%
initial_capital = 100000 # 初始资金5万元(修改为5万以便更清晰展示交易)
short_term_days = 20 # 短期定义为15个交易日
up_threshold = 0.07 # 短期上涨7.5%触发减仓
down_threshold = -0.03 # 短期下跌5%触发加仓
position_step = 0.5 # 每次调整50%仓位
# 复制数据并初始化策略相关列
df = fund_etf_hist_em_df.copy()
df['日期'] = pd.to_datetime(df['日期'])
df.set_index('日期', inplace=True)
df.sort_index(inplace=True)
# 合并沪深300数据
df = df.join(index_hs300_df, how='left')
df['沪深300收盘'] = df['close'].fillna(method='ffill') # 处理缺失值
df = df.drop(columns=['open', 'high', 'low', 'close', 'volume'])
# 计算短期涨跌幅
df['短期涨跌幅'] = df['收盘'].pct_change(periods=short_term_days)
# 初始化仓位、现金和总资产列
df['仓位'] = initial_position
df['现金比例'] = 1 - initial_position
df['总资产'] = initial_capital # 初始总资产
df['持仓市值'] = initial_capital * initial_position
df['现金'] = initial_capital * (1 - initial_position)
# 创建交易记录表
transactions = []
# 执行策略
for i in range(1, len(df)):
# 复制前一天的仓位和现金比例
df.iloc[i, df.columns.get_loc('仓位')] = df.iloc[i-1, df.columns.get_loc('仓位')]
df.iloc[i, df.columns.get_loc('现金比例')] = df.iloc[i-1, df.columns.get_loc('现金比例')]
# 获取当前仓位和前一天总资产
current_position = df.iloc[i, df.columns.get_loc('仓位')]
prev_assets = df.iloc[i-1, df.columns.get_loc('总资产')]
# 记录交易信息
transaction = {
'日期': df.index[i],
'操作': '无',
'操作前仓位': current_position,
'操作后仓位': current_position,
'操作金额': 0,
'价格': df.iloc[i, df.columns.get_loc('收盘')],
'交易数量(手)': 0 # 新增:交易数量(手)
}
# 检查是否需要调整仓位
if df.iloc[i, df.columns.get_loc('短期涨跌幅')] >= up_threshold and current_position > min_position:
# 短期上涨超过阈值且有仓位可减
reduce_position = min(position_step, current_position - min_position)
df.iloc[i, df.columns.get_loc('仓位')] -= reduce_position
df.iloc[i, df.columns.get_loc('现金比例')] += reduce_position
# 计算操作金额
operation_amount = prev_assets * reduce_position
# 计算交易数量(手),确保不低于1手
price = df.iloc[i, df.columns.get_loc('收盘')]
shares = int(operation_amount / price / 100) # 先计算股数,再转换为手数(1手=100股)
lots = max(1, shares) # 确保交易数量不低于1手
# 更新交易信息
transaction['操作'] = '减仓'
transaction['操作后仓位'] = current_position - reduce_position
transaction['操作金额'] = operation_amount
transaction['交易数量(手)'] = lots # 记录交易数量(手)
# 添加交易记录
transactions.append(transaction.copy())
elif df.iloc[i, df.columns.get_loc('短期涨跌幅')] <= down_threshold and current_position < 1:
# 短期下跌超过阈值且有现金可加仓
add_position = min(position_step, 1 - current_position)
df.iloc[i, df.columns.get_loc('仓位')] += add_position
df.iloc[i, df.columns.get_loc('现金比例')] -= add_position
# 计算操作金额(负数表示现金减少,用于买入)
operation_amount = -prev_assets * add_position
# 计算交易数量(手),确保不低于1手
price = df.iloc[i, df.columns.get_loc('收盘')]
# 买入时,使用现金计算可购买的股数,再转换为手数
available_cash = prev_assets * (1 - current_position) # 可用现金
max_shares = int(available_cash / price)
shares = min(max_shares, int(abs(operation_amount) / price))
lots = max(1, shares // 100) # 转换为手数,确保不低于1手
# 更新交易信息
transaction['操作'] = '加仓'
transaction['操作后仓位'] = current_position + add_position
transaction['操作金额'] = operation_amount
transaction['交易数量(手)'] = lots # 记录交易数量(手)
# 添加交易记录
transactions.append(transaction.copy())
# 计算当日总资产变化
price_change = df.iloc[i, df.columns.get_loc('收盘')] / df.iloc[i-1, df.columns.get_loc('收盘')] - 1
position_return = df.iloc[i-1, df.columns.get_loc('仓位')] * price_change
df.iloc[i, df.columns.get_loc('总资产')] = prev_assets * (1 + position_return)
# 计算持仓市值和现金
df.iloc[i, df.columns.get_loc('持仓市值')] = df.iloc[i, df.columns.get_loc('总资产')] * df.iloc[i, df.columns.get_loc('仓位')]
df.iloc[i, df.columns.get_loc('现金')] = df.iloc[i, df.columns.get_loc('总资产')] * df.iloc[i, df.columns.get_loc('现金比例')]
# 计算基准收益(买入持有策略)
df['基准收益'] = df['收盘'] / df['收盘'].iloc[0]
df['基准总资产'] = initial_capital * df['基准收益']
# 计算沪深300收益
df['沪深300收益'] = df['沪深300收盘'] / df['沪深300收盘'].iloc[0]
df['沪深300总资产'] = initial_capital * df['沪深300收益']
# 计算策略评价指标
total_days = len(df)
annual_return = (df['总资产'].iloc[-1] / df['总资产'].iloc[0]) ** (252 / total_days) - 1
annual_volatility = df['总资产'].pct_change().std() * (252 ** 0.5)
sharpe_ratio = annual_return / annual_volatility if annual_volatility != 0 else 0
# 计算基准评价指标
benchmark_annual_return = (df['基准总资产'].iloc[-1] / df['基准总资产'].iloc[0]) ** (252 / total_days) - 1
benchmark_annual_volatility = df['基准总资产'].pct_change().std() * (252 ** 0.5)
benchmark_sharpe_ratio = benchmark_annual_return / benchmark_annual_volatility if benchmark_annual_volatility != 0 else 0
# 计算沪深300评价指标
hs300_annual_return = (df['沪深300总资产'].iloc[-1] / df['沪深300总资产'].iloc[0]) ** (252 / total_days) - 1
hs300_annual_volatility = df['沪深300总资产'].pct_change().std() * (252 ** 0.5)
hs300_sharpe_ratio = hs300_annual_return / hs300_annual_volatility if hs300_annual_volatility != 0 else 0
# 最大回撤计算
df['累计最高'] = df['总资产'].cummax()
df['回撤'] = df['总资产'] / df['累计最高'] - 1
max_drawdown = df['回撤'].min()
# 打印策略评价指标
print(f"初始资金: {initial_capital}元")
print(f"策略年化收益率: {annual_return:.2%}")
print(f"策略年化波动率: {annual_volatility:.2%}")
print(f"策略夏普比率: {sharpe_ratio:.2f}")
print(f"策略最大回撤: {max_drawdown:.2%}")
print(f"基准(红利低波)年化收益率: {benchmark_annual_return:.2%}")
print(f"沪深300年化收益率: {hs300_annual_return:.2%}")
# 创建一个共享x轴的子图布局
fig, (ax1, ax2) = plt.subplots(2, 1, figsize=(14, 10), sharex=True, gridspec_kw={'hspace': 0.1})
# 绘制策略收益、基准收益与沪深300收益对比图
ax1.plot(df.index, df['总资产']/initial_capital, label='策略收益', color='blue')
ax1.plot(df.index, df['基准总资产']/initial_capital, label='基准收益(红利低波)', color='red')
#ax1.plot(df.index, df['沪深300总资产']/initial_capital, label='沪深300收益', color='purple')
ax1.set_title('红利低波ETF(512890)策略回测收益与仓位变化', fontproperties=font)
ax1.set_ylabel('累计收益(初始=1)', fontproperties=font)
ax1.legend(prop=font)
ax1.grid(True)
# 绘制仓位变化图
ax2.plot(df.index, df['仓位'], label='仓位', color='green')
ax2.axhline(y=min_position, color='r', linestyle='-', label='最低仓位线')
ax2.set_xlabel('日期', fontproperties=font)
ax2.set_ylabel('仓位比例', fontproperties=font)
ax2.legend(prop=font)
ax2.grid(True)
plt.tight_layout()
plt.show()
# 输出最终持仓情况
print(f"\n最终资产状况:")
print(f"策略最终总资产: {df['总资产'].iloc[-1]:.2f}元")
print(f"策略最终仓位: {df['仓位'].iloc[-1]:.2%}")
print(f"策略最终持仓市值: {df['持仓市值'].iloc[-1]:.2f}元")
print(f"策略最终现金: {df['现金'].iloc[-1]:.2f}元")
print(f"基准(红利低波)最终总资产: {df['基准总资产'].iloc[-1]:.2f}元")
print(f"沪深300最终总资产: {df['沪深300总资产'].iloc[-1]:.2f}元")
# 打印最近的交易记录
if transactions:
print("\n最近的交易记录:")
transactions_df = pd.DataFrame(transactions)
transactions_df['操作金额'] = transactions_df['操作金额'].apply(lambda x: f"+{x:.2f}" if x >=0 else f"{x:.2f}")
for _, row in transactions_df.tail().iterrows():
print(f"{row['日期'].strftime('%Y-%m-%d')} | {row['操作']} | 价格: {row['价格']:.3f} | "
f"仓位: {row['操作前仓位']:.2%} → {row['操作后仓位']:.2%} | "
f"数量: {row['交易数量(手)']}手 | 金额: {row['操作金额']}元")
else:
print("\n策略执行期间没有产生交易")
上一篇:【策略】金字塔补仓法 下一篇:黄金每次下跌都是建仓机会
次方量化-技术博客
发表评论:
◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。