openfund-maker 1.3.2__tar.gz → 1.3.4__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.
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/PKG-INFO +1 -1
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/pyproject.toml +1 -1
- openfund_maker-1.3.2/src/maker/MACDOrderBot.py → openfund_maker-1.3.4/src/maker/MACDStrategyMaker.py +80 -37
- openfund_maker-1.3.2/src/maker/ThreeLineOrderBot.py → openfund_maker-1.3.4/src/maker/ThreeLineStrategyMaker.py +22 -20
- openfund_maker-1.3.2/src/maker/WickReversalOrderBot.py → openfund_maker-1.3.4/src/maker/WickReversalStrategyMaker.py +5 -5
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/src/maker/main.py +6 -6
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/README.md +0 -0
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/src/maker/__init__.py +0 -0
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/src/maker/config.py +0 -0
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/src/maker/main_m.py +0 -0
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/src/maker/okxapi.py +0 -0
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/src/maker/zhen.py.bak +0 -0
- {openfund_maker-1.3.2 → openfund_maker-1.3.4}/src/maker/zhen_2.py +0 -0
openfund_maker-1.3.2/src/maker/MACDOrderBot.py → openfund_maker-1.3.4/src/maker/MACDStrategyMaker.py
RENAMED
@@ -1,44 +1,28 @@
|
|
1
1
|
# -*- coding: utf-8 -*-
|
2
|
-
import time
|
3
|
-
import ccxt
|
4
2
|
import traceback
|
5
|
-
import requests
|
6
3
|
import pandas as pd
|
7
4
|
import talib as ta
|
8
5
|
|
9
|
-
from logging.handlers import TimedRotatingFileHandler
|
10
6
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
11
|
-
from maker.
|
7
|
+
from maker.ThreeLineStrategyMaker import ThreeLineStrategyMaker
|
12
8
|
|
13
9
|
|
14
|
-
class
|
10
|
+
class MACDStrategyMaker(ThreeLineStrategyMaker):
|
15
11
|
def __init__(self, config, platform_config, feishu_webhook=None,logger=None):
|
16
12
|
super().__init__(config, platform_config, feishu_webhook, logger)
|
17
13
|
|
18
|
-
def
|
19
|
-
order_side = 'none'
|
20
|
-
|
21
|
-
'''
|
22
|
-
零轴之上的macd与signal形成金叉
|
23
|
-
零轴之下的死叉
|
24
|
-
零轴之上的死叉-金叉-死叉
|
25
|
-
零轴之下的金叉-死叉-金叉
|
26
|
-
'''
|
27
|
-
|
28
|
-
|
14
|
+
def get_macd_cross_direction(self, symbol, kLines, strategy=None) -> dict:
|
29
15
|
# 计算最近三个交叉点
|
30
16
|
last_up_crosses = []
|
31
17
|
last_down_crosses = []
|
32
18
|
other_crosses = []
|
33
19
|
all_cross = []
|
34
|
-
|
35
20
|
macd = pd.DataFrame(kLines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
36
21
|
# 将时间戳转换为日期时间格式
|
37
22
|
macd['timestamp'] = pd.to_datetime(macd['timestamp'], unit='ms').dt.strftime('%m-%d %H:%M')
|
38
23
|
|
39
24
|
# 使用 TA-Lib 计算 MACD
|
40
25
|
macd[['macd', 'signal', 'hist']] = pd.DataFrame(ta.MACD(macd['close'], fastperiod=12, slowperiod=26, signalperiod=9)).T
|
41
|
-
|
42
26
|
# 从最新K(排除最后一根K线可能没走完)开始往前遍历
|
43
27
|
for i in range(len(macd)-1, 2, -1):
|
44
28
|
# 检查是否发生死叉(MACD从上方穿过Signal)
|
@@ -70,14 +54,48 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
70
54
|
break
|
71
55
|
|
72
56
|
self.logger.debug(f"{symbol} : \n- 所有cross {all_cross} \n- 零轴之上cross {last_up_crosses} \n- 零轴之下cross {last_down_crosses} \n- 其他corss {other_crosses}。")
|
73
|
-
|
57
|
+
|
58
|
+
cross_direction = {
|
59
|
+
"all_cross": all_cross,
|
60
|
+
"last_up_crosses": last_up_crosses,
|
61
|
+
"last_down_crosses": last_down_crosses,
|
62
|
+
"other_crosses": other_crosses,
|
63
|
+
}
|
64
|
+
|
65
|
+
return cross_direction
|
66
|
+
|
67
|
+
|
68
|
+
def judge_order_side(self, symbol, kLines, valid_klines=5 ,strategy=None) -> str:
|
69
|
+
|
70
|
+
'''
|
71
|
+
零轴之上的macd与signal形成金叉
|
72
|
+
零轴之下的死叉
|
73
|
+
零轴之上的死叉-金叉-死叉
|
74
|
+
零轴之下的金叉-死叉-金叉
|
75
|
+
'''
|
76
|
+
|
77
|
+
order_side = 'none'
|
78
|
+
crosses = self.get_macd_cross_direction(symbol, kLines, strategy)
|
79
|
+
|
80
|
+
last_up_crosses = crosses.get('last_up_crosses',[])
|
81
|
+
last_down_crosses = crosses.get('last_down_crosses',[])
|
82
|
+
other_crosses = crosses.get('other_crosses',[])
|
83
|
+
all_cross = crosses.get('all_cross',[])
|
84
|
+
|
85
|
+
# valid_klines = strategy.get('valid_klines', 5)
|
74
86
|
# 如果最新的交叉是金叉,且又是零轴上方的金叉
|
75
|
-
if len(last_up_crosses) > 0 and
|
87
|
+
if (len(last_up_crosses) > 0 and
|
88
|
+
all_cross[0][0] == 'golden' and
|
89
|
+
all_cross[0][1] == last_up_crosses[0][1] and
|
90
|
+
len(kLines) - all_cross[0][1] <= valid_klines):
|
76
91
|
order_side = 'buy'
|
77
92
|
self.logger.debug(f"{symbol} : 零轴之上的macd与signal形成金叉{all_cross[0]} 。")
|
78
93
|
|
79
94
|
# 如果最新的交叉是死叉,且又是零轴下方的死叉
|
80
|
-
elif len(last_down_crosses) > 0 and
|
95
|
+
elif (len(last_down_crosses) > 0 and
|
96
|
+
all_cross[0][0] == 'death' and
|
97
|
+
all_cross[0][1] == last_down_crosses[0][1] and
|
98
|
+
len(kLines) - all_cross[0][1] <= valid_klines):
|
81
99
|
order_side ='sell'
|
82
100
|
self.logger.debug(f"{symbol} : 零轴之下的macd与signal形成死叉{all_cross[0]} 。")
|
83
101
|
# 分析交叉点模式,要满足连续的三个交叉都是零上
|
@@ -87,7 +105,7 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
87
105
|
if (last_up_crosses[0][0] == 'death' and
|
88
106
|
last_up_crosses[1][0] == 'golden' and
|
89
107
|
last_up_crosses[2][0] == 'death' and
|
90
|
-
len(
|
108
|
+
len(kLines) - last_up_crosses[0][1] <= valid_klines
|
91
109
|
):
|
92
110
|
order_side = 'sell'
|
93
111
|
self.logger.debug(f"{symbol} : 零轴之上的死叉-金叉-死叉模式 {order_side}。")
|
@@ -97,25 +115,43 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
97
115
|
if (last_down_crosses[0][0] == 'golden' and
|
98
116
|
last_down_crosses[1][0] == 'death' and
|
99
117
|
last_down_crosses[2][0] == 'golden' and
|
100
|
-
len(
|
118
|
+
len(kLines) - last_down_crosses[0][1] <= valid_klines
|
101
119
|
):
|
102
120
|
order_side = 'buy'
|
103
121
|
self.logger.debug(f"{symbol} : 零轴之下的金叉-死叉-金叉模式 {order_side}。")
|
104
|
-
|
122
|
+
|
105
123
|
|
106
124
|
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
# self.logger.debug(f"{symbol} : 零轴之上的macd与signal形成金叉 {order_side}。")
|
111
|
-
# elif macd.iloc[-1]['macd'] < 0 and macd.iloc[-1]['signal'] < 0 and macd.iloc[-1]['macd'] <= macd.iloc[-1]['signal'] and macd.iloc[-2]['macd'] > macd.iloc[-2]['signal']:
|
112
|
-
# order_side = 'sell'
|
113
|
-
# self.logger.debug(f"{symbol} : 零轴之下的macd与signal形成死叉 {order_side}。")
|
114
|
-
# else:
|
115
|
-
# order_side = 'none'
|
125
|
+
return order_side
|
126
|
+
|
127
|
+
def judge_HTF_side(self,symbol,strategy=None) -> str:
|
116
128
|
|
129
|
+
order_side = 'none'
|
130
|
+
try:
|
131
|
+
htf = str(strategy.get('HTF', '1h'))
|
132
|
+
htf_kLines = self.get_historical_klines(symbol=symbol, bar=htf)
|
133
|
+
|
134
|
+
crosses = self.get_macd_cross_direction(symbol, htf_kLines, strategy)
|
135
|
+
all_cross = crosses.get('all_cross',[])
|
136
|
+
if len(all_cross) > 1:
|
137
|
+
if all_cross[0][0] == 'golden':
|
138
|
+
order_side = 'buy'
|
139
|
+
else:
|
140
|
+
order_side = 'sell'
|
141
|
+
|
142
|
+
self.logger.debug(f"{symbol} : HTF={htf} , {order_side}。")
|
143
|
+
|
144
|
+
else:
|
145
|
+
self.logger.debug(f"{symbol} : HTF={htf} ,没有满足条件的交叉点。")
|
146
|
+
|
147
|
+
except Exception as e:
|
148
|
+
error_message = f"程序异常退出: {str(e)}"
|
149
|
+
self.logger.error(error_message,exc_info=True)
|
150
|
+
traceback.print_exc()
|
151
|
+
self.send_feishu_notification(error_message)
|
117
152
|
|
118
153
|
return order_side
|
154
|
+
|
119
155
|
|
120
156
|
|
121
157
|
def process_pair(self,symbol,pair_config):
|
@@ -127,15 +163,22 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
127
163
|
return
|
128
164
|
|
129
165
|
self.cancel_all_orders(symbol=symbol)
|
130
|
-
|
166
|
+
macd_strategy = pair_config.get('macd_strategy',{})
|
131
167
|
|
132
168
|
try:
|
169
|
+
klines_period = str(pair_config.get('klines_period', '1m'))
|
133
170
|
kLines = self.get_historical_klines(symbol=symbol, bar=klines_period)
|
134
171
|
self.logger.debug(f"开始监控 {symbol} : klines {klines_period} - {len(kLines)}")
|
135
172
|
|
136
|
-
|
173
|
+
valid_klines = pair_config.get('valid_klines', 5)
|
137
174
|
# self.logger.debug(f"{symbol} : MACD Values = \n {df.tail(5)}")
|
138
|
-
side = self.judge_order_side(symbol,
|
175
|
+
side = self.judge_order_side(symbol, kLines,valid_klines, macd_strategy)
|
176
|
+
|
177
|
+
htf_side = self.judge_HTF_side(symbol, macd_strategy)
|
178
|
+
if htf_side != 'none' and side != htf_side:
|
179
|
+
self.logger.debug(f"{symbol} : 下单方向 {side} 与HTF方向 {htf_side} 不一致,不进行下单。")
|
180
|
+
return
|
181
|
+
|
139
182
|
|
140
183
|
long_amount_usdt = pair_config.get('long_amount_usdt', 5)
|
141
184
|
short_amount_usdt = pair_config.get('short_amount_usdt', 5)
|
@@ -5,11 +5,10 @@ import traceback
|
|
5
5
|
import requests
|
6
6
|
import pandas as pd
|
7
7
|
|
8
|
-
from logging.handlers import TimedRotatingFileHandler
|
9
8
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
10
9
|
|
11
10
|
|
12
|
-
class
|
11
|
+
class ThreeLineStrategyMaker:
|
13
12
|
def __init__(self, config, platform_config, feishu_webhook=None,logger=None):
|
14
13
|
|
15
14
|
self.g_config = config
|
@@ -233,7 +232,7 @@ class ThreeLineOrdergBot:
|
|
233
232
|
return float(self.round_price_to_tick(place_order_price,tick_size))
|
234
233
|
|
235
234
|
# 定义根据均线斜率判断 K 线方向的函数: 0 空 1 多 -1 平
|
236
|
-
def judge_k_line_direction(self, symbol,
|
235
|
+
def judge_k_line_direction(self, symbol, ema: pd.Series, klines, period=3) -> int:
|
237
236
|
"""
|
238
237
|
判断K线方向
|
239
238
|
Args:
|
@@ -243,8 +242,7 @@ class ThreeLineOrdergBot:
|
|
243
242
|
Returns:
|
244
243
|
int: -1:平, 0:空, 1:多
|
245
244
|
"""
|
246
|
-
|
247
|
-
period = int(pair_config.get('ema_range_period', 3))
|
245
|
+
|
248
246
|
|
249
247
|
# precision= self.get_precision_length(symbol)
|
250
248
|
|
@@ -263,7 +261,7 @@ class ThreeLineOrdergBot:
|
|
263
261
|
self.logger.debug(f"{symbol}: K线极差={ema_diff.map('{:.9f}'.format).values} ,K线方向={direction}")
|
264
262
|
return direction
|
265
263
|
|
266
|
-
def judge_ema_direction(self, symbol,
|
264
|
+
def judge_ema_direction(self, symbol, ema: pd.Series, period=3) -> int:
|
267
265
|
"""
|
268
266
|
判断EMA方向
|
269
267
|
Args:
|
@@ -273,8 +271,6 @@ class ThreeLineOrdergBot:
|
|
273
271
|
Returns:
|
274
272
|
int: -1:平, 0:空, 1:多
|
275
273
|
"""
|
276
|
-
# 获取配置参数
|
277
|
-
period = int(pair_config.get('ema_range_period', 3))
|
278
274
|
|
279
275
|
precision= self.get_precision_length(symbol)
|
280
276
|
|
@@ -329,8 +325,8 @@ class ThreeLineOrdergBot:
|
|
329
325
|
'index': last_death
|
330
326
|
}
|
331
327
|
|
332
|
-
def judge_ma_apex(self,symbol,
|
333
|
-
|
328
|
+
def judge_ma_apex(self,symbol,cross_index, fastklines,slowklines,period=3) -> bool:
|
329
|
+
|
334
330
|
precision= self.get_precision_length(symbol)
|
335
331
|
|
336
332
|
# 获取交叉点的索引,从交叉点之后进行判断
|
@@ -362,7 +358,7 @@ class ThreeLineOrdergBot:
|
|
362
358
|
|
363
359
|
return is_expanding_or_contracting
|
364
360
|
|
365
|
-
def judge_range_diff(self,symbol,
|
361
|
+
def judge_range_diff(self,symbol,prices:pd.Series,limit = 1,period=3) -> bool:
|
366
362
|
"""
|
367
363
|
计算价格列表中最后一个价格与第一个价格的差值。
|
368
364
|
Args:
|
@@ -371,8 +367,8 @@ class ThreeLineOrdergBot:
|
|
371
367
|
diff: 计算最高价列的最大值与最小值的差值
|
372
368
|
。
|
373
369
|
"""
|
374
|
-
limit = int(pair_config.get('ema_range_limit', 1))
|
375
|
-
period = int(pair_config.get('ema_range_period', 3))
|
370
|
+
# limit = int(pair_config.get('ema_range_limit', 1))
|
371
|
+
# period = int(pair_config.get('ema_range_period', 3))
|
376
372
|
tick_size = self.get_tick_size(symbol)
|
377
373
|
if prices.empty:
|
378
374
|
return None
|
@@ -525,6 +521,7 @@ class ThreeLineOrdergBot:
|
|
525
521
|
return
|
526
522
|
# 取消之前的挂单
|
527
523
|
self.cancel_all_orders(symbol=symbol)
|
524
|
+
three_line_strategy = pair_config.get('three_line_strategy',{})
|
528
525
|
klines_period = str(pair_config.get('klines_period', '1m'))
|
529
526
|
try:
|
530
527
|
klines = self.get_historical_klines(symbol=symbol,bar=klines_period)
|
@@ -552,16 +549,20 @@ class ThreeLineOrdergBot:
|
|
552
549
|
cross_index = self.exchange.safe_dict(last_cross_direction,'index',None)
|
553
550
|
|
554
551
|
# 判断趋势:多头趋势或空头趋势
|
555
|
-
|
552
|
+
ema_range_period = int(three_line_strategy.get('ema_range_period', 3))
|
553
|
+
direction = self.judge_k_line_direction(symbol=symbol,ema=fastk,klines=klines,period=ema_range_period)
|
556
554
|
if direction == 1:
|
557
555
|
is_bullish_trend = True
|
558
556
|
elif direction == 0:
|
559
557
|
is_bearish_trend = True
|
560
558
|
|
561
559
|
# 结合金叉死叉判断是否是周期顶部和底部
|
562
|
-
ema_direction = self.judge_ema_direction(symbol=symbol,
|
563
|
-
is_apex = self.judge_ma_apex(symbol=symbol,
|
564
|
-
|
560
|
+
ema_direction = self.judge_ema_direction(symbol=symbol,ema=fastk,period=ema_range_period)
|
561
|
+
is_apex = self.judge_ma_apex(symbol=symbol,cross_index=cross_index, fastklines=fastk,slowklines=slowk,period=ema_range_period)
|
562
|
+
|
563
|
+
# ema_range_limit = int(pair_config.get('ema_range_limit', 1))
|
564
|
+
# if_inner_range = self.judge_range_diff(symbol=symbol, prices=fastk,limit=ema_range_limit, period=ema_range_period)
|
565
|
+
|
565
566
|
# 金叉死叉逻辑
|
566
567
|
if last_cross_direction and last_cross_direction['cross'] == 1 : # 金叉
|
567
568
|
# 强校验下单条件
|
@@ -612,13 +613,14 @@ class ThreeLineOrdergBot:
|
|
612
613
|
min_low = low_prices.min()
|
613
614
|
|
614
615
|
# 计算当前 振幅是否超过amplitude_limit
|
616
|
+
long_amount_usdt = pair_config.get('long_amount_usdt', 5)
|
617
|
+
short_amount_usdt = pair_config.get('short_amount_usdt', 5)
|
615
618
|
|
616
|
-
|
619
|
+
|
620
|
+
amplitude_limit = three_line_strategy.get('amplitude_limit', 0.32)
|
617
621
|
|
618
622
|
self.logger.debug(f"{symbol} 当前K线的前三根K线 最高价: {max_high}, 最低价: {min_low}")
|
619
623
|
|
620
|
-
long_amount_usdt = pair_config.get('long_amount_usdt', 5)
|
621
|
-
short_amount_usdt = pair_config.get('short_amount_usdt', 5)
|
622
624
|
|
623
625
|
'''
|
624
626
|
挂单线都是三线中最高/低,如果打到下单线说明趋势反转,所以应该挂和反方向的单,
|
@@ -9,7 +9,7 @@ import pandas as pd
|
|
9
9
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
10
10
|
|
11
11
|
|
12
|
-
class
|
12
|
+
class WickReversalStrategyMaker:
|
13
13
|
def __init__(self, config, platform_config, feishu_webhook=None , logger=None):
|
14
14
|
|
15
15
|
self.g_config = config
|
@@ -320,8 +320,8 @@ class WickReversalOrderBot:
|
|
320
320
|
is_bearish_trend = close_prices[-1] < ema # 收盘价在 EMA 之下
|
321
321
|
self.logger.info(f"{symbol} EMA: {ema:.6f}, 当前价格: {close_prices[-1]:.6f}, 多头趋势: {is_bullish_trend}, 空头趋势: {is_bearish_trend}")
|
322
322
|
# 接针挂单逻辑
|
323
|
-
|
324
|
-
use_market_price =
|
323
|
+
wick_reversal_strategy = pair_config.get('wick_reversal_strategy', {})
|
324
|
+
use_market_price = wick_reversal_strategy.get('use_market_price', 1)
|
325
325
|
if use_market_price == 1 :
|
326
326
|
base_price = self.get_mark_price(symbol=symbol) #最新价格
|
327
327
|
else :
|
@@ -336,7 +336,7 @@ class WickReversalOrderBot:
|
|
336
336
|
self.logger.info(f"{symbol} 平均振幅: {average_amplitude:.2f}%")
|
337
337
|
|
338
338
|
# 接针的挂单距离,默认计算逻辑是atr/close 跟 振幅ma的区间求最小值 *系数,如果周期小这样其实大部分时候都是采用的振幅,
|
339
|
-
value_multiplier =
|
339
|
+
value_multiplier = wick_reversal_strategy.get('value_multiplier', 2)
|
340
340
|
'''
|
341
341
|
接针的挂单距离,默认计算逻辑是atr/close 跟 振幅ma的区间求最小值 *系数,如果周期小这样其实大部分时候都是采用的振幅,
|
342
342
|
其实可以多试试其他方案,比如改成atr/close 跟 振幅ma的平均值,这样的话atr权重实际会更大,大部分行情还是atr反应更直接。
|
@@ -344,7 +344,7 @@ class WickReversalOrderBot:
|
|
344
344
|
# selected_value = (average_amplitude + price_atr_ratio)/2 * value_multiplier
|
345
345
|
|
346
346
|
selected_value = min(average_amplitude, price_atr_ratio) * value_multiplier
|
347
|
-
amplitude_limit = float(
|
347
|
+
amplitude_limit = float(wick_reversal_strategy.get('amplitude_limit', 0.8))
|
348
348
|
selected_value = max(selected_value, amplitude_limit)
|
349
349
|
self.logger.info(f"{symbol} selected_value: {selected_value} ")
|
350
350
|
|
@@ -5,13 +5,13 @@ from apscheduler.triggers.interval import IntervalTrigger
|
|
5
5
|
from apscheduler.schedulers.blocking import BlockingScheduler
|
6
6
|
from datetime import datetime
|
7
7
|
|
8
|
-
from maker.
|
9
|
-
from maker.
|
10
|
-
from maker.
|
8
|
+
from maker.WickReversalStrategyMaker import WickReversalStrategyMaker
|
9
|
+
from maker.ThreeLineStrategyMaker import ThreeLineStrategyMaker
|
10
|
+
from maker.MACDStrategyMaker import MACDStrategyMaker
|
11
11
|
|
12
12
|
def build_logger(log_config) -> logging.Logger:
|
13
|
-
|
14
|
-
|
13
|
+
# 配置日志
|
14
|
+
|
15
15
|
log_file = log_config["file"]
|
16
16
|
logger = logging.getLogger(__name__)
|
17
17
|
logger.setLevel(log_config["level"])
|
@@ -60,7 +60,7 @@ def main():
|
|
60
60
|
package_name = __package__ or "maker"
|
61
61
|
logger.info(f" ++ {package_name}:{version} is doing...")
|
62
62
|
logger.info("开始执行交易任务...")
|
63
|
-
bot =
|
63
|
+
bot = MACDStrategyMaker(config_data, platform_config, feishu_webhook=feishu_webhook_url, logger=logger)
|
64
64
|
|
65
65
|
# 获取计划配置
|
66
66
|
schedule_config = config_data.get('schedule', {})
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|