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

千问AI:区间涨幅动量轮动(3)

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

现在AI已经牛到,你只需要讲中文,给他一些知识库的内容,他就可以给你写量化策略,然后按照你的要求做优化和提升。

对AI的任务需求:

用akshare数据源写一个ETF趋势交易的轮动策略

豆包提示词:

#通过Akshare获取数据源,返回字段 date open high low close volume,5开头的基金是sh,1开头的基金是sz,把里面的条件尽量参数化

import akshare as ak

fund_etf_hist_sina_df = ak.fund_etf_hist_sina(symbol="sh510050")

print(fund_etf_hist_sina_df)

plt.rcParams["font.family"] = ["SimHei"]

image.png

import akshare as ak
import pandas as pd
import numpy as np
import time

# ========== 配置参数 ==========
etf_pool = [
    "sh510300",  # 沪深300ETF
    "sh510500",  # 中证500ETF
    "sh512880",  # 证券ETF
    "sh512660",  # 军工ETF
    "sh513500",  # 标普500ETF
    "sh513100",  # 纳指ETF
    "sh513130",  # 恒生科技ETF
    "sz159915",  # 创业板ETF
]

start_year = 2018
end_date_str = "2025-11-01"
momentum_window = 20   # 动量窗口(交易日)
top_k = 1              # 选Top 1

# ========== 获取数据 ==========
etf_data_dict = {}
for symbol in etf_pool:
    try:
        print(f"正在获取 {symbol} 数据...")
        df = ak.fund_etf_hist_sina(symbol=symbol)
        df['date'] = pd.to_datetime(df['date'])
        df.set_index('date', inplace=True)
        df.sort_index(inplace=True)
        etf_data_dict[symbol] = df
        time.sleep(0.3)
    except Exception as e:
        print(f"❌ 获取 {symbol} 失败: {e}")

# ========== 构建价格矩阵 ==========
all_dates = sorted(set.union(*[set(df.index) for df in etf_data_dict.values()]))
price_df = pd.DataFrame(index=all_dates)
for symbol, df in etf_data_dict.items():
    price_df[symbol] = df['close']

price_df = price_df.loc[f"{start_year}-01-01":end_date_str].sort_index().ffill().dropna(how='all')
price_df.dropna(how='all', inplace=True)

if price_df.empty:
    raise ValueError("价格数据为空,请检查ETF代码或网络")

# ========== 找出每周第一个交易日 ==========
trading_days = price_df.index
rebalance_dates = []
for i, date in enumerate(trading_days):
    if i == 0:
        rebalance_dates.append(date)
    else:
        prev = trading_days[i - 1]
        if date.week != prev.week or date.year != prev.year:
            rebalance_dates.append(date)
weekly_rebalance_dates = pd.DatetimeIndex(rebalance_dates)

# ========== 初始化持仓序列(修复版)==========
position_series = pd.Series([[] for _ in range(len(price_df))], index=price_df.index)

# ========== 执行每周调仓 ==========
for date in weekly_rebalance_dates:
    lookback_start = date - pd.Timedelta(days=momentum_window * 2)
    window_data = price_df.loc[lookback_start:date]
    
    if len(window_data) < momentum_window:
        continue
        
    returns = (window_data.iloc[-1] / window_data.iloc[0]) - 1
    returns = returns.dropna()
    if returns.empty:
        continue
        
    top_symbols = returns.nlargest(top_k).index.tolist()
    position_series[date] = top_symbols

# 向前填充持仓
position_series.ffill(inplace=True)

# ========== 计算策略收益 ==========
strategy_ret = pd.Series(index=price_df.index, dtype=float)
strategy_ret.iloc[0] = 0.0

for i in range(1, len(price_df)):
    today = price_df.index[i]
    yesterday = price_df.index[i - 1]
    holdings = position_series[today]
    
    if not holdings:
        strategy_ret[today] = 0.0
    else:
        daily_rtn = 0.0
        valid_count = 0
        weight = 1.0 / len(holdings)
        for sym in holdings:
            if pd.notna(price_df.loc[yesterday, sym]) and pd.notna(price_df.loc[today, sym]):
                ret = price_df.loc[today, sym] / price_df.loc[yesterday, sym] - 1
                daily_rtn += weight * ret
                valid_count += 1
        strategy_ret[today] = daily_rtn if valid_count > 0 else 0.0

strategy_nav = (1 + strategy_ret).cumprod()

# ========== 基准 ==========
benchmark_symbol = "sh510300"
benchmark_nav = price_df[benchmark_symbol] / price_df[benchmark_symbol].iloc[0]

# ========== 输出结果 ==========
def annual_return(nav, freq=252): return nav.iloc[-1] ** (freq / len(nav)) - 1
def max_drawdown(nav): 
    rm = nav.cummax()
    return ((nav - rm) / rm).min()

print(f"策略年化收益: {annual_return(strategy_nav):.2%}")
print(f"基准年化收益: {annual_return(benchmark_nav):.2%}")
print(f"策略最大回撤: {max_drawdown(strategy_nav):.2%}")
print(f"调仓次数: {len(weekly_rebalance_dates)}")

# ========== 绘图 ==========
import matplotlib.pyplot as plt
plt.figure(figsize=(12, 6))
plt.plot(strategy_nav, label='每周轮动策略')
plt.plot(benchmark_nav, label='沪深300ETF')
plt.legend()
plt.title('ETF 趋势轮动策略(每周一调仓 | sina 数据源)')
plt.grid(True)
plt.show()


image.png


策略年化收益: 42.85%

基准年化收益: 1.87%

策略最大回撤: -16.83%

调仓次数: 403




分享到:

发表评论:

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

热门
标签列表