次方量化 全球宏观 每月宏观 公众号 股票开户

【策略】中证红利低吸高抛

lindercube 2个月前 (10-30) 阅读数 164 #量化教程

最终资产状况:

策略最终总资产: 231336.85元

策略最终仓位: 100.00%

策略最终持仓市值: 231336.85元

策略最终现金: 0.00元

基准(红利低波)最终总资产: 195888.16元

沪深300最终总资产: 113946.27元

image.png


# -*- 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策略执行期间没有产生交易")





分享到:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门
标签列表