cyqnt-trd 0.1.2__py3-none-any.whl
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.
- cyqnt_trd/__init__.py +26 -0
- cyqnt_trd/backtesting/README.md +264 -0
- cyqnt_trd/backtesting/__init__.py +12 -0
- cyqnt_trd/backtesting/factor_test.py +332 -0
- cyqnt_trd/backtesting/framework.py +311 -0
- cyqnt_trd/backtesting/strategy_backtest.py +545 -0
- cyqnt_trd/diagnose_api.py +28 -0
- cyqnt_trd/get_data/__init__.py +15 -0
- cyqnt_trd/get_data/get_futures_data.py +472 -0
- cyqnt_trd/get_data/get_trending_data.py +771 -0
- cyqnt_trd/online_trading/__init__.py +13 -0
- cyqnt_trd/online_trading/realtime_price_tracker.py +1001 -0
- cyqnt_trd/test.py +119 -0
- cyqnt_trd/test_script/README.md +411 -0
- cyqnt_trd/test_script/get_network_info.py +192 -0
- cyqnt_trd/test_script/get_symbols_by_volume.py +227 -0
- cyqnt_trd/test_script/realtime_price_tracker.py +839 -0
- cyqnt_trd/test_script/test_alpha.py +261 -0
- cyqnt_trd/test_script/test_kline_data.py +479 -0
- cyqnt_trd/test_script/test_order.py +1360 -0
- cyqnt_trd/trading_signal/README.md +276 -0
- cyqnt_trd/trading_signal/__init__.py +17 -0
- cyqnt_trd/trading_signal/example_test_alpha.py +430 -0
- cyqnt_trd/trading_signal/example_usage.py +431 -0
- cyqnt_trd/trading_signal/factor/__init__.py +18 -0
- cyqnt_trd/trading_signal/factor/ma_factor.py +75 -0
- cyqnt_trd/trading_signal/factor/rsi_factor.py +56 -0
- cyqnt_trd/trading_signal/selected_alpha/__init__.py +158 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha1.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha10.py +90 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha100.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha101.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha11.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha12.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha13.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha14.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha15.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha16.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha17.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha18.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha19.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha2.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha20.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha21.py +89 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha22.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha23.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha24.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha25.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha26.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha27.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha28.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha29.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha3.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha30.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha31.py +90 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha32.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha33.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha34.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha35.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha36.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha37.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha38.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha39.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha4.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha40.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha41.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha42.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha43.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha44.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha45.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha46.py +89 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha47.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha48.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha49.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha5.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha50.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha51.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha52.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha53.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha54.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha55.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha56.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha57.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha58.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha59.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha6.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha60.py +89 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha61.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha62.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha63.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha64.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha65.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha66.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha67.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha68.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha69.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha7.py +88 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha70.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha71.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha72.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha73.py +91 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha74.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha75.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha76.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha77.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha78.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha79.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha8.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha80.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha81.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha82.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha83.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha84.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha85.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha86.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha87.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha88.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha89.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha9.py +90 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha90.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha91.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha92.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha93.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha94.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha95.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha96.py +92 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha97.py +74 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha98.py +87 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha99.py +86 -0
- cyqnt_trd/trading_signal/selected_alpha/alpha_utils.py +342 -0
- cyqnt_trd/trading_signal/selected_alpha/create_all_alphas.py +279 -0
- cyqnt_trd/trading_signal/selected_alpha/generate_alphas.py +133 -0
- cyqnt_trd/trading_signal/selected_alpha/test_alpha.py +261 -0
- cyqnt_trd/trading_signal/signal/__init__.py +20 -0
- cyqnt_trd/trading_signal/signal/factor_based_signal.py +387 -0
- cyqnt_trd/trading_signal/signal/ma_signal.py +163 -0
- cyqnt_trd/utils/__init__.py +3 -0
- cyqnt_trd/utils/set_user.py +33 -0
- cyqnt_trd-0.1.2.dist-info/METADATA +148 -0
- cyqnt_trd-0.1.2.dist-info/RECORD +147 -0
- cyqnt_trd-0.1.2.dist-info/WHEEL +5 -0
- cyqnt_trd-0.1.2.dist-info/licenses/LICENSE +21 -0
- cyqnt_trd-0.1.2.dist-info/top_level.txt +2 -0
- test/real_time_trade.py +746 -0
- test/test_example_usage.py +381 -0
- test/test_get_data.py +310 -0
- test/test_realtime_price_tracker.py +546 -0
|
@@ -0,0 +1,387 @@
|
|
|
1
|
+
"""
|
|
2
|
+
基于因子的交易信号策略
|
|
3
|
+
|
|
4
|
+
展示如何在信号策略中使用factor中的因子
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from typing import Callable, Optional
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import numpy as np
|
|
10
|
+
|
|
11
|
+
# 导入因子
|
|
12
|
+
from ..factor.ma_factor import ma_factor, ma_cross_factor
|
|
13
|
+
from ..factor.rsi_factor import rsi_factor
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def factor_based_signal(
|
|
17
|
+
data_slice: 'pd.DataFrame',
|
|
18
|
+
position: float,
|
|
19
|
+
entry_price: float,
|
|
20
|
+
entry_index: int,
|
|
21
|
+
take_profit: float,
|
|
22
|
+
stop_loss: float,
|
|
23
|
+
check_periods: int,
|
|
24
|
+
factor_func: Optional[Callable] = None,
|
|
25
|
+
factor_period: int = 3
|
|
26
|
+
) -> str:
|
|
27
|
+
"""
|
|
28
|
+
基于因子的交易信号策略,带止盈止损
|
|
29
|
+
|
|
30
|
+
使用指定的因子函数生成交易信号
|
|
31
|
+
当因子值从负转正时买入,从正转负时卖出
|
|
32
|
+
|
|
33
|
+
Args:
|
|
34
|
+
data_slice: 数据切片,必须包含足够的历史数据用于计算因子
|
|
35
|
+
以及未来check_periods个周期用于检查止盈止损
|
|
36
|
+
最后一行是当前数据点,前面是历史数据,后面是未来数据(如果有)
|
|
37
|
+
position: 当前持仓数量(如果没有持仓则为0)
|
|
38
|
+
entry_price: 入场价格(如果没有持仓则为0)
|
|
39
|
+
entry_index: 入场索引(如果没有持仓则为-1,保留用于兼容性)
|
|
40
|
+
take_profit: 止盈比例(例如:0.1 表示 10%)
|
|
41
|
+
stop_loss: 止损比例(例如:0.1 表示 10%)
|
|
42
|
+
check_periods: 检查未来多少个周期(用于止盈止损检查)
|
|
43
|
+
factor_func: 因子函数,接受数据切片作为参数(如果为None,默认使用ma_factor)
|
|
44
|
+
factor_period: 因子计算周期(用于某些因子)
|
|
45
|
+
|
|
46
|
+
Returns:
|
|
47
|
+
'buy', 'sell' 或 'hold'
|
|
48
|
+
"""
|
|
49
|
+
# 默认使用ma_factor
|
|
50
|
+
if factor_func is None:
|
|
51
|
+
factor_func = lambda d: ma_factor(d, period=factor_period)
|
|
52
|
+
# 当使用默认factor_func时,使用factor_period
|
|
53
|
+
min_required = factor_period
|
|
54
|
+
else:
|
|
55
|
+
# 当传入自定义factor_func时,使用保守估计值(30)
|
|
56
|
+
# 这样可以确保有足够的数据用于大多数因子计算(如MA5需要6行,RSI14需要15行,alpha1需要25+行)
|
|
57
|
+
min_required = 30
|
|
58
|
+
|
|
59
|
+
# 需要至少min_required+1行数据来计算当前因子,以及额外一行来计算上一周期的因子
|
|
60
|
+
if len(data_slice) < min_required + 2:
|
|
61
|
+
return 'hold'
|
|
62
|
+
|
|
63
|
+
try:
|
|
64
|
+
# 当前数据切片:最后一行是当前数据点,前面是历史数据
|
|
65
|
+
# 使用足够的数据切片(min_required+1行)以确保因子函数有足够的数据
|
|
66
|
+
current_slice = data_slice.iloc[-(min_required+1):]
|
|
67
|
+
current_factor = factor_func(current_slice)
|
|
68
|
+
|
|
69
|
+
# 如果因子返回0且数据足够,可能是数据不足导致的
|
|
70
|
+
if current_factor == 0 and len(data_slice) > min_required + 1:
|
|
71
|
+
# 不直接返回hold,继续尝试计算上一周期的因子
|
|
72
|
+
pass
|
|
73
|
+
except Exception:
|
|
74
|
+
return 'hold'
|
|
75
|
+
|
|
76
|
+
# 计算上一周期的因子值
|
|
77
|
+
prev_factor = 0.0
|
|
78
|
+
if len(data_slice) > min_required + 2:
|
|
79
|
+
try:
|
|
80
|
+
# 上一周期的数据切片:倒数第二行是上一周期的数据点
|
|
81
|
+
# 使用足够的数据切片(min_required+1行)
|
|
82
|
+
prev_slice = data_slice.iloc[-(min_required+2):-1]
|
|
83
|
+
prev_factor = factor_func(prev_slice)
|
|
84
|
+
except Exception:
|
|
85
|
+
prev_factor = 0.0
|
|
86
|
+
|
|
87
|
+
# 如果有持仓,先检查止盈止损
|
|
88
|
+
if position > 0 and entry_price > 0:
|
|
89
|
+
# 计算止盈和止损价格
|
|
90
|
+
take_profit_price = entry_price * (1 + take_profit) if take_profit is not None else None
|
|
91
|
+
stop_loss_price = entry_price * (1 - stop_loss) if stop_loss is not None else None
|
|
92
|
+
|
|
93
|
+
# 检查当前周期和未来 check_periods 个周期
|
|
94
|
+
# 当前周期是最后一行,未来周期在data_slice中(如果有)
|
|
95
|
+
# 注意:如果check_periods > 1,data_slice应该包含未来数据
|
|
96
|
+
check_end = min(check_periods, len(data_slice))
|
|
97
|
+
for i in range(check_end):
|
|
98
|
+
# 从最后一行开始,向前检查(如果data_slice包含未来数据)
|
|
99
|
+
# 或者只检查当前周期(最后一行)
|
|
100
|
+
if i == 0:
|
|
101
|
+
# 当前周期(最后一行)
|
|
102
|
+
check_low = data_slice.iloc[-1]['low_price']
|
|
103
|
+
check_high = data_slice.iloc[-1]['high_price']
|
|
104
|
+
else:
|
|
105
|
+
# 未来周期(如果data_slice包含未来数据,应该从最后一行之后开始)
|
|
106
|
+
# 但根据设计,data_slice应该只包含历史+当前,不包含未来
|
|
107
|
+
# 所以这里只检查当前周期
|
|
108
|
+
continue
|
|
109
|
+
|
|
110
|
+
# 优先检查止损
|
|
111
|
+
if stop_loss_price is not None and check_low <= stop_loss_price:
|
|
112
|
+
return 'sell' # 触发止损
|
|
113
|
+
|
|
114
|
+
# 检查止盈
|
|
115
|
+
if take_profit_price is not None and check_high >= take_profit_price:
|
|
116
|
+
return 'sell' # 触发止盈
|
|
117
|
+
|
|
118
|
+
# 如果没有触发止盈止损,检查策略信号
|
|
119
|
+
# 因子从正转负:卖出
|
|
120
|
+
if prev_factor > 0 and current_factor < 0:
|
|
121
|
+
return 'sell'
|
|
122
|
+
else:
|
|
123
|
+
return 'hold'
|
|
124
|
+
|
|
125
|
+
# 如果没有持仓,检查买入信号
|
|
126
|
+
else:
|
|
127
|
+
# 因子从负转正:买入
|
|
128
|
+
if prev_factor < 0 and current_factor > 0:
|
|
129
|
+
return 'buy'
|
|
130
|
+
else:
|
|
131
|
+
return 'hold'
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def multi_factor_signal(
|
|
135
|
+
data_slice: 'pd.DataFrame',
|
|
136
|
+
position: float,
|
|
137
|
+
entry_price: float,
|
|
138
|
+
entry_index: int,
|
|
139
|
+
take_profit: float,
|
|
140
|
+
stop_loss: float,
|
|
141
|
+
check_periods: int,
|
|
142
|
+
factor_funcs: list = None,
|
|
143
|
+
weights: list = None
|
|
144
|
+
) -> str:
|
|
145
|
+
"""
|
|
146
|
+
多因子组合交易信号策略,带止盈止损
|
|
147
|
+
|
|
148
|
+
组合多个因子,加权求和后生成交易信号
|
|
149
|
+
|
|
150
|
+
Args:
|
|
151
|
+
data_slice: 数据切片,必须包含足够的历史数据用于计算因子
|
|
152
|
+
以及未来check_periods个周期用于检查止盈止损
|
|
153
|
+
最后一行是当前数据点,前面是历史数据,后面是未来数据(如果有)
|
|
154
|
+
position: 当前持仓数量(如果没有持仓则为0)
|
|
155
|
+
entry_price: 入场价格(如果没有持仓则为0)
|
|
156
|
+
entry_index: 入场索引(如果没有持仓则为-1,保留用于兼容性)
|
|
157
|
+
take_profit: 止盈比例(例如:0.1 表示 10%)
|
|
158
|
+
stop_loss: 止损比例(例如:0.1 表示 10%)
|
|
159
|
+
check_periods: 检查未来多少个周期(用于止盈止损检查)
|
|
160
|
+
factor_funcs: 因子函数列表,每个函数接受数据切片作为参数
|
|
161
|
+
(如果为None,默认使用[ma_factor, rsi_factor])
|
|
162
|
+
weights: 因子权重列表(如果为None,默认等权重)
|
|
163
|
+
|
|
164
|
+
Returns:
|
|
165
|
+
'buy', 'sell' 或 'hold'
|
|
166
|
+
"""
|
|
167
|
+
# 默认使用ma_factor和rsi_factor
|
|
168
|
+
if factor_funcs is None:
|
|
169
|
+
factor_funcs = [
|
|
170
|
+
lambda d: ma_factor(d, period=5),
|
|
171
|
+
lambda d: rsi_factor(d, period=14)
|
|
172
|
+
]
|
|
173
|
+
|
|
174
|
+
# 默认等权重
|
|
175
|
+
if weights is None:
|
|
176
|
+
weights = [1.0 / len(factor_funcs)] * len(factor_funcs)
|
|
177
|
+
|
|
178
|
+
# 确保权重和因子数量一致
|
|
179
|
+
if len(weights) != len(factor_funcs):
|
|
180
|
+
weights = [1.0 / len(factor_funcs)] * len(factor_funcs)
|
|
181
|
+
|
|
182
|
+
# 需要至少max_period+1行数据来计算因子
|
|
183
|
+
# 假设最长的因子周期是20(ma_cross_factor的long_period)
|
|
184
|
+
max_period = 20
|
|
185
|
+
if len(data_slice) < max_period + 1:
|
|
186
|
+
return 'hold'
|
|
187
|
+
|
|
188
|
+
# 计算加权因子值
|
|
189
|
+
try:
|
|
190
|
+
# 当前数据切片:最后一行是当前数据点,前面是历史数据
|
|
191
|
+
current_slice = data_slice.iloc[-(max_period+1):]
|
|
192
|
+
combined_factor = 0.0
|
|
193
|
+
for factor_func, weight in zip(factor_funcs, weights):
|
|
194
|
+
factor_value = factor_func(current_slice)
|
|
195
|
+
if factor_value is not None and not (isinstance(factor_value, float) and factor_value == 0 and len(data_slice) == 1):
|
|
196
|
+
combined_factor += factor_value * weight
|
|
197
|
+
except Exception:
|
|
198
|
+
return 'hold'
|
|
199
|
+
|
|
200
|
+
# 计算上一周期的组合因子值
|
|
201
|
+
prev_combined_factor = 0.0
|
|
202
|
+
if len(data_slice) > max_period + 1:
|
|
203
|
+
try:
|
|
204
|
+
# 上一周期的数据切片:倒数第二行是上一周期的数据点
|
|
205
|
+
prev_slice = data_slice.iloc[-(max_period+2):-1]
|
|
206
|
+
for factor_func, weight in zip(factor_funcs, weights):
|
|
207
|
+
factor_value = factor_func(prev_slice)
|
|
208
|
+
if factor_value is not None:
|
|
209
|
+
prev_combined_factor += factor_value * weight
|
|
210
|
+
except Exception:
|
|
211
|
+
prev_combined_factor = 0.0
|
|
212
|
+
|
|
213
|
+
# 如果有持仓,先检查止盈止损
|
|
214
|
+
if position > 0 and entry_price > 0:
|
|
215
|
+
# 计算止盈和止损价格
|
|
216
|
+
take_profit_price = entry_price * (1 + take_profit) if take_profit is not None else None
|
|
217
|
+
stop_loss_price = entry_price * (1 - stop_loss) if stop_loss is not None else None
|
|
218
|
+
|
|
219
|
+
# 检查当前周期(最后一行)
|
|
220
|
+
check_low = data_slice.iloc[-1]['low_price']
|
|
221
|
+
check_high = data_slice.iloc[-1]['high_price']
|
|
222
|
+
|
|
223
|
+
# 优先检查止损
|
|
224
|
+
if stop_loss_price is not None and check_low <= stop_loss_price:
|
|
225
|
+
return 'sell' # 触发止损
|
|
226
|
+
|
|
227
|
+
# 检查止盈
|
|
228
|
+
if take_profit_price is not None and check_high >= take_profit_price:
|
|
229
|
+
return 'sell' # 触发止盈
|
|
230
|
+
|
|
231
|
+
# 如果没有触发止盈止损,检查策略信号
|
|
232
|
+
# 组合因子从正转负:卖出
|
|
233
|
+
if prev_combined_factor > 0 and combined_factor < 0:
|
|
234
|
+
return 'sell'
|
|
235
|
+
else:
|
|
236
|
+
return 'hold'
|
|
237
|
+
|
|
238
|
+
# 如果没有持仓,检查买入信号
|
|
239
|
+
else:
|
|
240
|
+
# 组合因子从负转正:买入
|
|
241
|
+
if prev_combined_factor < 0 and combined_factor > 0:
|
|
242
|
+
return 'buy'
|
|
243
|
+
else:
|
|
244
|
+
return 'hold'
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
import concurrent.futures
|
|
248
|
+
|
|
249
|
+
def normalized_factor_signal(
|
|
250
|
+
data_slice: 'pd.DataFrame',
|
|
251
|
+
position: float,
|
|
252
|
+
entry_price: float,
|
|
253
|
+
entry_index: int,
|
|
254
|
+
take_profit: float,
|
|
255
|
+
stop_loss: float,
|
|
256
|
+
check_periods: int,
|
|
257
|
+
factor_func: Optional[Callable] = None,
|
|
258
|
+
factor_period: int = 3,
|
|
259
|
+
lookback_periods: int = 30
|
|
260
|
+
) -> str:
|
|
261
|
+
"""
|
|
262
|
+
基于归一化因子的交易信号策略,带止盈止损
|
|
263
|
+
|
|
264
|
+
计算当前周期和之前lookback_periods个周期的因子值,进行归一化后生成交易信号
|
|
265
|
+
当归一化后的因子值从负转正时买入,从正转负时卖出
|
|
266
|
+
|
|
267
|
+
Args:
|
|
268
|
+
data_slice: 数据切片,必须包含足够的历史数据用于计算因子
|
|
269
|
+
以及未来check_periods个周期用于检查止盈止损
|
|
270
|
+
最后一行是当前数据点,前面是历史数据,后面是未来数据(如果有)
|
|
271
|
+
position: 当前持仓数量(如果没有持仓则为0)
|
|
272
|
+
entry_price: 入场价格(如果没有持仓则为0)
|
|
273
|
+
entry_index: 入场索引(如果没有持仓则为-1,保留用于兼容性)
|
|
274
|
+
take_profit: 止盈比例(例如:0.1 表示 10%)
|
|
275
|
+
stop_loss: 止损比例(例如:0.1 表示 10%)
|
|
276
|
+
check_periods: 检查未来多少个周期(用于止盈止损检查)
|
|
277
|
+
factor_func: 因子函数,接受数据切片作为参数(如果为None,默认使用ma_factor)
|
|
278
|
+
factor_period: 因子计算周期(用于某些因子)
|
|
279
|
+
lookback_periods: 回看周期数,计算当前周期和之前多少个周期的因子值(默认30)
|
|
280
|
+
|
|
281
|
+
Returns:
|
|
282
|
+
'buy', 'sell' 或 'hold'
|
|
283
|
+
"""
|
|
284
|
+
# 默认使用ma_factor
|
|
285
|
+
if factor_func is None:
|
|
286
|
+
factor_func = lambda d: ma_factor(d, period=factor_period)
|
|
287
|
+
# 当使用默认factor_func时,使用factor_period
|
|
288
|
+
min_required = factor_period
|
|
289
|
+
else:
|
|
290
|
+
# 当传入自定义factor_func时,使用保守估计值(30)
|
|
291
|
+
# 这样可以确保有足够的数据用于大多数因子计算
|
|
292
|
+
min_required = 30
|
|
293
|
+
|
|
294
|
+
# 需要至少min_required+lookback_periods+1行数据
|
|
295
|
+
# min_required用于计算因子,lookback_periods用于回看,+1用于当前周期
|
|
296
|
+
# 如果数据不足,自适应调整回看周期
|
|
297
|
+
available_periods = len(data_slice) - min_required - 1
|
|
298
|
+
if available_periods < 2:
|
|
299
|
+
return 'hold' # 至少需要2个周期才能计算prev_factor和current_factor
|
|
300
|
+
|
|
301
|
+
# 自适应调整回看周期,但不能小于2
|
|
302
|
+
actual_lookback = min(lookback_periods, max(2, available_periods))
|
|
303
|
+
|
|
304
|
+
try:
|
|
305
|
+
# 利用多线程并行计算因子值
|
|
306
|
+
def compute_factor_value(i):
|
|
307
|
+
end_idx = len(data_slice) - i
|
|
308
|
+
start_idx = max(0, end_idx - min_required - 1)
|
|
309
|
+
if end_idx <= start_idx:
|
|
310
|
+
return 0.0
|
|
311
|
+
|
|
312
|
+
period_slice = data_slice.iloc[start_idx:end_idx]
|
|
313
|
+
try:
|
|
314
|
+
factor_value = factor_func(period_slice)
|
|
315
|
+
if factor_value is not None:
|
|
316
|
+
return factor_value
|
|
317
|
+
else:
|
|
318
|
+
return 0.0
|
|
319
|
+
except Exception:
|
|
320
|
+
return 0.0
|
|
321
|
+
|
|
322
|
+
indices = list(range(actual_lookback + 1))
|
|
323
|
+
factor_values = []
|
|
324
|
+
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
325
|
+
# 返回顺序保证和原for循环一致
|
|
326
|
+
factor_values = list(executor.map(compute_factor_value, indices))
|
|
327
|
+
|
|
328
|
+
# 如果收集到的因子值不足,返回hold
|
|
329
|
+
if len(factor_values) < 2:
|
|
330
|
+
return 'hold'
|
|
331
|
+
|
|
332
|
+
# 将因子值转换为numpy数组进行归一化
|
|
333
|
+
factor_array = np.array(factor_values)
|
|
334
|
+
|
|
335
|
+
# Min-Max归一化:将值映射到[-1, 1]区间
|
|
336
|
+
# 如果所有值都相同,则归一化后都为0
|
|
337
|
+
factor_min = factor_array.min()
|
|
338
|
+
factor_max = factor_array.max()
|
|
339
|
+
|
|
340
|
+
if factor_max == factor_min:
|
|
341
|
+
# 所有值相同,归一化后都为0
|
|
342
|
+
normalized_factors = np.zeros_like(factor_array)
|
|
343
|
+
else:
|
|
344
|
+
# Min-Max归一化到[-1, 1]区间
|
|
345
|
+
normalized_factors = 2 * (factor_array - factor_min) / (factor_max - factor_min) - 1
|
|
346
|
+
|
|
347
|
+
# 当前归一化因子值(第一个,即当前周期)
|
|
348
|
+
current_factor = normalized_factors[0]
|
|
349
|
+
# 上一周期的归一化因子值(第二个,即前1个周期)
|
|
350
|
+
prev_factor = normalized_factors[1] if len(normalized_factors) > 1 else 0.0
|
|
351
|
+
|
|
352
|
+
except Exception:
|
|
353
|
+
return 'hold'
|
|
354
|
+
|
|
355
|
+
# 如果有持仓,先检查止盈止损
|
|
356
|
+
if position > 0 and entry_price > 0:
|
|
357
|
+
# 计算止盈和止损价格
|
|
358
|
+
take_profit_price = entry_price * (1 + take_profit) if take_profit is not None else None
|
|
359
|
+
stop_loss_price = entry_price * (1 - stop_loss) if stop_loss is not None else None
|
|
360
|
+
|
|
361
|
+
# 检查当前周期(最后一行)
|
|
362
|
+
check_low = data_slice.iloc[-1]['low_price']
|
|
363
|
+
check_high = data_slice.iloc[-1]['high_price']
|
|
364
|
+
|
|
365
|
+
# 优先检查止损
|
|
366
|
+
if stop_loss_price is not None and check_low <= stop_loss_price:
|
|
367
|
+
return 'sell' # 触发止损
|
|
368
|
+
|
|
369
|
+
# 检查止盈
|
|
370
|
+
if take_profit_price is not None and check_high >= take_profit_price:
|
|
371
|
+
return 'sell' # 触发止盈
|
|
372
|
+
|
|
373
|
+
# 如果没有触发止盈止损,检查策略信号
|
|
374
|
+
# 归一化因子从正转负:卖出
|
|
375
|
+
if prev_factor > 0 and current_factor < 0:
|
|
376
|
+
return 'sell'
|
|
377
|
+
else:
|
|
378
|
+
return 'hold'
|
|
379
|
+
|
|
380
|
+
# 如果没有持仓,检查买入信号
|
|
381
|
+
else:
|
|
382
|
+
# 归一化因子从负转正:买入
|
|
383
|
+
if prev_factor < 0 and current_factor > 0:
|
|
384
|
+
return 'buy'
|
|
385
|
+
else:
|
|
386
|
+
return 'hold'
|
|
387
|
+
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""
|
|
2
|
+
基于移动平均线的交易信号策略
|
|
3
|
+
|
|
4
|
+
包含基于移动平均线的各种交易策略
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import pandas as pd
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def ma_signal(
|
|
11
|
+
data_slice: 'pd.DataFrame',
|
|
12
|
+
position: float,
|
|
13
|
+
entry_price: float,
|
|
14
|
+
entry_index: int,
|
|
15
|
+
take_profit: float,
|
|
16
|
+
stop_loss: float,
|
|
17
|
+
period: int = 5
|
|
18
|
+
) -> str:
|
|
19
|
+
"""
|
|
20
|
+
基于移动平均线的交易信号策略,带止盈止损
|
|
21
|
+
|
|
22
|
+
当价格上穿MA时买入,下穿MA时卖出
|
|
23
|
+
同时检查止盈止损条件
|
|
24
|
+
|
|
25
|
+
注意:只使用当前周期的数据生成信号,不使用未来数据(无预知功能)
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
data_slice: 数据切片,必须包含至少 period+1 行数据
|
|
29
|
+
最后一行是当前数据点,前面 period+1 行是历史数据
|
|
30
|
+
position: 当前持仓数量(如果没有持仓则为0)
|
|
31
|
+
entry_price: 入场价格(如果没有持仓则为0)
|
|
32
|
+
entry_index: 入场索引(如果没有持仓则为-1,保留用于兼容性)
|
|
33
|
+
take_profit: 止盈比例(例如:0.1 表示 10%)
|
|
34
|
+
stop_loss: 止损比例(例如:0.1 表示 10%)
|
|
35
|
+
period: 移动平均周期(默认5)
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
'buy', 'sell' 或 'hold'
|
|
39
|
+
"""
|
|
40
|
+
if len(data_slice) < period + 1:
|
|
41
|
+
return 'hold'
|
|
42
|
+
|
|
43
|
+
# 最后一行是当前数据点
|
|
44
|
+
current_price = data_slice.iloc[-1]['close_price']
|
|
45
|
+
# 前period行是历史数据(不包括最后一行)
|
|
46
|
+
ma = data_slice.iloc[-period-1:-1]['close_price'].mean()
|
|
47
|
+
# 倒数第二行是上一周期的价格
|
|
48
|
+
prev_price = data_slice.iloc[-2]['close_price']
|
|
49
|
+
# 上一周期的MA(使用倒数第二行之前的数据)
|
|
50
|
+
prev_ma = data_slice.iloc[-period-2:-2]['close_price'].mean()
|
|
51
|
+
|
|
52
|
+
# 如果有持仓,先检查止盈止损
|
|
53
|
+
if position > 0 and entry_price > 0:
|
|
54
|
+
# 计算止盈和止损价格
|
|
55
|
+
take_profit_price = entry_price * (1 + take_profit) if take_profit is not None else None
|
|
56
|
+
stop_loss_price = entry_price * (1 - stop_loss) if stop_loss is not None else None
|
|
57
|
+
|
|
58
|
+
# 只检查当前周期的数据,不检查未来数据
|
|
59
|
+
current_low = data_slice.iloc[-1]['low_price']
|
|
60
|
+
current_high = data_slice.iloc[-1]['high_price']
|
|
61
|
+
|
|
62
|
+
# 优先检查止损
|
|
63
|
+
if stop_loss_price is not None and current_low <= stop_loss_price:
|
|
64
|
+
return 'sell' # 触发止损
|
|
65
|
+
|
|
66
|
+
# 检查止盈
|
|
67
|
+
if take_profit_price is not None and current_high >= take_profit_price:
|
|
68
|
+
return 'sell' # 触发止盈
|
|
69
|
+
|
|
70
|
+
# 如果没有触发止盈止损,检查策略信号
|
|
71
|
+
# 下穿:之前价格在MA上方,现在在MA下方
|
|
72
|
+
if prev_price >= prev_ma and current_price < ma:
|
|
73
|
+
return 'sell'
|
|
74
|
+
else:
|
|
75
|
+
return 'hold'
|
|
76
|
+
|
|
77
|
+
# 如果没有持仓,检查买入信号
|
|
78
|
+
else:
|
|
79
|
+
# 上穿:之前价格在MA下方,现在在MA上方
|
|
80
|
+
if prev_price <= prev_ma and current_price > ma:
|
|
81
|
+
return 'buy'
|
|
82
|
+
else:
|
|
83
|
+
return 'hold'
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def ma_cross_signal(
|
|
87
|
+
data_slice: 'pd.DataFrame',
|
|
88
|
+
position: float,
|
|
89
|
+
entry_price: float,
|
|
90
|
+
entry_index: int,
|
|
91
|
+
take_profit: float,
|
|
92
|
+
stop_loss: float,
|
|
93
|
+
check_periods: int,
|
|
94
|
+
short_period: int = 5,
|
|
95
|
+
long_period: int = 20
|
|
96
|
+
) -> str:
|
|
97
|
+
"""
|
|
98
|
+
基于移动平均线交叉的交易信号策略,带止盈止损
|
|
99
|
+
|
|
100
|
+
当短期均线上穿长期均线时买入,下穿时卖出
|
|
101
|
+
同时检查止盈止损条件
|
|
102
|
+
|
|
103
|
+
注意:只使用当前周期的数据生成信号,不使用未来数据(无预知功能)
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
data_slice: 数据切片,必须包含至少 long_period+2 行数据
|
|
107
|
+
最后一行是当前数据点,前面 long_period+1 行是历史数据
|
|
108
|
+
position: 当前持仓数量(如果没有持仓则为0)
|
|
109
|
+
entry_price: 入场价格(如果没有持仓则为0)
|
|
110
|
+
entry_index: 入场索引(如果没有持仓则为-1,保留用于兼容性)
|
|
111
|
+
take_profit: 止盈比例(例如:0.1 表示 10%)
|
|
112
|
+
stop_loss: 止损比例(例如:0.1 表示 10%)
|
|
113
|
+
check_periods: 已弃用,不再使用(保留以保持向后兼容性)
|
|
114
|
+
short_period: 短期均线周期(默认5)
|
|
115
|
+
long_period: 长期均线周期(默认20)
|
|
116
|
+
|
|
117
|
+
Returns:
|
|
118
|
+
'buy', 'sell' 或 'hold'
|
|
119
|
+
"""
|
|
120
|
+
if len(data_slice) < long_period + 2:
|
|
121
|
+
return 'hold'
|
|
122
|
+
|
|
123
|
+
# 计算当前周期的均线(使用最后period行数据,不包括最后一行)
|
|
124
|
+
short_ma_current = data_slice.iloc[-short_period-1:-1]['close_price'].mean()
|
|
125
|
+
long_ma_current = data_slice.iloc[-long_period-1:-1]['close_price'].mean()
|
|
126
|
+
|
|
127
|
+
# 计算上一周期的均线(使用倒数第二行之前的数据)
|
|
128
|
+
short_ma_prev = data_slice.iloc[-short_period-2:-2]['close_price'].mean()
|
|
129
|
+
long_ma_prev = data_slice.iloc[-long_period-2:-2]['close_price'].mean()
|
|
130
|
+
|
|
131
|
+
# 如果有持仓,先检查止盈止损
|
|
132
|
+
if position > 0 and entry_price > 0:
|
|
133
|
+
# 计算止盈和止损价格
|
|
134
|
+
take_profit_price = entry_price * (1 + take_profit) if take_profit is not None else None
|
|
135
|
+
stop_loss_price = entry_price * (1 - stop_loss) if stop_loss is not None else None
|
|
136
|
+
|
|
137
|
+
# 只检查当前周期的数据,不检查未来数据
|
|
138
|
+
current_low = data_slice.iloc[-1]['low_price']
|
|
139
|
+
current_high = data_slice.iloc[-1]['high_price']
|
|
140
|
+
|
|
141
|
+
# 优先检查止损
|
|
142
|
+
if stop_loss_price is not None and current_low <= stop_loss_price:
|
|
143
|
+
return 'sell' # 触发止损
|
|
144
|
+
|
|
145
|
+
# 检查止盈
|
|
146
|
+
if take_profit_price is not None and current_high >= take_profit_price:
|
|
147
|
+
return 'sell' # 触发止盈
|
|
148
|
+
|
|
149
|
+
# 如果没有触发止盈止损,检查策略信号
|
|
150
|
+
# 下穿:之前短期均线在长期均线上方,现在在下方
|
|
151
|
+
if short_ma_prev >= long_ma_prev and short_ma_current < long_ma_current:
|
|
152
|
+
return 'sell'
|
|
153
|
+
else:
|
|
154
|
+
return 'hold'
|
|
155
|
+
|
|
156
|
+
# 如果没有持仓,检查买入信号
|
|
157
|
+
else:
|
|
158
|
+
# 上穿:之前短期均线在长期均线下方,现在在上方
|
|
159
|
+
if short_ma_prev <= long_ma_prev and short_ma_current > long_ma_current:
|
|
160
|
+
return 'buy'
|
|
161
|
+
else:
|
|
162
|
+
return 'hold'
|
|
163
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from binance_sdk_derivatives_trading_usds_futures.derivatives_trading_usds_futures import (
|
|
3
|
+
ConfigurationRestAPI,
|
|
4
|
+
DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL,
|
|
5
|
+
)
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def set_user(
|
|
9
|
+
api_key: str = "",
|
|
10
|
+
api_secret: str = "",
|
|
11
|
+
base_path: str = DERIVATIVES_TRADING_USDS_FUTURES_REST_API_PROD_URL
|
|
12
|
+
) -> ConfigurationRestAPI:
|
|
13
|
+
"""
|
|
14
|
+
设置 API 配置,将 API_KEY、API_SECRET 和 BASE_PATH 写入环境变量,并返回配置对象
|
|
15
|
+
|
|
16
|
+
Args:
|
|
17
|
+
api_key (str, optional): API Key. 默认值为内置测试Key。
|
|
18
|
+
api_secret (str, optional): API Secret. 默认值为内置测试Secret。
|
|
19
|
+
base_path (str, optional): API Base路径。默认生产环境。
|
|
20
|
+
|
|
21
|
+
Returns:
|
|
22
|
+
ConfigurationRestAPI: Binance REST API 配置对象
|
|
23
|
+
"""
|
|
24
|
+
os.environ["API_KEY"] = api_key
|
|
25
|
+
os.environ["API_SECRET"] = api_secret
|
|
26
|
+
os.environ["BASE_PATH"] = base_path
|
|
27
|
+
|
|
28
|
+
configuration_rest_api = ConfigurationRestAPI(
|
|
29
|
+
api_key=os.getenv("API_KEY"),
|
|
30
|
+
api_secret=os.getenv("API_SECRET"),
|
|
31
|
+
base_path=os.getenv("BASE_PATH"),
|
|
32
|
+
)
|
|
33
|
+
return configuration_rest_api
|