iciv-predictor 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- iciv_predictor-0.1.0/PKG-INFO +181 -0
- iciv_predictor-0.1.0/README.md +157 -0
- iciv_predictor-0.1.0/iciv_predictor/__init__.py +18 -0
- iciv_predictor-0.1.0/iciv_predictor/backtest.py +173 -0
- iciv_predictor-0.1.0/iciv_predictor/cli.py +111 -0
- iciv_predictor-0.1.0/iciv_predictor/predictor.py +159 -0
- iciv_predictor-0.1.0/iciv_predictor.egg-info/PKG-INFO +181 -0
- iciv_predictor-0.1.0/iciv_predictor.egg-info/SOURCES.txt +12 -0
- iciv_predictor-0.1.0/iciv_predictor.egg-info/dependency_links.txt +1 -0
- iciv_predictor-0.1.0/iciv_predictor.egg-info/entry_points.txt +2 -0
- iciv_predictor-0.1.0/iciv_predictor.egg-info/requires.txt +6 -0
- iciv_predictor-0.1.0/iciv_predictor.egg-info/top_level.txt +1 -0
- iciv_predictor-0.1.0/pyproject.toml +45 -0
- iciv_predictor-0.1.0/setup.cfg +4 -0
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iciv-predictor
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ICIV股票预测竞赛SDK - 用于开发和测试交易策略
|
|
5
|
+
Author-email: ICIV Lab <iciv@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://stock.w3drop.com
|
|
8
|
+
Project-URL: Documentation, https://stock.w3drop.com/docs
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: pandas>=1.5.0
|
|
20
|
+
Requires-Dist: numpy>=1.21.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
24
|
+
|
|
25
|
+
# ICIV Stock Predictor SDK
|
|
26
|
+
|
|
27
|
+
股票预测竞赛SDK,用于开发和测试交易策略。
|
|
28
|
+
|
|
29
|
+
## 安装
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install iciv-predictor
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
或从源码安装:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/your-repo/iciv-predictor-sdk.git
|
|
39
|
+
cd iciv-predictor-sdk
|
|
40
|
+
pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 快速开始
|
|
44
|
+
|
|
45
|
+
### 1. 创建预测器
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from iciv_predictor import BasePredictor, PredictionOutput
|
|
49
|
+
import numpy as np
|
|
50
|
+
|
|
51
|
+
class MyPredictor(BasePredictor):
|
|
52
|
+
def __init__(self, df, predict_steps=48):
|
|
53
|
+
super().__init__(df, predict_steps)
|
|
54
|
+
# 初始化你的参数
|
|
55
|
+
self.short_ma = 12
|
|
56
|
+
self.long_ma = 48
|
|
57
|
+
|
|
58
|
+
def predict_step(self, current_idx):
|
|
59
|
+
"""
|
|
60
|
+
预测当前时间点
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
current_idx: 当前索引,只能使用历史数据 df[:current_idx+1]
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
PredictionOutput 或 None
|
|
67
|
+
"""
|
|
68
|
+
if current_idx < self.long_ma:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
# 获取历史收盘价
|
|
72
|
+
closes = self.get_close_prices(current_idx)
|
|
73
|
+
|
|
74
|
+
# 计算均线
|
|
75
|
+
short_avg = closes[-self.short_ma:].mean()
|
|
76
|
+
long_avg = closes[-self.long_ma:].mean()
|
|
77
|
+
|
|
78
|
+
# 判断方向
|
|
79
|
+
if short_avg > long_avg:
|
|
80
|
+
direction = 1 # 看涨,买入
|
|
81
|
+
elif short_avg < long_avg:
|
|
82
|
+
direction = -1 # 看跌,卖出
|
|
83
|
+
else:
|
|
84
|
+
direction = 0 # 持有
|
|
85
|
+
|
|
86
|
+
return PredictionOutput(
|
|
87
|
+
predict_from_idx=current_idx,
|
|
88
|
+
predicted_prices=np.zeros(self.predict_steps),
|
|
89
|
+
predicted_returns=np.zeros(self.predict_steps),
|
|
90
|
+
confidence=0.6,
|
|
91
|
+
direction=direction
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 2. 本地测试
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
iciv-test my_predictor.py --data 000021_SZ.csv
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
或使用Python:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from iciv_predictor import Backtester
|
|
105
|
+
import pandas as pd
|
|
106
|
+
|
|
107
|
+
# 加载数据
|
|
108
|
+
df = pd.read_csv('000021_SZ.csv')
|
|
109
|
+
df['trade_time'] = pd.to_datetime(df['datetime'])
|
|
110
|
+
|
|
111
|
+
# 创建预测器
|
|
112
|
+
predictor = MyPredictor(df)
|
|
113
|
+
|
|
114
|
+
# 运行回测
|
|
115
|
+
backtester = Backtester()
|
|
116
|
+
result = backtester.run(df, predictor)
|
|
117
|
+
|
|
118
|
+
print(f"收益率: {result.total_return:.2f}%")
|
|
119
|
+
print(f"最大回撤: {result.max_drawdown:.2f}%")
|
|
120
|
+
print(f"交易次数: {result.total_trades}")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. 提交到平台
|
|
124
|
+
|
|
125
|
+
测试通过后,将你的 `.py` 文件提交到 https://stock.w3drop.com
|
|
126
|
+
|
|
127
|
+
## API 参考
|
|
128
|
+
|
|
129
|
+
### BasePredictor
|
|
130
|
+
|
|
131
|
+
基类,所有预测器必须继承此类。
|
|
132
|
+
|
|
133
|
+
**方法**:
|
|
134
|
+
|
|
135
|
+
| 方法 | 说明 |
|
|
136
|
+
|------|------|
|
|
137
|
+
| `__init__(df, predict_steps=48)` | 初始化,predict_steps为预测步长 |
|
|
138
|
+
| `predict_step(current_idx)` | **必须实现**,返回PredictionOutput |
|
|
139
|
+
| `get_historical_data(idx)` | 获取历史DataFrame |
|
|
140
|
+
| `get_close_prices(idx)` | 获取历史收盘价数组 |
|
|
141
|
+
| `get_returns(idx)` | 获取历史收益率数组 |
|
|
142
|
+
|
|
143
|
+
### PredictionOutput
|
|
144
|
+
|
|
145
|
+
预测输出结构。
|
|
146
|
+
|
|
147
|
+
**字段**:
|
|
148
|
+
|
|
149
|
+
| 字段 | 类型 | 说明 |
|
|
150
|
+
|------|------|------|
|
|
151
|
+
| `predict_from_idx` | int | 预测起始索引 |
|
|
152
|
+
| `predicted_prices` | np.ndarray | 预测价格序列 |
|
|
153
|
+
| `predicted_returns` | np.ndarray | 预测收益率序列 |
|
|
154
|
+
| `confidence` | float | 置信度 (0-1) |
|
|
155
|
+
| `direction` | int | **交易信号**: 1=买入, -1=卖出, 0=持有 |
|
|
156
|
+
|
|
157
|
+
## 回测规则
|
|
158
|
+
|
|
159
|
+
1. **预测频率**: 每48步(约4小时)预测一次
|
|
160
|
+
2. **测试集**: 使用后20%数据
|
|
161
|
+
3. **交易逻辑**:
|
|
162
|
+
- direction=1 且无持仓 → 买入
|
|
163
|
+
- direction=-1 且有持仓 → 卖出
|
|
164
|
+
4. **手续费**: 买卖各万分之三
|
|
165
|
+
5. **强制平仓**: 测试期结束时平仓
|
|
166
|
+
|
|
167
|
+
## 注意事项
|
|
168
|
+
|
|
169
|
+
⚠️ **禁止使用未来数据**!`predict_step(idx)` 只能访问 `idx` 及之前的数据。
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
# ✅ 正确
|
|
173
|
+
closes = self.get_close_prices(current_idx)
|
|
174
|
+
|
|
175
|
+
# ❌ 错误 - 访问了未来数据
|
|
176
|
+
future = self.df['close'].iloc[current_idx + 10]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# ICIV Stock Predictor SDK
|
|
2
|
+
|
|
3
|
+
股票预测竞赛SDK,用于开发和测试交易策略。
|
|
4
|
+
|
|
5
|
+
## 安装
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install iciv-predictor
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
或从源码安装:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
git clone https://github.com/your-repo/iciv-predictor-sdk.git
|
|
15
|
+
cd iciv-predictor-sdk
|
|
16
|
+
pip install -e .
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## 快速开始
|
|
20
|
+
|
|
21
|
+
### 1. 创建预测器
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from iciv_predictor import BasePredictor, PredictionOutput
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
class MyPredictor(BasePredictor):
|
|
28
|
+
def __init__(self, df, predict_steps=48):
|
|
29
|
+
super().__init__(df, predict_steps)
|
|
30
|
+
# 初始化你的参数
|
|
31
|
+
self.short_ma = 12
|
|
32
|
+
self.long_ma = 48
|
|
33
|
+
|
|
34
|
+
def predict_step(self, current_idx):
|
|
35
|
+
"""
|
|
36
|
+
预测当前时间点
|
|
37
|
+
|
|
38
|
+
Args:
|
|
39
|
+
current_idx: 当前索引,只能使用历史数据 df[:current_idx+1]
|
|
40
|
+
|
|
41
|
+
Returns:
|
|
42
|
+
PredictionOutput 或 None
|
|
43
|
+
"""
|
|
44
|
+
if current_idx < self.long_ma:
|
|
45
|
+
return None
|
|
46
|
+
|
|
47
|
+
# 获取历史收盘价
|
|
48
|
+
closes = self.get_close_prices(current_idx)
|
|
49
|
+
|
|
50
|
+
# 计算均线
|
|
51
|
+
short_avg = closes[-self.short_ma:].mean()
|
|
52
|
+
long_avg = closes[-self.long_ma:].mean()
|
|
53
|
+
|
|
54
|
+
# 判断方向
|
|
55
|
+
if short_avg > long_avg:
|
|
56
|
+
direction = 1 # 看涨,买入
|
|
57
|
+
elif short_avg < long_avg:
|
|
58
|
+
direction = -1 # 看跌,卖出
|
|
59
|
+
else:
|
|
60
|
+
direction = 0 # 持有
|
|
61
|
+
|
|
62
|
+
return PredictionOutput(
|
|
63
|
+
predict_from_idx=current_idx,
|
|
64
|
+
predicted_prices=np.zeros(self.predict_steps),
|
|
65
|
+
predicted_returns=np.zeros(self.predict_steps),
|
|
66
|
+
confidence=0.6,
|
|
67
|
+
direction=direction
|
|
68
|
+
)
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### 2. 本地测试
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
iciv-test my_predictor.py --data 000021_SZ.csv
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
或使用Python:
|
|
78
|
+
|
|
79
|
+
```python
|
|
80
|
+
from iciv_predictor import Backtester
|
|
81
|
+
import pandas as pd
|
|
82
|
+
|
|
83
|
+
# 加载数据
|
|
84
|
+
df = pd.read_csv('000021_SZ.csv')
|
|
85
|
+
df['trade_time'] = pd.to_datetime(df['datetime'])
|
|
86
|
+
|
|
87
|
+
# 创建预测器
|
|
88
|
+
predictor = MyPredictor(df)
|
|
89
|
+
|
|
90
|
+
# 运行回测
|
|
91
|
+
backtester = Backtester()
|
|
92
|
+
result = backtester.run(df, predictor)
|
|
93
|
+
|
|
94
|
+
print(f"收益率: {result.total_return:.2f}%")
|
|
95
|
+
print(f"最大回撤: {result.max_drawdown:.2f}%")
|
|
96
|
+
print(f"交易次数: {result.total_trades}")
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 3. 提交到平台
|
|
100
|
+
|
|
101
|
+
测试通过后,将你的 `.py` 文件提交到 https://stock.w3drop.com
|
|
102
|
+
|
|
103
|
+
## API 参考
|
|
104
|
+
|
|
105
|
+
### BasePredictor
|
|
106
|
+
|
|
107
|
+
基类,所有预测器必须继承此类。
|
|
108
|
+
|
|
109
|
+
**方法**:
|
|
110
|
+
|
|
111
|
+
| 方法 | 说明 |
|
|
112
|
+
|------|------|
|
|
113
|
+
| `__init__(df, predict_steps=48)` | 初始化,predict_steps为预测步长 |
|
|
114
|
+
| `predict_step(current_idx)` | **必须实现**,返回PredictionOutput |
|
|
115
|
+
| `get_historical_data(idx)` | 获取历史DataFrame |
|
|
116
|
+
| `get_close_prices(idx)` | 获取历史收盘价数组 |
|
|
117
|
+
| `get_returns(idx)` | 获取历史收益率数组 |
|
|
118
|
+
|
|
119
|
+
### PredictionOutput
|
|
120
|
+
|
|
121
|
+
预测输出结构。
|
|
122
|
+
|
|
123
|
+
**字段**:
|
|
124
|
+
|
|
125
|
+
| 字段 | 类型 | 说明 |
|
|
126
|
+
|------|------|------|
|
|
127
|
+
| `predict_from_idx` | int | 预测起始索引 |
|
|
128
|
+
| `predicted_prices` | np.ndarray | 预测价格序列 |
|
|
129
|
+
| `predicted_returns` | np.ndarray | 预测收益率序列 |
|
|
130
|
+
| `confidence` | float | 置信度 (0-1) |
|
|
131
|
+
| `direction` | int | **交易信号**: 1=买入, -1=卖出, 0=持有 |
|
|
132
|
+
|
|
133
|
+
## 回测规则
|
|
134
|
+
|
|
135
|
+
1. **预测频率**: 每48步(约4小时)预测一次
|
|
136
|
+
2. **测试集**: 使用后20%数据
|
|
137
|
+
3. **交易逻辑**:
|
|
138
|
+
- direction=1 且无持仓 → 买入
|
|
139
|
+
- direction=-1 且有持仓 → 卖出
|
|
140
|
+
4. **手续费**: 买卖各万分之三
|
|
141
|
+
5. **强制平仓**: 测试期结束时平仓
|
|
142
|
+
|
|
143
|
+
## 注意事项
|
|
144
|
+
|
|
145
|
+
⚠️ **禁止使用未来数据**!`predict_step(idx)` 只能访问 `idx` 及之前的数据。
|
|
146
|
+
|
|
147
|
+
```python
|
|
148
|
+
# ✅ 正确
|
|
149
|
+
closes = self.get_close_prices(current_idx)
|
|
150
|
+
|
|
151
|
+
# ❌ 错误 - 访问了未来数据
|
|
152
|
+
future = self.df['close'].iloc[current_idx + 10]
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## License
|
|
156
|
+
|
|
157
|
+
MIT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ICIV Stock Predictor SDK
|
|
3
|
+
股票预测竞赛SDK - 用于开发和测试交易策略
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
__version__ = "0.1.0"
|
|
7
|
+
__author__ = "ICIV Lab"
|
|
8
|
+
|
|
9
|
+
from .predictor import BasePredictor, PredictionOutput
|
|
10
|
+
from .backtest import Backtester, BacktestConfig, BacktestResult
|
|
11
|
+
|
|
12
|
+
__all__ = [
|
|
13
|
+
'BasePredictor',
|
|
14
|
+
'PredictionOutput',
|
|
15
|
+
'Backtester',
|
|
16
|
+
'BacktestConfig',
|
|
17
|
+
'BacktestResult'
|
|
18
|
+
]
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""
|
|
2
|
+
回测引擎 - 模拟服务器回测逻辑
|
|
3
|
+
"""
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from typing import List, Optional
|
|
6
|
+
from datetime import datetime
|
|
7
|
+
import numpy as np
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dataclass
|
|
12
|
+
class TradeRecord:
|
|
13
|
+
"""交易记录"""
|
|
14
|
+
trade_time: datetime
|
|
15
|
+
trade_type: str # 'BUY' or 'SELL'
|
|
16
|
+
price: float
|
|
17
|
+
shares: int
|
|
18
|
+
amount: float
|
|
19
|
+
fee: float
|
|
20
|
+
capital_after: float
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class BacktestResult:
|
|
25
|
+
"""回测结果"""
|
|
26
|
+
initial_capital: float
|
|
27
|
+
final_capital: float
|
|
28
|
+
total_return: float # 收益率 %
|
|
29
|
+
max_drawdown: float # 最大回撤 %
|
|
30
|
+
win_rate: float # 胜率 %
|
|
31
|
+
total_trades: int
|
|
32
|
+
trade_records: List[TradeRecord]
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
@dataclass
|
|
36
|
+
class BacktestConfig:
|
|
37
|
+
"""回测配置"""
|
|
38
|
+
initial_capital: float = 1000000.0 # 初始资金 100万
|
|
39
|
+
buy_fee_rate: float = 0.0003 # 买入费率 万三
|
|
40
|
+
sell_fee_rate: float = 0.0003 # 卖出费率 万三
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class Backtester:
|
|
44
|
+
"""
|
|
45
|
+
回测引擎 - 完全匹配服务器逻辑
|
|
46
|
+
|
|
47
|
+
特点:
|
|
48
|
+
- 每48步预测一次(约4小时)
|
|
49
|
+
- direction=1 买入, direction=-1 卖出
|
|
50
|
+
- 测试结束强制平仓
|
|
51
|
+
"""
|
|
52
|
+
|
|
53
|
+
def __init__(self, config: BacktestConfig = None):
|
|
54
|
+
self.config = config or BacktestConfig()
|
|
55
|
+
|
|
56
|
+
def run(self, df: pd.DataFrame, predictor) -> BacktestResult:
|
|
57
|
+
"""
|
|
58
|
+
运行回测
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
df: K线数据 (需包含 trade_time, close 列)
|
|
62
|
+
predictor: 预测器实例
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
BacktestResult
|
|
66
|
+
"""
|
|
67
|
+
capital = self.config.initial_capital
|
|
68
|
+
position = 0
|
|
69
|
+
trades = []
|
|
70
|
+
equity_curve = [capital]
|
|
71
|
+
|
|
72
|
+
predict_steps = predictor.predict_steps
|
|
73
|
+
total_len = len(df)
|
|
74
|
+
|
|
75
|
+
# 每48步预测一次
|
|
76
|
+
for idx in range(0, total_len - predict_steps, predict_steps):
|
|
77
|
+
output = predictor.predict_step(idx)
|
|
78
|
+
|
|
79
|
+
if output is None:
|
|
80
|
+
continue
|
|
81
|
+
|
|
82
|
+
direction = output.direction
|
|
83
|
+
price = df['close'].iloc[idx]
|
|
84
|
+
trade_time = df['trade_time'].iloc[idx]
|
|
85
|
+
|
|
86
|
+
# 买入
|
|
87
|
+
if direction == 1 and position == 0:
|
|
88
|
+
shares = int(capital * 0.99 / price / 100) * 100
|
|
89
|
+
if shares >= 100:
|
|
90
|
+
amount = shares * price
|
|
91
|
+
fee = amount * self.config.buy_fee_rate
|
|
92
|
+
capital -= (amount + fee)
|
|
93
|
+
position = shares
|
|
94
|
+
trades.append(TradeRecord(
|
|
95
|
+
trade_time=trade_time,
|
|
96
|
+
trade_type='BUY',
|
|
97
|
+
price=price,
|
|
98
|
+
shares=shares,
|
|
99
|
+
amount=amount,
|
|
100
|
+
fee=fee,
|
|
101
|
+
capital_after=capital
|
|
102
|
+
))
|
|
103
|
+
|
|
104
|
+
# 卖出
|
|
105
|
+
elif direction == -1 and position > 0:
|
|
106
|
+
amount = position * price
|
|
107
|
+
fee = amount * self.config.sell_fee_rate
|
|
108
|
+
capital += (amount - fee)
|
|
109
|
+
trades.append(TradeRecord(
|
|
110
|
+
trade_time=trade_time,
|
|
111
|
+
trade_type='SELL',
|
|
112
|
+
price=price,
|
|
113
|
+
shares=position,
|
|
114
|
+
amount=amount,
|
|
115
|
+
fee=fee,
|
|
116
|
+
capital_after=capital
|
|
117
|
+
))
|
|
118
|
+
position = 0
|
|
119
|
+
|
|
120
|
+
# 记录权益
|
|
121
|
+
equity = capital + position * price
|
|
122
|
+
equity_curve.append(equity)
|
|
123
|
+
|
|
124
|
+
# 强制平仓
|
|
125
|
+
if position > 0:
|
|
126
|
+
price = df['close'].iloc[-1]
|
|
127
|
+
trade_time = df['trade_time'].iloc[-1]
|
|
128
|
+
amount = position * price
|
|
129
|
+
fee = amount * self.config.sell_fee_rate
|
|
130
|
+
capital += (amount - fee)
|
|
131
|
+
trades.append(TradeRecord(
|
|
132
|
+
trade_time=trade_time,
|
|
133
|
+
trade_type='SELL',
|
|
134
|
+
price=price,
|
|
135
|
+
shares=position,
|
|
136
|
+
amount=amount,
|
|
137
|
+
fee=fee,
|
|
138
|
+
capital_after=capital
|
|
139
|
+
))
|
|
140
|
+
|
|
141
|
+
# 计算指标
|
|
142
|
+
initial = self.config.initial_capital
|
|
143
|
+
total_return = (capital - initial) / initial * 100
|
|
144
|
+
|
|
145
|
+
# 最大回撤
|
|
146
|
+
max_drawdown = 0.0
|
|
147
|
+
peak = equity_curve[0]
|
|
148
|
+
for eq in equity_curve:
|
|
149
|
+
if eq > peak:
|
|
150
|
+
peak = eq
|
|
151
|
+
dd = (peak - eq) / peak * 100
|
|
152
|
+
if dd > max_drawdown:
|
|
153
|
+
max_drawdown = dd
|
|
154
|
+
|
|
155
|
+
# 胜率
|
|
156
|
+
wins = 0
|
|
157
|
+
pairs = 0
|
|
158
|
+
for i in range(0, len(trades) - 1, 2):
|
|
159
|
+
if trades[i].trade_type == 'BUY' and trades[i+1].trade_type == 'SELL':
|
|
160
|
+
pairs += 1
|
|
161
|
+
if trades[i+1].price > trades[i].price:
|
|
162
|
+
wins += 1
|
|
163
|
+
win_rate = (wins / pairs * 100) if pairs > 0 else 0
|
|
164
|
+
|
|
165
|
+
return BacktestResult(
|
|
166
|
+
initial_capital=initial,
|
|
167
|
+
final_capital=capital,
|
|
168
|
+
total_return=total_return,
|
|
169
|
+
max_drawdown=max_drawdown,
|
|
170
|
+
win_rate=win_rate,
|
|
171
|
+
total_trades=len(trades),
|
|
172
|
+
trade_records=trades
|
|
173
|
+
)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""
|
|
2
|
+
命令行工具 - 本地测试策略
|
|
3
|
+
"""
|
|
4
|
+
import argparse
|
|
5
|
+
import sys
|
|
6
|
+
import os
|
|
7
|
+
import importlib.util
|
|
8
|
+
import pandas as pd
|
|
9
|
+
|
|
10
|
+
from .predictor import BasePredictor
|
|
11
|
+
from .backtest import Backtester, BacktestConfig
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def load_predictor_class(filepath):
|
|
15
|
+
"""从文件加载预测器类"""
|
|
16
|
+
spec = importlib.util.spec_from_file_location("strategy", filepath)
|
|
17
|
+
module = importlib.util.module_from_spec(spec)
|
|
18
|
+
spec.loader.exec_module(module)
|
|
19
|
+
|
|
20
|
+
# 查找继承BasePredictor的类
|
|
21
|
+
for name in dir(module):
|
|
22
|
+
obj = getattr(module, name)
|
|
23
|
+
if isinstance(obj, type) and issubclass(obj, BasePredictor) and obj is not BasePredictor:
|
|
24
|
+
return obj
|
|
25
|
+
|
|
26
|
+
raise ValueError("未找到继承BasePredictor的类")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def main():
|
|
30
|
+
parser = argparse.ArgumentParser(description='ICIV策略本地测试工具')
|
|
31
|
+
parser.add_argument('strategy', help='策略文件路径 (.py)')
|
|
32
|
+
parser.add_argument('--data', '-d', default='000021_SZ.csv', help='数据文件路径')
|
|
33
|
+
parser.add_argument('--capital', '-c', type=float, default=1000000, help='初始资金')
|
|
34
|
+
parser.add_argument('--test-ratio', '-t', type=float, default=0.2, help='测试集比例')
|
|
35
|
+
|
|
36
|
+
args = parser.parse_args()
|
|
37
|
+
|
|
38
|
+
# 检查文件
|
|
39
|
+
if not os.path.exists(args.strategy):
|
|
40
|
+
print(f"❌ 策略文件不存在: {args.strategy}")
|
|
41
|
+
sys.exit(1)
|
|
42
|
+
|
|
43
|
+
if not os.path.exists(args.data):
|
|
44
|
+
print(f"❌ 数据文件不存在: {args.data}")
|
|
45
|
+
sys.exit(1)
|
|
46
|
+
|
|
47
|
+
print(f"\n{'='*60}")
|
|
48
|
+
print(f"📊 ICIV 策略测试")
|
|
49
|
+
print(f"{'='*60}")
|
|
50
|
+
print(f"策略文件: {args.strategy}")
|
|
51
|
+
print(f"数据文件: {args.data}")
|
|
52
|
+
print(f"初始资金: ¥{args.capital:,.0f}")
|
|
53
|
+
print(f"{'='*60}\n")
|
|
54
|
+
|
|
55
|
+
# 加载数据
|
|
56
|
+
try:
|
|
57
|
+
df = pd.read_csv(args.data)
|
|
58
|
+
if 'datetime' in df.columns:
|
|
59
|
+
df = df.rename(columns={'datetime': 'trade_time'})
|
|
60
|
+
df['trade_time'] = pd.to_datetime(df['trade_time'])
|
|
61
|
+
print(f"✓ 数据加载: {len(df)} 条记录")
|
|
62
|
+
except Exception as e:
|
|
63
|
+
print(f"❌ 数据加载失败: {e}")
|
|
64
|
+
sys.exit(1)
|
|
65
|
+
|
|
66
|
+
# 加载策略
|
|
67
|
+
try:
|
|
68
|
+
predictor_class = load_predictor_class(args.strategy)
|
|
69
|
+
print(f"✓ 策略加载: {predictor_class.__name__}")
|
|
70
|
+
except Exception as e:
|
|
71
|
+
print(f"❌ 策略加载失败: {e}")
|
|
72
|
+
sys.exit(1)
|
|
73
|
+
|
|
74
|
+
# 分割数据
|
|
75
|
+
test_start = int(len(df) * (1 - args.test_ratio))
|
|
76
|
+
test_df = df.iloc[test_start:].reset_index(drop=True)
|
|
77
|
+
print(f"✓ 测试集: {len(test_df)} 条 (从索引 {test_start} 开始)")
|
|
78
|
+
|
|
79
|
+
# 创建预测器和回测器
|
|
80
|
+
try:
|
|
81
|
+
predictor = predictor_class(test_df)
|
|
82
|
+
config = BacktestConfig(initial_capital=args.capital)
|
|
83
|
+
backtester = Backtester(config)
|
|
84
|
+
|
|
85
|
+
print(f"\n🔄 运行回测...")
|
|
86
|
+
result = backtester.run(test_df, predictor)
|
|
87
|
+
|
|
88
|
+
print(f"\n{'='*60}")
|
|
89
|
+
print(f"📈 回测结果")
|
|
90
|
+
print(f"{'='*60}")
|
|
91
|
+
print(f" 初始资金: ¥{result.initial_capital:,.0f}")
|
|
92
|
+
print(f" 最终资金: ¥{result.final_capital:,.2f}")
|
|
93
|
+
print(f" 收益率: {result.total_return:+.2f}%")
|
|
94
|
+
print(f" 最大回撤: {result.max_drawdown:.2f}%")
|
|
95
|
+
print(f" 胜率: {result.win_rate:.1f}%")
|
|
96
|
+
print(f" 交易次数: {result.total_trades}")
|
|
97
|
+
|
|
98
|
+
if result.trade_records:
|
|
99
|
+
print(f"\n📋 交易记录:")
|
|
100
|
+
for t in result.trade_records[-6:]:
|
|
101
|
+
print(f" {t.trade_time} {t.trade_type:5s} ¥{t.price:.2f} x {t.shares}")
|
|
102
|
+
|
|
103
|
+
except Exception as e:
|
|
104
|
+
print(f"❌ 回测失败: {e}")
|
|
105
|
+
import traceback
|
|
106
|
+
traceback.print_exc()
|
|
107
|
+
sys.exit(1)
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
if __name__ == '__main__':
|
|
111
|
+
main()
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
"""
|
|
2
|
+
ICIV Stock Predictor SDK
|
|
3
|
+
用户需要继承 BasePredictor 类实现自己的预测算法
|
|
4
|
+
"""
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from dataclasses import dataclass
|
|
7
|
+
from typing import Optional, List
|
|
8
|
+
import numpy as np
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class PredictionOutput:
|
|
14
|
+
"""预测输出结构"""
|
|
15
|
+
# 预测起始索引 (当前时间点)
|
|
16
|
+
predict_from_idx: int
|
|
17
|
+
|
|
18
|
+
# 预测的价格序列 (长度 = predict_steps)
|
|
19
|
+
predicted_prices: np.ndarray
|
|
20
|
+
|
|
21
|
+
# 预测的收益率序列 (百分比)
|
|
22
|
+
predicted_returns: np.ndarray
|
|
23
|
+
|
|
24
|
+
# 置信度 (0-1)
|
|
25
|
+
confidence: float = 0.5
|
|
26
|
+
|
|
27
|
+
# 方向判断: 1=看涨, -1=看跌, 0=中性
|
|
28
|
+
direction: int = 0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BasePredictor(ABC):
|
|
32
|
+
"""
|
|
33
|
+
预测器基类 - 用户需要继承此类实现自己的预测算法
|
|
34
|
+
|
|
35
|
+
使用示例:
|
|
36
|
+
```python
|
|
37
|
+
class MyPredictor(BasePredictor):
|
|
38
|
+
def __init__(self, df: pd.DataFrame, predict_steps: int = 48):
|
|
39
|
+
super().__init__(df, predict_steps)
|
|
40
|
+
# 自定义初始化...
|
|
41
|
+
|
|
42
|
+
def predict_step(self, current_idx: int) -> Optional[PredictionOutput]:
|
|
43
|
+
# 实现预测逻辑...
|
|
44
|
+
return PredictionOutput(...)
|
|
45
|
+
```
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
def __init__(self, df: pd.DataFrame, predict_steps: int = 48):
|
|
49
|
+
"""
|
|
50
|
+
初始化预测器
|
|
51
|
+
|
|
52
|
+
Args:
|
|
53
|
+
df: K线数据 DataFrame, 包含以下列:
|
|
54
|
+
- trade_time: 交易时间
|
|
55
|
+
- open: 开盘价
|
|
56
|
+
- high: 最高价
|
|
57
|
+
- low: 最低价
|
|
58
|
+
- close: 收盘价
|
|
59
|
+
- volume: 成交量
|
|
60
|
+
- amount: 成交额
|
|
61
|
+
predict_steps: 预测步数 (默认48步, 即4小时)
|
|
62
|
+
"""
|
|
63
|
+
self.df = df.copy()
|
|
64
|
+
self.predict_steps = predict_steps
|
|
65
|
+
|
|
66
|
+
# 确保数据按时间排序
|
|
67
|
+
if 'trade_time' in self.df.columns:
|
|
68
|
+
self.df = self.df.sort_values('trade_time').reset_index(drop=True)
|
|
69
|
+
|
|
70
|
+
# 计算收益率
|
|
71
|
+
self.df['return'] = self.df['close'].pct_change() * 100
|
|
72
|
+
|
|
73
|
+
# 数据总长度
|
|
74
|
+
self.total_len = len(self.df)
|
|
75
|
+
|
|
76
|
+
@abstractmethod
|
|
77
|
+
def predict_step(self, current_idx: int) -> Optional[PredictionOutput]:
|
|
78
|
+
"""
|
|
79
|
+
对当前时间点进行预测
|
|
80
|
+
|
|
81
|
+
Args:
|
|
82
|
+
current_idx: 当前时间点索引 (只能使用 df[:current_idx+1] 的数据)
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
PredictionOutput 或 None (如果无法预测)
|
|
86
|
+
|
|
87
|
+
注意:
|
|
88
|
+
- 不要使用未来数据! 只能使用 current_idx 及之前的数据
|
|
89
|
+
- direction 字段用于生成交易信号
|
|
90
|
+
"""
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
def get_historical_data(self, current_idx: int) -> pd.DataFrame:
|
|
94
|
+
"""获取历史数据 (截至 current_idx)"""
|
|
95
|
+
return self.df.iloc[:current_idx + 1].copy()
|
|
96
|
+
|
|
97
|
+
def get_close_prices(self, current_idx: int) -> np.ndarray:
|
|
98
|
+
"""获取历史收盘价"""
|
|
99
|
+
return self.df['close'].iloc[:current_idx + 1].values
|
|
100
|
+
|
|
101
|
+
def get_returns(self, current_idx: int) -> np.ndarray:
|
|
102
|
+
"""获取历史收益率"""
|
|
103
|
+
return self.df['return'].iloc[:current_idx + 1].values
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# ==================== 示例预测器 ====================
|
|
107
|
+
|
|
108
|
+
class SimpleMovingAveragePredictor(BasePredictor):
|
|
109
|
+
"""
|
|
110
|
+
简单移动平均预测器 (示例)
|
|
111
|
+
"""
|
|
112
|
+
|
|
113
|
+
def __init__(self, df: pd.DataFrame, predict_steps: int = 48,
|
|
114
|
+
short_window: int = 12, long_window: int = 48):
|
|
115
|
+
super().__init__(df, predict_steps)
|
|
116
|
+
self.short_window = short_window
|
|
117
|
+
self.long_window = long_window
|
|
118
|
+
|
|
119
|
+
def predict_step(self, current_idx: int) -> Optional[PredictionOutput]:
|
|
120
|
+
if current_idx < self.long_window:
|
|
121
|
+
return None
|
|
122
|
+
|
|
123
|
+
closes = self.get_close_prices(current_idx)
|
|
124
|
+
current_price = closes[-1]
|
|
125
|
+
|
|
126
|
+
# 计算移动平均
|
|
127
|
+
short_ma = closes[-self.short_window:].mean()
|
|
128
|
+
long_ma = closes[-self.long_window:].mean()
|
|
129
|
+
|
|
130
|
+
# 判断方向
|
|
131
|
+
if short_ma > long_ma * 1.01: # 短期均线高于长期均线 1%
|
|
132
|
+
direction = 1 # 看涨
|
|
133
|
+
elif short_ma < long_ma * 0.99:
|
|
134
|
+
direction = -1 # 看跌
|
|
135
|
+
else:
|
|
136
|
+
direction = 0
|
|
137
|
+
|
|
138
|
+
# 简单预测: 假设价格按趋势变化
|
|
139
|
+
avg_return = self.df['return'].iloc[current_idx - self.short_window:current_idx + 1].mean()
|
|
140
|
+
predicted_returns = np.full(self.predict_steps, avg_return)
|
|
141
|
+
|
|
142
|
+
# 计算预测价格
|
|
143
|
+
predicted_prices = np.zeros(self.predict_steps)
|
|
144
|
+
price = current_price
|
|
145
|
+
for i in range(self.predict_steps):
|
|
146
|
+
price = price * (1 + predicted_returns[i] / 100)
|
|
147
|
+
predicted_prices[i] = price
|
|
148
|
+
|
|
149
|
+
# 置信度基于均线差距
|
|
150
|
+
ma_diff = abs(short_ma - long_ma) / long_ma
|
|
151
|
+
confidence = min(0.9, 0.3 + ma_diff * 10)
|
|
152
|
+
|
|
153
|
+
return PredictionOutput(
|
|
154
|
+
predict_from_idx=current_idx,
|
|
155
|
+
predicted_prices=predicted_prices,
|
|
156
|
+
predicted_returns=predicted_returns,
|
|
157
|
+
confidence=confidence,
|
|
158
|
+
direction=direction
|
|
159
|
+
)
|
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: iciv-predictor
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: ICIV股票预测竞赛SDK - 用于开发和测试交易策略
|
|
5
|
+
Author-email: ICIV Lab <iciv@example.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://stock.w3drop.com
|
|
8
|
+
Project-URL: Documentation, https://stock.w3drop.com/docs
|
|
9
|
+
Classifier: Development Status :: 4 - Beta
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Requires-Python: >=3.9
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: pandas>=1.5.0
|
|
20
|
+
Requires-Dist: numpy>=1.21.0
|
|
21
|
+
Provides-Extra: dev
|
|
22
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
23
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == "dev"
|
|
24
|
+
|
|
25
|
+
# ICIV Stock Predictor SDK
|
|
26
|
+
|
|
27
|
+
股票预测竞赛SDK,用于开发和测试交易策略。
|
|
28
|
+
|
|
29
|
+
## 安装
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
pip install iciv-predictor
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
或从源码安装:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
git clone https://github.com/your-repo/iciv-predictor-sdk.git
|
|
39
|
+
cd iciv-predictor-sdk
|
|
40
|
+
pip install -e .
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## 快速开始
|
|
44
|
+
|
|
45
|
+
### 1. 创建预测器
|
|
46
|
+
|
|
47
|
+
```python
|
|
48
|
+
from iciv_predictor import BasePredictor, PredictionOutput
|
|
49
|
+
import numpy as np
|
|
50
|
+
|
|
51
|
+
class MyPredictor(BasePredictor):
|
|
52
|
+
def __init__(self, df, predict_steps=48):
|
|
53
|
+
super().__init__(df, predict_steps)
|
|
54
|
+
# 初始化你的参数
|
|
55
|
+
self.short_ma = 12
|
|
56
|
+
self.long_ma = 48
|
|
57
|
+
|
|
58
|
+
def predict_step(self, current_idx):
|
|
59
|
+
"""
|
|
60
|
+
预测当前时间点
|
|
61
|
+
|
|
62
|
+
Args:
|
|
63
|
+
current_idx: 当前索引,只能使用历史数据 df[:current_idx+1]
|
|
64
|
+
|
|
65
|
+
Returns:
|
|
66
|
+
PredictionOutput 或 None
|
|
67
|
+
"""
|
|
68
|
+
if current_idx < self.long_ma:
|
|
69
|
+
return None
|
|
70
|
+
|
|
71
|
+
# 获取历史收盘价
|
|
72
|
+
closes = self.get_close_prices(current_idx)
|
|
73
|
+
|
|
74
|
+
# 计算均线
|
|
75
|
+
short_avg = closes[-self.short_ma:].mean()
|
|
76
|
+
long_avg = closes[-self.long_ma:].mean()
|
|
77
|
+
|
|
78
|
+
# 判断方向
|
|
79
|
+
if short_avg > long_avg:
|
|
80
|
+
direction = 1 # 看涨,买入
|
|
81
|
+
elif short_avg < long_avg:
|
|
82
|
+
direction = -1 # 看跌,卖出
|
|
83
|
+
else:
|
|
84
|
+
direction = 0 # 持有
|
|
85
|
+
|
|
86
|
+
return PredictionOutput(
|
|
87
|
+
predict_from_idx=current_idx,
|
|
88
|
+
predicted_prices=np.zeros(self.predict_steps),
|
|
89
|
+
predicted_returns=np.zeros(self.predict_steps),
|
|
90
|
+
confidence=0.6,
|
|
91
|
+
direction=direction
|
|
92
|
+
)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 2. 本地测试
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
iciv-test my_predictor.py --data 000021_SZ.csv
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
或使用Python:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from iciv_predictor import Backtester
|
|
105
|
+
import pandas as pd
|
|
106
|
+
|
|
107
|
+
# 加载数据
|
|
108
|
+
df = pd.read_csv('000021_SZ.csv')
|
|
109
|
+
df['trade_time'] = pd.to_datetime(df['datetime'])
|
|
110
|
+
|
|
111
|
+
# 创建预测器
|
|
112
|
+
predictor = MyPredictor(df)
|
|
113
|
+
|
|
114
|
+
# 运行回测
|
|
115
|
+
backtester = Backtester()
|
|
116
|
+
result = backtester.run(df, predictor)
|
|
117
|
+
|
|
118
|
+
print(f"收益率: {result.total_return:.2f}%")
|
|
119
|
+
print(f"最大回撤: {result.max_drawdown:.2f}%")
|
|
120
|
+
print(f"交易次数: {result.total_trades}")
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 3. 提交到平台
|
|
124
|
+
|
|
125
|
+
测试通过后,将你的 `.py` 文件提交到 https://stock.w3drop.com
|
|
126
|
+
|
|
127
|
+
## API 参考
|
|
128
|
+
|
|
129
|
+
### BasePredictor
|
|
130
|
+
|
|
131
|
+
基类,所有预测器必须继承此类。
|
|
132
|
+
|
|
133
|
+
**方法**:
|
|
134
|
+
|
|
135
|
+
| 方法 | 说明 |
|
|
136
|
+
|------|------|
|
|
137
|
+
| `__init__(df, predict_steps=48)` | 初始化,predict_steps为预测步长 |
|
|
138
|
+
| `predict_step(current_idx)` | **必须实现**,返回PredictionOutput |
|
|
139
|
+
| `get_historical_data(idx)` | 获取历史DataFrame |
|
|
140
|
+
| `get_close_prices(idx)` | 获取历史收盘价数组 |
|
|
141
|
+
| `get_returns(idx)` | 获取历史收益率数组 |
|
|
142
|
+
|
|
143
|
+
### PredictionOutput
|
|
144
|
+
|
|
145
|
+
预测输出结构。
|
|
146
|
+
|
|
147
|
+
**字段**:
|
|
148
|
+
|
|
149
|
+
| 字段 | 类型 | 说明 |
|
|
150
|
+
|------|------|------|
|
|
151
|
+
| `predict_from_idx` | int | 预测起始索引 |
|
|
152
|
+
| `predicted_prices` | np.ndarray | 预测价格序列 |
|
|
153
|
+
| `predicted_returns` | np.ndarray | 预测收益率序列 |
|
|
154
|
+
| `confidence` | float | 置信度 (0-1) |
|
|
155
|
+
| `direction` | int | **交易信号**: 1=买入, -1=卖出, 0=持有 |
|
|
156
|
+
|
|
157
|
+
## 回测规则
|
|
158
|
+
|
|
159
|
+
1. **预测频率**: 每48步(约4小时)预测一次
|
|
160
|
+
2. **测试集**: 使用后20%数据
|
|
161
|
+
3. **交易逻辑**:
|
|
162
|
+
- direction=1 且无持仓 → 买入
|
|
163
|
+
- direction=-1 且有持仓 → 卖出
|
|
164
|
+
4. **手续费**: 买卖各万分之三
|
|
165
|
+
5. **强制平仓**: 测试期结束时平仓
|
|
166
|
+
|
|
167
|
+
## 注意事项
|
|
168
|
+
|
|
169
|
+
⚠️ **禁止使用未来数据**!`predict_step(idx)` 只能访问 `idx` 及之前的数据。
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
# ✅ 正确
|
|
173
|
+
closes = self.get_close_prices(current_idx)
|
|
174
|
+
|
|
175
|
+
# ❌ 错误 - 访问了未来数据
|
|
176
|
+
future = self.df['close'].iloc[current_idx + 10]
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
iciv_predictor/__init__.py
|
|
4
|
+
iciv_predictor/backtest.py
|
|
5
|
+
iciv_predictor/cli.py
|
|
6
|
+
iciv_predictor/predictor.py
|
|
7
|
+
iciv_predictor.egg-info/PKG-INFO
|
|
8
|
+
iciv_predictor.egg-info/SOURCES.txt
|
|
9
|
+
iciv_predictor.egg-info/dependency_links.txt
|
|
10
|
+
iciv_predictor.egg-info/entry_points.txt
|
|
11
|
+
iciv_predictor.egg-info/requires.txt
|
|
12
|
+
iciv_predictor.egg-info/top_level.txt
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
iciv_predictor
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61.0", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "iciv-predictor"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "ICIV股票预测竞赛SDK - 用于开发和测试交易策略"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = {text = "MIT"}
|
|
11
|
+
authors = [
|
|
12
|
+
{name = "ICIV Lab", email = "iciv@example.com"}
|
|
13
|
+
]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"License :: OSI Approved :: MIT License",
|
|
18
|
+
"Programming Language :: Python :: 3",
|
|
19
|
+
"Programming Language :: Python :: 3.9",
|
|
20
|
+
"Programming Language :: Python :: 3.10",
|
|
21
|
+
"Programming Language :: Python :: 3.11",
|
|
22
|
+
"Programming Language :: Python :: 3.12",
|
|
23
|
+
]
|
|
24
|
+
requires-python = ">=3.9"
|
|
25
|
+
dependencies = [
|
|
26
|
+
"pandas>=1.5.0",
|
|
27
|
+
"numpy>=1.21.0",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.optional-dependencies]
|
|
31
|
+
dev = [
|
|
32
|
+
"pytest>=7.0.0",
|
|
33
|
+
"pytest-cov>=4.0.0",
|
|
34
|
+
]
|
|
35
|
+
|
|
36
|
+
[project.urls]
|
|
37
|
+
Homepage = "https://stock.w3drop.com"
|
|
38
|
+
Documentation = "https://stock.w3drop.com/docs"
|
|
39
|
+
|
|
40
|
+
[project.scripts]
|
|
41
|
+
iciv-test = "iciv_predictor.cli:main"
|
|
42
|
+
|
|
43
|
+
[tool.setuptools.packages.find]
|
|
44
|
+
where = ["."]
|
|
45
|
+
include = ["iciv_predictor*"]
|