openfund-core 1.0.8__tar.gz → 1.0.10__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_core-1.0.8 → openfund_core-1.0.10}/PKG-INFO +1 -1
- {openfund_core-1.0.8 → openfund_core-1.0.10}/pyproject.toml +1 -1
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/Exchange.py +28 -77
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/smc/SMCLiquidity.py +5 -7
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/smc/SMCPDArray.py +28 -11
- {openfund_core-1.0.8 → openfund_core-1.0.10}/README.md +0 -0
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/__init__.py +0 -0
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/main.py +0 -0
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/smc/SMCBase.py +0 -0
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/smc/SMCFVG.py +0 -0
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/smc/SMCOrderBlock.py +0 -0
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/smc/SMCStruct.py +0 -0
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/smc/__init__.py +0 -0
- {openfund_core-1.0.8 → openfund_core-1.0.10}/src/core/utils/OPTools.py +0 -0
@@ -24,8 +24,6 @@ class Exchange:
|
|
24
24
|
self.exchange = getattr(ccxt, exchangeKey)(config)
|
25
25
|
self.logger = logging.getLogger(__name__)
|
26
26
|
|
27
|
-
|
28
|
-
|
29
27
|
def getMarket(self, symbol:str):
|
30
28
|
# 配置交易对
|
31
29
|
self.exchange.load_markets()
|
@@ -61,9 +59,7 @@ class Exchange:
|
|
61
59
|
|
62
60
|
def amount_to_precision(self,symbol, contract_size):
|
63
61
|
return self.exchange.amount_to_precision(symbol, contract_size)
|
64
|
-
|
65
|
-
|
66
|
-
|
62
|
+
|
67
63
|
def set_leverage(self,symbol, leverage, mgnMode='isolated',posSide=None):
|
68
64
|
try:
|
69
65
|
# 设置杠杆
|
@@ -166,8 +162,7 @@ class Exchange:
|
|
166
162
|
else:
|
167
163
|
self.logger.warning(f"{symbol} 平仓失败,正在进行第{retry_count}次重试: {str(e)}")
|
168
164
|
time.sleep(0.1) # 重试前等待0.1秒
|
169
|
-
|
170
|
-
|
165
|
+
|
171
166
|
def cancel_all_orders(self, symbol):
|
172
167
|
max_retries = 3
|
173
168
|
retry_count = 0
|
@@ -257,6 +252,7 @@ class Exchange:
|
|
257
252
|
|
258
253
|
self.logger.warning(f"{symbol} : Error cancelling order {algo_ids}: {str(e)}")
|
259
254
|
time.sleep(0.1) # 重试前等待0.1秒
|
255
|
+
|
260
256
|
def place_algo_orders(self, symbol, position, price: Decimal, order_type, sl_or_tp='SL', params={}) -> bool:
|
261
257
|
"""
|
262
258
|
下单
|
@@ -323,47 +319,23 @@ class Exchange:
|
|
323
319
|
self.logger.info(f"{symbol} : Pre Algo Order placed: {order} ")
|
324
320
|
while retry_count < max_retries:
|
325
321
|
try:
|
322
|
+
# 创建订单
|
323
|
+
order_result = self.exchange.create_order(**order)
|
324
|
+
self.logger.info(f"{symbol} : --------- ++ Algo Order placed done. --------")
|
325
|
+
return True
|
326
326
|
|
327
|
-
|
328
|
-
**order
|
329
|
-
# symbol=symbol,
|
330
|
-
# type=order_type,
|
331
|
-
# price=adjusted_price,
|
332
|
-
# side=orderSide,
|
333
|
-
# amount=amount,
|
334
|
-
# params=order_params
|
335
|
-
)
|
336
|
-
|
337
|
-
break
|
338
|
-
|
339
|
-
except ccxt.NetworkError as e:
|
340
|
-
# 处理网络相关错误
|
341
|
-
retry_count += 1
|
342
|
-
self.logger.warning(f"{symbol} : 设置止盈止损时发生网络错误,正在进行第{retry_count}次重试: {str(e)}")
|
343
|
-
time.sleep(0.1) # 重试前等待1秒
|
344
|
-
continue
|
345
|
-
except ccxt.ExchangeError as e:
|
346
|
-
# 处理交易所API相关错误
|
327
|
+
except (ccxt.NetworkError, ccxt.ExchangeError, Exception) as e:
|
347
328
|
retry_count += 1
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
329
|
+
error_type = "网络" if isinstance(e, ccxt.NetworkError) else "交易所" if isinstance(e, ccxt.ExchangeError) else "未知"
|
330
|
+
self.logger.warning(f"{symbol} : 设置止盈止损单时发生{error_type}错误,正在进行第{retry_count}次重试: {str(e)}")
|
331
|
+
|
332
|
+
if retry_count == max_retries:
|
333
|
+
error_message = f"!! {symbol}: 设置止盈止损单失败(重试{max_retries}次): {str(e)}"
|
334
|
+
self.logger.error(error_message)
|
335
|
+
raise Exception(error_message)
|
336
|
+
|
355
337
|
time.sleep(0.1)
|
356
|
-
continue
|
357
|
-
|
358
|
-
if retry_count >= max_retries:
|
359
|
-
# 重试次数用完仍未成功设置止损单
|
360
|
-
error_message = f"!! {symbol}: 设置止盈止损单时重试次数用完仍未成功设置成功。 "
|
361
|
-
self.logger.error(error_message)
|
362
|
-
raise Exception(error_message)
|
363
|
-
self.logger.debug(f"{symbol} : --------- ++ Order placed done. --------")
|
364
|
-
return True
|
365
338
|
|
366
|
-
|
367
339
|
def place_order(self, symbol, price: Decimal, amount_usdt, side, leverage=20, order_type='limit', params={}) -> bool:
|
368
340
|
"""
|
369
341
|
下单
|
@@ -418,40 +390,18 @@ class Exchange:
|
|
418
390
|
while retry_count < max_retries:
|
419
391
|
try:
|
420
392
|
# 使用ccxt创建订单
|
421
|
-
order_result = self.exchange.create_order(
|
422
|
-
|
423
|
-
|
424
|
-
|
425
|
-
# side=side,
|
426
|
-
# amount=amount_usdt,
|
427
|
-
# price=float(adjusted_price),
|
428
|
-
# params=params
|
429
|
-
)
|
430
|
-
except ccxt.NetworkError as e:
|
431
|
-
# 处理网络相关错误
|
432
|
-
retry_count += 1
|
433
|
-
self.logger.warning(f"{symbol} : 下单时发生网络错误,正在进行第{retry_count}次重试: {str(e)}")
|
434
|
-
time.sleep(0.1) # 重试前等待1秒
|
435
|
-
continue
|
436
|
-
except ccxt.ExchangeError as e:
|
437
|
-
# 处理交易所API相关错误
|
438
|
-
retry_count += 1
|
439
|
-
self.logger.warning(f"{symbol} : 下单时发生交易所错误,正在进行第{retry_count}次重试: {str(e)}")
|
440
|
-
time.sleep(0.1)
|
441
|
-
continue
|
442
|
-
except Exception as e:
|
443
|
-
# 处理其他未预期的错误
|
393
|
+
order_result = self.exchange.create_order(**order)
|
394
|
+
self.logger.info(f"{symbol} : --------- ++ Order placed done. --------")
|
395
|
+
return True
|
396
|
+
except (ccxt.NetworkError, ccxt.ExchangeError, Exception) as e:
|
444
397
|
retry_count += 1
|
445
|
-
|
446
|
-
|
447
|
-
|
448
|
-
|
449
|
-
|
450
|
-
|
451
|
-
|
452
|
-
raise Exception(error_message)
|
453
|
-
self.logger.debug(f"{symbol} : --------- ++ Order placed done. --------")
|
454
|
-
return True
|
398
|
+
error_type = "网络" if isinstance(e, ccxt.NetworkError) else "交易所" if isinstance(e, ccxt.ExchangeError) else "未知"
|
399
|
+
self.logger.warning(f"{symbol} : 下单时发生{error_type}错误,正在进行第{retry_count}次重试: {str(e)}")
|
400
|
+
if retry_count == max_retries:
|
401
|
+
error_message = f"!! {symbol}: 下单时重试{max_retries}次后仍未成功:{str(e)}"
|
402
|
+
self.logger.error(error_message)
|
403
|
+
raise Exception(error_message)
|
404
|
+
time.sleep(1)
|
455
405
|
|
456
406
|
def fetch_position(self, symbol):
|
457
407
|
"""_summary_
|
@@ -523,6 +473,7 @@ class Exchange:
|
|
523
473
|
|
524
474
|
self.logger.warning(f"{symbol} : Error fetching open orders: {str(e)}")
|
525
475
|
time.sleep(0.1) # 重试前等待0.1秒
|
476
|
+
|
526
477
|
def get_market_price(self, symbol) -> Decimal:
|
527
478
|
"""
|
528
479
|
获取最新价格
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import logging
|
2
|
+
from decimal import Decimal
|
2
3
|
from core.smc.SMCStruct import SMCStruct
|
3
4
|
|
4
5
|
|
@@ -27,20 +28,17 @@ class SMCLiquidity(SMCStruct):
|
|
27
28
|
df = data.copy()
|
28
29
|
|
29
30
|
# 识别高点
|
30
|
-
df[self.LIQU_HIGH_COL] = 0
|
31
|
+
df[self.LIQU_HIGH_COL] = Decimal(0.0)
|
31
32
|
for i in range(pivot_length, len(df) - pivot_length):
|
32
33
|
if df[self.HIGH_COL].iloc[i] == max(df[self.HIGH_COL].iloc[i-pivot_length:i+pivot_length+1]):
|
33
|
-
df.loc[df.index[i], self.LIQU_HIGH_COL] = df[self.HIGH_COL].iloc[i]
|
34
|
-
|
34
|
+
df.loc[df.index[i], self.LIQU_HIGH_COL] = df[self.HIGH_COL].iloc[i]
|
35
35
|
# 识别低点
|
36
|
-
df[self.LIQU_LOW_COL] = 0
|
36
|
+
df[self.LIQU_LOW_COL] = Decimal(0.0)
|
37
37
|
for i in range(pivot_length, len(df) - pivot_length):
|
38
38
|
|
39
39
|
if df[self.LOW_COL].iloc[i] == min(df[self.LOW_COL].iloc[i-pivot_length:i+pivot_length+1]):
|
40
40
|
df.loc[df.index[i], self.LIQU_LOW_COL] = df[self.LOW_COL].iloc[i]
|
41
41
|
|
42
|
-
|
43
|
-
|
44
42
|
return df
|
45
43
|
|
46
44
|
def find_EQH_EQL(self, data, trend, end_idx=-1, atr_offset=0.1) -> dict:
|
@@ -63,7 +61,7 @@ class SMCLiquidity(SMCStruct):
|
|
63
61
|
try:
|
64
62
|
self.check_columns(df, check_columns)
|
65
63
|
except ValueError as e:
|
66
|
-
self.logger.warning(f"DataFrame must contain columns {check_columns} : {str(e)}")
|
64
|
+
# self.logger.warning(f"DataFrame must contain columns {check_columns} : {str(e)}")
|
67
65
|
df = self._identify_liquidity_pivots(df)
|
68
66
|
|
69
67
|
df = df[(df[self.LIQU_HIGH_COL] > 0) | (df[self.LIQU_LOW_COL] > 0)]
|
@@ -9,13 +9,14 @@ class SMCPDArray(SMCFVG,SMCOrderBlock):
|
|
9
9
|
PD_LOW_COL = "pd_low"
|
10
10
|
PD_MID_COL = "pd_mid"
|
11
11
|
PD_TYPE_COL = "pd_type"
|
12
|
+
PD_WAS_BALANCED_COL = "pd_was_balanced"
|
12
13
|
|
13
14
|
def __init__(self):
|
14
15
|
super().__init__()
|
15
16
|
self.logger = logging.getLogger(__name__)
|
16
17
|
|
17
18
|
def find_PDArrays(
|
18
|
-
self, struct: pd.DataFrame, side, start_index=-1,
|
19
|
+
self, struct: pd.DataFrame, side, start_index=-1, balanced=False,
|
19
20
|
) -> pd.DataFrame:
|
20
21
|
"""_summary_
|
21
22
|
寻找PDArrays,包括Fair Value Gap (FVG)|Order Block (OB)|Breaker Block(BB)|Mitigation Block(BB)
|
@@ -23,7 +24,7 @@ class SMCPDArray(SMCFVG,SMCOrderBlock):
|
|
23
24
|
data (pd.DataFrame): K线数据
|
24
25
|
side (_type_): 交易方向 'buy'|'sell'
|
25
26
|
start_index (int): 开始查找索引的起点,默认为-1
|
26
|
-
|
27
|
+
balanced (bool): PD是否有效,默认为False。PD被crossed过,则是无效PD
|
27
28
|
|
28
29
|
Returns:
|
29
30
|
pd.DataFrame: _description_
|
@@ -36,11 +37,14 @@ class SMCPDArray(SMCFVG,SMCOrderBlock):
|
|
36
37
|
else struct.copy().iloc[max(0, start_index - 1) :]
|
37
38
|
)
|
38
39
|
|
39
|
-
df_FVGs = self.find_FVGs(df, side,
|
40
|
-
# self.logger.info(f"fvgs:\n{df_FVGs[['timestamp', self.FVG_SIDE, self.FVG_TOP, self.FVG_BOT, self.FVG_WAS_BALANCED]]}")
|
40
|
+
df_FVGs = self.find_FVGs(df, side, start_index)
|
41
41
|
|
42
|
+
if not balanced:
|
43
|
+
df_FVGs = df_FVGs[~df_FVGs[self.FVG_WAS_BALANCED]]
|
44
|
+
# self.logger.info(f"fvgs:\n{df_FVGs[['timestamp', self.FVG_SIDE, self.FVG_TOP, self.FVG_BOT, self.FVG_WAS_BALANCED]]}")
|
42
45
|
|
43
|
-
|
46
|
+
is_valid = not balanced
|
47
|
+
df_OBs = self.find_OBs(struct=df, side=side, start_index=start_index, is_valid=is_valid)
|
44
48
|
# self.logger.info("find_OBs:\n %s", df_OBs)
|
45
49
|
|
46
50
|
# 使用更简洁的方式重命名和合并时间戳列
|
@@ -59,6 +63,10 @@ class SMCPDArray(SMCFVG,SMCOrderBlock):
|
|
59
63
|
df_PDArrays[self.TIMESTAMP_COL] = df_PDArrays[timestamp_mapping[self.TIMESTAMP_COL][0]].fillna(
|
60
64
|
df_PDArrays[timestamp_mapping[self.TIMESTAMP_COL][1]]
|
61
65
|
)
|
66
|
+
|
67
|
+
df_PDArrays[self.PD_WAS_BALANCED_COL] = df_PDArrays[[self.OB_WAS_CROSSED, self.FVG_WAS_BALANCED]].apply(
|
68
|
+
lambda x: x.iloc[0] if pd.notna(x.iloc[0]) else x.iloc[1], axis=1)
|
69
|
+
|
62
70
|
df_PDArrays[self.PD_TYPE_COL] = df_PDArrays[[self.FVG_SIDE, self.OB_DIRECTION_COL]].apply(
|
63
71
|
lambda x: 'FVG-OB' if pd.notna(x.iloc[0]) and pd.notna(x.iloc[1]) else 'FVG' if pd.notna(x.iloc[0]) else 'OB', axis=1
|
64
72
|
)
|
@@ -68,10 +76,12 @@ class SMCPDArray(SMCFVG,SMCOrderBlock):
|
|
68
76
|
df_PDArrays.loc[:, self.PD_MID_COL] = (df_PDArrays[self.PD_HIGH_COL] + df_PDArrays[self.PD_LOW_COL]) / 2
|
69
77
|
|
70
78
|
|
71
|
-
|
79
|
+
# 根据balanced参数过滤PDArrays,返回符合条件的数据
|
80
|
+
|
81
|
+
return df_PDArrays[df_PDArrays[self.PD_WAS_BALANCED_COL] == balanced]
|
72
82
|
|
73
83
|
|
74
|
-
def get_latest_PDArray(self, df_PDArrays: pd.DataFrame, side, start_index=-1,
|
84
|
+
def get_latest_PDArray(self, df_PDArrays: pd.DataFrame, side, start_index=-1, balanced=False, mask=None) -> dict:
|
75
85
|
"""_summary_
|
76
86
|
过滤PDArrays,只保留指定方向的PDArrays
|
77
87
|
Args:
|
@@ -89,16 +99,22 @@ class SMCPDArray(SMCFVG,SMCOrderBlock):
|
|
89
99
|
self.check_columns(df, check_columns)
|
90
100
|
except ValueError as e:
|
91
101
|
df = self.build_struct(df)
|
92
|
-
|
93
|
-
df = self.find_PDArrays(df, side, start_index, check_balanced)
|
94
102
|
|
95
|
-
|
96
|
-
|
103
|
+
|
104
|
+
check_columns = [self.PD_TYPE_COL]
|
105
|
+
try:
|
106
|
+
self.check_columns(df, check_columns)
|
107
|
+
except ValueError as e:
|
108
|
+
df = self.find_PDArrays(df, side, start_index, balanced)
|
109
|
+
|
110
|
+
if mask is not None:
|
111
|
+
df = df[mask]
|
97
112
|
|
98
113
|
if len(df) == 0:
|
99
114
|
self.logger.info("未找到PDArray.")
|
100
115
|
return None
|
101
116
|
else:
|
117
|
+
self.logger.debug(f"PDArray:\n{df[[self.TIMESTAMP_COL, self.PD_TYPE_COL, self.PD_HIGH_COL, self.PD_LOW_COL, self.PD_MID_COL,self.PD_WAS_BALANCED_COL,self.OB_WAS_CROSSED,self.FVG_WAS_BALANCED]]}")
|
102
118
|
last_pd = df.iloc[-1]
|
103
119
|
return {
|
104
120
|
self.TIMESTAMP_COL: last_pd[self.TIMESTAMP_COL],
|
@@ -106,4 +122,5 @@ class SMCPDArray(SMCFVG,SMCOrderBlock):
|
|
106
122
|
self.PD_HIGH_COL: last_pd[self.PD_HIGH_COL],
|
107
123
|
self.PD_LOW_COL: last_pd[self.PD_LOW_COL],
|
108
124
|
self.PD_MID_COL: last_pd[self.PD_MID_COL],
|
125
|
+
self.PD_WAS_BALANCED_COL: last_pd[self.PD_WAS_BALANCED_COL],
|
109
126
|
}
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|