1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
| import pandas as pd import akshare as ak import numpy as np
RSI_PERIOD = 6 RSI_OVERSOLD = 20 RSI_OVERBOUGHT = 80 INITIAL_CAPITAL = 10000 START_DATE = "2024-06-01" BOLLINGER_WINDOW = 20 BOLLINGER_STD = 2 ETF_LIST = [ ("主要消费ETF", "159928"), ("酒ETF", "512690"), ("细分食品饮料ETF", "515710"),
("医疗ETF", "512170"), ("生物医药ETF", "512290"),
("科技龙头ETF", "515000"), ("半导体芯片ETF", "159995"), ("人工智能ETF", "515980"), ("5G通信主题ETF", "515050"), ("计算机主题ETF", "512720"),
("新能源汽车ETF", "515030"), ("光伏产业ETF", "515790"), ("碳中和ETF", "159790"),
("有色金属ETF", "512400"), ("煤炭ETF", "515220"), ("钢铁ETF", "515210"), ("化工ETF", "159870"),
("银行ETF", "512800"), ("证券ETF", "512880"), ("房地产ETF", "512200"),
("沪深300ETF", "510300"), ("创业板50ETF", "159949"),
("军工ETF", "512660"), ("黄金ETF", "518880"), ("农业主题ETF", "159825"), ("家电ETF", "159996"), ("传媒ETF", "512980"), ("畜牧养殖ETF", "159865"), ("基建ETF", "516950"), ("现代物流ETF", "516910"), ("稀土产业ETF", "516780"), ("旅游主题ETF", "159766"), ("电力ETF", "159611"), ("红利ETF", "510880"), ("纳斯达克ETF", "159632"), ("标普500ETF", "513650")
]
def fetch_historical_data(etf_code): """获取历史数据""" try: df = ak.fund_etf_hist_em(symbol=etf_code, adjust="qfq") if df.empty: print(f"获取 {etf_code} 历史数据失败: 数据为空") return None df['日期'] = pd.to_datetime(df['日期']) df = df.sort_values(by='日期', ascending=True) return df except Exception as e: print(f"获取 {etf_code} 历史数据失败: {e}") return None
def calculate_rsi(data, period=14): """计算RSI""" close_prices = data['收盘'] if len(close_prices) < period + 1: return None
deltas = close_prices.diff() gains = deltas.where(deltas > 0, 0) losses = -deltas.where(deltas < 0, 0)
avg_gain = gains.ewm(alpha=1 / period, adjust=False).mean() avg_loss = losses.ewm(alpha=1 / period, adjust=False).mean()
rs = avg_gain / avg_loss rsi = 100.0 - (100.0 / (1 + rs)) return rsi
def calculate_bollinger_bands(data, window=20, std=2): """计算布林带""" data['Middle Band'] = data['收盘'].rolling(window=window).mean() data['Std Dev'] = data['收盘'].rolling(window=window).std() data['Upper Band'] = data['Middle Band'] + (data['Std Dev'] * std) data['Lower Band'] = data['Middle Band'] - (data['Std Dev'] * std) return data
def backtest_strategy(data, initial_capital): """回测策略""" data['RSI'] = calculate_rsi(data, RSI_PERIOD) data = calculate_bollinger_bands(data, BOLLINGER_WINDOW, BOLLINGER_STD)
data['Signal'] = 0 data['Position'] = 0 data['Profit'] = 0.0
position = 0 buy_price = 0.0 buy_count = 0 sell_count = 0 total_profit = 0.0 capital = initial_capital
trades = []
for i in range(1, len(data)): current_price = data['收盘'].iloc[i] if position == 0 and data['RSI'].iloc[i] < RSI_OVERSOLD and current_price <= data['Lower Band'].iloc[i]: position = 1 buy_price = current_price buy_count += 1 trades.append({ '日期': data['日期'].iloc[i], '价格': buy_price, 'RSI': data['RSI'].iloc[i], '布林带': data['Lower Band'].iloc[i], '操作': '买入' })
elif position == 1 and (current_price >= data['Upper Band'].iloc[i] or data['RSI'].iloc[i] >= RSI_OVERBOUGHT): position = 0 profit = (current_price - buy_price) / buy_price total_profit += profit capital *= (1 + profit) sell_count += 1 trades.append({ '日期': data['日期'].iloc[i], '价格': current_price, 'RSI': data['RSI'].iloc[i], '布林带': data['Upper Band'].iloc[i], '操作': '卖出' })
return buy_count, sell_count, total_profit, capital, trades
def main(): results = [] for etf_name, etf_code in ETF_LIST: print(f"\n开始回测基金: {etf_name} ({etf_code})") data = fetch_historical_data(etf_code) if data is None: print(f"无法获取 {etf_code} 的历史数据,跳过该基金。") continue
data = data[data['日期'] >= START_DATE] if len(data) == 0: print(f"没有找到 {START_DATE} 之后的数据,请调整开始时间。") continue
buy_count, sell_count, total_profit, final_capital, trades = backtest_strategy(data, INITIAL_CAPITAL)
results.append({ '基金名称': etf_name, '基金代码': etf_code, '买入次数': buy_count, '卖出次数': sell_count, '总盈利': total_profit, '初始本金': INITIAL_CAPITAL, '最终本金': final_capital, '最终盈利': final_capital - INITIAL_CAPITAL, '年化收益': (total_profit / len(data) * 252) * 100, '交易记录': trades })
print("\n所有基金的回测结果:") for result in results: print(f"\n基金名称: {result['基金名称']} ({result['基金代码']})") print(f"买入次数: {result['买入次数']}") print(f"卖出次数: {result['卖出次数']}") print(f"总盈利: {result['总盈利'] * 100:.2f}%") print(f"初始本金: {result['初始本金']:.2f} 元") print(f"最终本金: {result['最终本金']:.2f} 元") print(f"最终盈利: {result['最终盈利']:.2f} 元") print(f"年化收益: {result['年化收益']:.2f}%") print("\n交易记录:") for trade in result['交易记录']: print(f"{trade['操作']} - 时间: {trade['日期']}, 价格: {trade['价格']:.2f} 元, RSI: {trade['RSI']:.2f}, 布林带: {trade['布林带']:.2f}")
if __name__ == "__main__": main()
|