openfund-maker 1.2.13__py3-none-any.whl → 1.3.1__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.
- maker/MACDOrderBot.py +26 -20
- maker/ThreeLineOrderBot.py +53 -28
- maker/main.py +52 -6
- {openfund_maker-1.2.13.dist-info → openfund_maker-1.3.1.dist-info}/METADATA +3 -2
- openfund_maker-1.3.1.dist-info/RECORD +14 -0
- openfund_maker-1.2.13.dist-info/RECORD +0 -14
- {openfund_maker-1.2.13.dist-info → openfund_maker-1.3.1.dist-info}/WHEEL +0 -0
- {openfund_maker-1.2.13.dist-info → openfund_maker-1.3.1.dist-info}/entry_points.txt +0 -0
maker/MACDOrderBot.py
CHANGED
@@ -15,7 +15,7 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
15
15
|
def __init__(self, config, platform_config, feishu_webhook=None,logger=None):
|
16
16
|
super().__init__(config, platform_config, feishu_webhook, logger)
|
17
17
|
|
18
|
-
def judge_order_side(self, symbol, pair_config,
|
18
|
+
def judge_order_side(self, symbol, pair_config, kLines) -> str:
|
19
19
|
order_side = 'none'
|
20
20
|
|
21
21
|
'''
|
@@ -31,8 +31,15 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
31
31
|
last_down_crosses = []
|
32
32
|
other_crosses = []
|
33
33
|
all_cross = []
|
34
|
-
|
35
|
-
|
34
|
+
|
35
|
+
macd = pd.DataFrame(kLines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
36
|
+
# 将时间戳转换为日期时间格式
|
37
|
+
macd['timestamp'] = pd.to_datetime(macd['timestamp'], unit='ms').dt.strftime('%m-%d %H:%M')
|
38
|
+
|
39
|
+
# 使用 TA-Lib 计算 MACD
|
40
|
+
macd[['macd', 'signal', 'hist']] = pd.DataFrame(ta.MACD(macd['close'], fastperiod=12, slowperiod=26, signalperiod=9)).T
|
41
|
+
|
42
|
+
# 从最新K(排除最后一根K线可能没走完)开始往前遍历
|
36
43
|
for i in range(len(macd)-1, 2, -1):
|
37
44
|
# 检查是否发生死叉(MACD从上方穿过Signal)
|
38
45
|
if (macd.iloc[i-1]['macd'] <= macd.iloc[i-1]['signal'] and
|
@@ -116,7 +123,9 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
116
123
|
# 检查是否有持仓,有持仓不进行下单
|
117
124
|
if self.check_position(symbol=symbol) :
|
118
125
|
self.logger.info(f"{symbol} 有持仓合约,不进行下单。")
|
126
|
+
self.logger.info("-" * 60)
|
119
127
|
return
|
128
|
+
|
120
129
|
self.cancel_all_orders(symbol=symbol)
|
121
130
|
klines_period = str(pair_config.get('klines_period', '1m'))
|
122
131
|
|
@@ -124,20 +133,17 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
124
133
|
kLines = self.get_historical_klines(symbol=symbol, bar=klines_period)
|
125
134
|
self.logger.debug(f"开始监控 {symbol} : klines {klines_period} - {len(kLines)}")
|
126
135
|
|
127
|
-
|
128
|
-
#
|
129
|
-
|
130
|
-
|
131
|
-
# 使用 TA-Lib 计算 MACD
|
132
|
-
df[['macd', 'signal', 'hist']] = pd.DataFrame(ta.MACD(df['close'], fastperiod=12, slowperiod=26, signalperiod=9)).T
|
133
|
-
self.logger.debug(f"{symbol} : MACD Values = \n {df.tail(5)}")
|
134
|
-
side = self.judge_order_side(symbol, pair_config , df)
|
136
|
+
|
137
|
+
# self.logger.debug(f"{symbol} : MACD Values = \n {df.tail(5)}")
|
138
|
+
side = self.judge_order_side(symbol, pair_config , kLines)
|
135
139
|
|
136
140
|
long_amount_usdt = pair_config.get('long_amount_usdt', 5)
|
137
141
|
short_amount_usdt = pair_config.get('short_amount_usdt', 5)
|
138
142
|
order_amount_usdt = 5
|
139
143
|
order_type='optimal_limit_ioc'
|
140
|
-
|
144
|
+
|
145
|
+
# order_price = df['close'].iloc[-1]
|
146
|
+
order_price = float(kLines[-1][4])
|
141
147
|
if side == 'none' :
|
142
148
|
self.logger.debug(f"{symbol} : 没有触发下单条件。")
|
143
149
|
return
|
@@ -164,13 +170,13 @@ class MACDOrdergBot(ThreeLineOrdergBot):
|
|
164
170
|
def monitor_klines(self):
|
165
171
|
symbols = list(self.trading_pairs_config.keys()) # 获取所有币对的ID
|
166
172
|
batch_size = 5 # 每批处理的数量
|
167
|
-
while True:
|
173
|
+
# while True:
|
168
174
|
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
+
for i in range(0, len(symbols), batch_size):
|
176
|
+
batch = symbols[i:i + batch_size]
|
177
|
+
with ThreadPoolExecutor(max_workers=batch_size) as executor:
|
178
|
+
futures = [executor.submit(self.process_pair, symbol,self.trading_pairs_config[symbol]) for symbol in batch]
|
179
|
+
for future in as_completed(futures):
|
180
|
+
future.result() # Raise any exceptions caught during execution
|
175
181
|
|
176
|
-
time.sleep(self.monitor_interval)
|
182
|
+
# time.sleep(self.monitor_interval)
|
maker/ThreeLineOrderBot.py
CHANGED
@@ -397,9 +397,9 @@ class ThreeLineOrdergBot:
|
|
397
397
|
except Exception as e:
|
398
398
|
self.logger.error(f"{symbol} Error setting leverage: {e}")
|
399
399
|
#
|
400
|
-
def check_position(self,symbol) -> bool:
|
400
|
+
def check_position(self, symbol) -> bool:
|
401
401
|
"""
|
402
|
-
|
402
|
+
检查指定交易对是否有持仓,失败时最多重试3次
|
403
403
|
|
404
404
|
Args:
|
405
405
|
symbol: 交易对ID
|
@@ -407,15 +407,23 @@ class ThreeLineOrdergBot:
|
|
407
407
|
Returns:
|
408
408
|
bool: 是否有持仓
|
409
409
|
"""
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
418
|
-
|
410
|
+
max_retries = 3
|
411
|
+
retry_count = 0
|
412
|
+
|
413
|
+
while retry_count < max_retries:
|
414
|
+
try:
|
415
|
+
position = self.exchange.fetch_position(symbol=symbol)
|
416
|
+
if position and position['contracts'] > 0:
|
417
|
+
self.logger.debug(f"{symbol} 有持仓合约数: {position['contracts']}")
|
418
|
+
return True
|
419
|
+
return False
|
420
|
+
except Exception as e:
|
421
|
+
retry_count += 1
|
422
|
+
if retry_count == max_retries:
|
423
|
+
self.logger.warning(f"{symbol} 检查持仓失败(重试{retry_count}次): {str(e)}")
|
424
|
+
return True
|
425
|
+
self.logger.warning(f"{symbol} 检查持仓失败,正在进行第{retry_count}次重试: {str(e)}")
|
426
|
+
time.sleep(0.1) # 重试前等待0.1秒
|
419
427
|
|
420
428
|
def place_order(self,symbol, price, amount_usdt, side,order_type='limit'):
|
421
429
|
|
@@ -478,27 +486,42 @@ class ThreeLineOrdergBot:
|
|
478
486
|
self.logger.error(f"{symbol} Failed to place order: {e}")
|
479
487
|
self.logger.info(f"--------- ++ {symbol} Order placed done! --------")
|
480
488
|
|
481
|
-
def cancel_all_orders(self,symbol):
|
482
|
-
|
483
|
-
|
484
|
-
params = {
|
485
|
-
# 'instId': instId
|
486
|
-
}
|
487
|
-
open_orders = self.exchange.fetch_open_orders(symbol=symbol,params=params)
|
488
|
-
|
489
|
-
# 取消每个订单
|
490
|
-
for order in open_orders:
|
491
|
-
self.exchange.cancel_order(order['id'], symbol,params=params)
|
492
|
-
|
493
|
-
self.logger.info(f"{symbol} 挂单取消成功.")
|
494
|
-
except Exception as e:
|
495
|
-
self.logger.error(f"{symbol} 取消订单失败: {str(e)}")
|
489
|
+
def cancel_all_orders(self, symbol):
|
490
|
+
max_retries = 3
|
491
|
+
retry_count = 0
|
496
492
|
|
493
|
+
while retry_count < max_retries:
|
494
|
+
try:
|
495
|
+
# 获取所有未完成订单
|
496
|
+
params = {
|
497
|
+
# 'instId': instId
|
498
|
+
}
|
499
|
+
open_orders = self.exchange.fetch_open_orders(symbol=symbol, params=params)
|
500
|
+
|
501
|
+
# 批量取消所有订单
|
502
|
+
if open_orders:
|
503
|
+
order_ids = [order['id'] for order in open_orders]
|
504
|
+
self.exchange.cancel_orders(order_ids, symbol, params=params)
|
505
|
+
|
506
|
+
self.logger.info(f"{symbol}: {order_ids} 挂单取消成功.")
|
507
|
+
else:
|
508
|
+
self.logger.info(f"{symbol}: 没有未完成订单.")
|
509
|
+
return True
|
510
|
+
|
511
|
+
except Exception as e:
|
512
|
+
retry_count += 1
|
513
|
+
if retry_count == max_retries:
|
514
|
+
self.logger.warning(f"{symbol} 取消订单失败(重试{retry_count}次): {str(e)}")
|
515
|
+
return False
|
516
|
+
self.logger.warning(f"{symbol} 取消订单失败,正在进行第{retry_count}次重试: {str(e)}")
|
517
|
+
time.sleep(0.1) # 重试前等待0.1秒
|
518
|
+
|
497
519
|
def process_pair(self,symbol,pair_config):
|
498
520
|
self.logger.info("=" * 60)
|
499
521
|
# 检查是否有持仓,有持仓不进行下单
|
500
522
|
if self.check_position(symbol=symbol) :
|
501
523
|
self.logger.info(f"{symbol} 有持仓合约,不进行下单。")
|
524
|
+
self.logger.info("-" * 60)
|
502
525
|
return
|
503
526
|
# 取消之前的挂单
|
504
527
|
self.cancel_all_orders(symbol=symbol)
|
@@ -635,7 +658,9 @@ class ThreeLineOrdergBot:
|
|
635
658
|
self.logger.error(error_message,exc_info=True)
|
636
659
|
traceback.print_exc()
|
637
660
|
self.send_feishu_notification(error_message)
|
638
|
-
|
661
|
+
|
662
|
+
self.logger.info("-" * 60)
|
663
|
+
|
639
664
|
def monitor_klines(self):
|
640
665
|
symbols = list(self.trading_pairs_config.keys()) # 获取所有币对的ID
|
641
666
|
batch_size = 5 # 每批处理的数量
|
@@ -648,4 +673,4 @@ class ThreeLineOrdergBot:
|
|
648
673
|
for future in as_completed(futures):
|
649
674
|
future.result() # Raise any exceptions caught during execution
|
650
675
|
|
651
|
-
time.sleep(self.monitor_interval)
|
676
|
+
# time.sleep(self.monitor_interval)
|
maker/main.py
CHANGED
@@ -1,10 +1,14 @@
|
|
1
1
|
import logging
|
2
2
|
import yaml
|
3
3
|
from logging.handlers import TimedRotatingFileHandler
|
4
|
+
from apscheduler.triggers.interval import IntervalTrigger
|
5
|
+
from apscheduler.schedulers.blocking import BlockingScheduler
|
6
|
+
from datetime import datetime
|
4
7
|
|
5
8
|
from maker.WickReversalOrderBot import WickReversalOrderBot
|
6
9
|
from maker.ThreeLineOrderBot import ThreeLineOrdergBot
|
7
10
|
from maker.MACDOrderBot import MACDOrdergBot
|
11
|
+
|
8
12
|
def build_logger(log_config) -> logging.Logger:
|
9
13
|
# 配置日志
|
10
14
|
# log_file = "log/okx_MultiAssetNewTradingBot.log"
|
@@ -36,6 +40,13 @@ def read_config_file(file_path):
|
|
36
40
|
except yaml.YAMLError as e:
|
37
41
|
raise Exception(f"解析 {file_path} 文件时出错: {e}")
|
38
42
|
|
43
|
+
def run_bot(bot, logger):
|
44
|
+
try:
|
45
|
+
|
46
|
+
bot.monitor_klines()
|
47
|
+
except Exception as e:
|
48
|
+
logger.error(f"执行任务时发生错误: {str(e)}", exc_info=True)
|
49
|
+
|
39
50
|
def main():
|
40
51
|
import importlib.metadata
|
41
52
|
version = importlib.metadata.version("openfund-maker")
|
@@ -45,15 +56,50 @@ def main():
|
|
45
56
|
|
46
57
|
platform_config = config_data['okx']
|
47
58
|
feishu_webhook_url = config_data['feishu_webhook']
|
48
|
-
# monitor_interval = config_data.get("monitor_interval", 4) # 默认值为60秒
|
49
59
|
logger = build_logger(config_data["Logger"])
|
50
|
-
# 获取当前包名
|
51
60
|
package_name = __package__ or "maker"
|
52
61
|
logger.info(f" ++ {package_name}:{version} is doing...")
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
62
|
+
logger.info("开始执行交易任务...")
|
63
|
+
bot = MACDOrdergBot(config_data, platform_config, feishu_webhook=feishu_webhook_url, logger=logger)
|
64
|
+
|
65
|
+
# 获取计划配置
|
66
|
+
schedule_config = config_data.get('schedule', {})
|
67
|
+
if schedule_config.get('enabled', False):
|
68
|
+
scheduler = BlockingScheduler()
|
69
|
+
|
70
|
+
# 设置每5分钟执行一次的任务,从整点开始
|
71
|
+
monitor_interval = int(schedule_config.get('monitor_interval', 4))
|
72
|
+
|
73
|
+
# 计算下一个整点分钟
|
74
|
+
now = datetime.now()
|
75
|
+
# 将当前时间的秒和微秒设置为0
|
76
|
+
next_run = now.replace(second=59, microsecond=0)
|
77
|
+
# 计算下一个周期的开始时间
|
78
|
+
current_minute = next_run.minute
|
79
|
+
# 向上取整到下一个周期时间点
|
80
|
+
next_interval = ((current_minute // monitor_interval) + 1) * monitor_interval -1
|
81
|
+
# 如果下一个周期时间点超过60分钟,需要调整为下一个小时的对应分钟数
|
82
|
+
if next_interval >= 60:
|
83
|
+
next_interval = next_interval % 60
|
84
|
+
next_run = next_run.replace(hour=next_run.hour + 1)
|
85
|
+
next_run = next_run.replace(minute=next_interval)
|
86
|
+
|
87
|
+
scheduler.add_job(
|
88
|
+
run_bot,
|
89
|
+
IntervalTrigger(minutes=monitor_interval),
|
90
|
+
args=[bot, logger],
|
91
|
+
next_run_time=next_run # 从下一个周期整点开始
|
92
|
+
)
|
93
|
+
|
94
|
+
try:
|
95
|
+
logger.info(f"启动定时任务调度器,从 {next_run} 开始每5分钟执行一次...")
|
96
|
+
scheduler.start()
|
97
|
+
except (KeyboardInterrupt, SystemExit):
|
98
|
+
logger.info("程序收到中断信号,正在退出...")
|
99
|
+
scheduler.shutdown()
|
100
|
+
else:
|
101
|
+
# 如果未启用计划,直接运行
|
102
|
+
run_bot(bot, logger)
|
57
103
|
|
58
104
|
if __name__ == "__main__":
|
59
105
|
main()
|
@@ -1,6 +1,6 @@
|
|
1
1
|
Metadata-Version: 2.3
|
2
2
|
Name: openfund-maker
|
3
|
-
Version: 1.
|
3
|
+
Version: 1.3.1
|
4
4
|
Summary: Openfund-maker.
|
5
5
|
Requires-Python: >=3.9,<4.0
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
@@ -9,7 +9,8 @@ Classifier: Programming Language :: Python :: 3.10
|
|
9
9
|
Classifier: Programming Language :: Python :: 3.11
|
10
10
|
Classifier: Programming Language :: Python :: 3.12
|
11
11
|
Classifier: Programming Language :: Python :: 3.13
|
12
|
-
Requires-Dist: TA-Lib (>=0.
|
12
|
+
Requires-Dist: TA-Lib (>=0.5.1,<0.6.0)
|
13
|
+
Requires-Dist: apscheduler (>=3.11.0,<4.0.0)
|
13
14
|
Requires-Dist: ccxt (>=4.4.26,<5.0.0)
|
14
15
|
Requires-Dist: pandas (>=2.2.3,<3.0.0)
|
15
16
|
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
maker/MACDOrderBot.py,sha256=BrdcAUJl4-Rh8Wy36femauzs5oBMvrrAt1W0F6SV8L0,9002
|
2
|
+
maker/ThreeLineOrderBot.py,sha256=4uPrsmP_vl_iwMNuDUY9XTR4ETcMpfsckZR1xh_ktRI,30272
|
3
|
+
maker/WickReversalOrderBot.py,sha256=Oc6wChdWu39lfWh3NRHM8BqvaRIYDNZiDR6PDnE9XUM,17374
|
4
|
+
maker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
+
maker/config.py,sha256=YPxghO5i0vgRg9Cja8kGj9O7pgSbbtzOgf3RexqXXwY,1188
|
6
|
+
maker/main.py,sha256=LCM008ndGiG2G1UIGKRqgY9cq4tqJ5iaq6Pd6sMBAXg,4127
|
7
|
+
maker/main_m.py,sha256=0PzDTnuBrxfpy5WDfsIHKAzZ_7pkuvuqqeWik0vpWio,15522
|
8
|
+
maker/okxapi.py,sha256=_9G0U_o0ZC8NxaT6PqpiLgxBm9gPobC9PsFHZE1c5w0,553
|
9
|
+
maker/zhen.py.bak,sha256=HNkrQbJts8G9umE9chEFsc0cLQApcM9KOVNMYPpkBXM,10918
|
10
|
+
maker/zhen_2.py,sha256=4IaHVtTCMSlrLGSTZrWpW2q-f7HZsUNRkW_-5QgWv24,10509
|
11
|
+
openfund_maker-1.3.1.dist-info/METADATA,sha256=XG6SO5qglW1hY-26oIBlsqMHcuCws7HbHhwDyVpOQg8,1953
|
12
|
+
openfund_maker-1.3.1.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
13
|
+
openfund_maker-1.3.1.dist-info/entry_points.txt,sha256=gKMytICEKcMRFQDFkHZLnIpID7UQFoTIM_xcpiiV6Ns,50
|
14
|
+
openfund_maker-1.3.1.dist-info/RECORD,,
|
@@ -1,14 +0,0 @@
|
|
1
|
-
maker/MACDOrderBot.py,sha256=XA-eZBMJ3zYFrpafMiH8YDmT_IKNQMDVr0pq4xMPRiQ,8962
|
2
|
-
maker/ThreeLineOrderBot.py,sha256=I-Y-bMUfBKUB00rNF1UtTc08T5IND1LEBVjHUzG89vU,29064
|
3
|
-
maker/WickReversalOrderBot.py,sha256=Oc6wChdWu39lfWh3NRHM8BqvaRIYDNZiDR6PDnE9XUM,17374
|
4
|
-
maker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
5
|
-
maker/config.py,sha256=YPxghO5i0vgRg9Cja8kGj9O7pgSbbtzOgf3RexqXXwY,1188
|
6
|
-
maker/main.py,sha256=VP1u7vYeUng8Vakqr1dK1Wi2Z9Hl8k3Peb1YXZ-3lNU,2440
|
7
|
-
maker/main_m.py,sha256=0PzDTnuBrxfpy5WDfsIHKAzZ_7pkuvuqqeWik0vpWio,15522
|
8
|
-
maker/okxapi.py,sha256=_9G0U_o0ZC8NxaT6PqpiLgxBm9gPobC9PsFHZE1c5w0,553
|
9
|
-
maker/zhen.py.bak,sha256=HNkrQbJts8G9umE9chEFsc0cLQApcM9KOVNMYPpkBXM,10918
|
10
|
-
maker/zhen_2.py,sha256=4IaHVtTCMSlrLGSTZrWpW2q-f7HZsUNRkW_-5QgWv24,10509
|
11
|
-
openfund_maker-1.2.13.dist-info/METADATA,sha256=_BlNAqUL_f5S0mm6vDZegkfW-jlapz-uNgD76PJLECU,1909
|
12
|
-
openfund_maker-1.2.13.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
13
|
-
openfund_maker-1.2.13.dist-info/entry_points.txt,sha256=gKMytICEKcMRFQDFkHZLnIpID7UQFoTIM_xcpiiV6Ns,50
|
14
|
-
openfund_maker-1.2.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|