openfund-core 1.0.5__py3-none-any.whl → 1.0.7__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.
core/Exchange.py CHANGED
@@ -31,19 +31,8 @@ class Exchange:
31
31
  self.exchange.load_markets()
32
32
 
33
33
  return self.exchange.market(symbol)
34
-
35
- def get_tick_size(self,symbol) -> Decimal:
36
-
37
- market = self.getMarket(symbol)
38
- if market and 'precision' in market and 'price' in market['precision']:
39
- return OPTools.toDecimal(market['precision']['price'])
40
- else:
41
- raise ValueError(f"{symbol}: 无法从市场数据中获取价格精度")
42
-
43
- def amount_to_precision(self,symbol, contract_size):
44
- return self.exchange.amount_to_precision(symbol, contract_size)
45
-
46
- def get_position_mode(self):
34
+
35
+ def get_position_mode(self) -> str:
47
36
 
48
37
  try:
49
38
  # 假设获取账户持仓模式的 API
@@ -61,6 +50,19 @@ class Exchange:
61
50
  error_message = f"Error fetching position mode: {e}"
62
51
  self.logger.error(error_message)
63
52
  raise Exception(error_message)
53
+
54
+ def get_tick_size(self,symbol) -> Decimal:
55
+
56
+ market = self.getMarket(symbol)
57
+ if market and 'precision' in market and 'price' in market['precision']:
58
+ return OPTools.toDecimal(market['precision']['price'])
59
+ else:
60
+ raise ValueError(f"{symbol}: 无法从市场数据中获取价格精度")
61
+
62
+ def amount_to_precision(self,symbol, contract_size):
63
+ return self.exchange.amount_to_precision(symbol, contract_size)
64
+
65
+
64
66
 
65
67
  def set_leverage(self,symbol, leverage, mgnMode='isolated',posSide=None):
66
68
  try:
@@ -105,10 +107,66 @@ class Exchange:
105
107
  elif direction == 'cost_to_contract':
106
108
  contract_size = amount / price / market_contractSize
107
109
  else:
108
- raise Exception(f"{symbol}:{direction} 是无效的转换方向,请输入 'amount_to_contract' 或 'cost_to_contract'。")
110
+ raise Exception(f"{symbol} : {direction} 是无效的转换方向,请输入 'amount_to_contract' 或 'cost_to_contract'。")
109
111
 
110
112
  return self.amount_to_precision(symbol, contract_size)
111
-
113
+
114
+ def close_position(self, symbol, position, params={}) -> dict:
115
+
116
+ amount = abs(float(position['contracts']))
117
+
118
+ if amount <= 0:
119
+ self.logger.warning(f"{symbol}: position contracts must be greater than 0")
120
+ return
121
+
122
+ max_retries = 3
123
+ retry_count = 0
124
+ while retry_count < max_retries:
125
+
126
+ try:
127
+ side = position[self.SIDE_KEY]
128
+ self.logger.debug(f"{symbol}: Preparing to close position, side= {side}, amount={amount}")
129
+ position_mode = self.get_position_mode() # 获取持仓模式
130
+ if position_mode == 'long_short_mode':
131
+ # 在双向持仓模式下,指定平仓方向
132
+ # pos_side = 'long' if side == 'long' else 'short'
133
+ pos_side = side
134
+ else:
135
+ # 在单向模式下,不指定方向
136
+ pos_side = 'net'
137
+ orderSide = 'buy' if side == 'long' else 'sell'
138
+
139
+ td_mode = position['marginMode']
140
+ params = {
141
+ 'mgnMode': td_mode,
142
+ 'posSide': pos_side,
143
+ # 当市价全平时,平仓单是否需要自动撤销,默认为false. false:不自动撤单 true:自动撤单
144
+ 'autoCxl': 'true',
145
+ **params
146
+
147
+ }
148
+
149
+ # 发送平仓请求并获取返回值
150
+ order = self.exchange.close_position(
151
+ symbol=symbol,
152
+ side=orderSide,
153
+ params=params
154
+ )
155
+
156
+ self.logger.info(f"{symbol} Close position response : {order}")
157
+ return order
158
+
159
+ except Exception as e:
160
+
161
+ retry_count += 1
162
+ if retry_count == max_retries:
163
+ error_message = f"{symbol} Error closing position : {str(e)}"
164
+ self.logger.error(error_message)
165
+ raise Exception(error_message)
166
+ else:
167
+ self.logger.warning(f"{symbol} 平仓失败,正在进行第{retry_count}次重试: {str(e)}")
168
+ time.sleep(0.1) # 重试前等待0.1秒
169
+
112
170
 
113
171
  def cancel_all_orders(self, symbol):
114
172
  max_retries = 3
@@ -262,7 +320,7 @@ class Exchange:
262
320
 
263
321
  max_retries = 3
264
322
  retry_count = 0
265
- self.logger.debug(f"{symbol} : Pre Algo Order placed: {order} ")
323
+ self.logger.info(f"{symbol} : Pre Algo Order placed: {order} ")
266
324
  while retry_count < max_retries:
267
325
  try:
268
326
 
@@ -355,10 +413,11 @@ class Exchange:
355
413
  max_retries = 3
356
414
  retry_count = 0
357
415
 
416
+ self.logger.info(f"{symbol} : Pre Order placed: {order} ")
417
+
358
418
  while retry_count < max_retries:
359
419
  try:
360
420
  # 使用ccxt创建订单
361
- self.logger.debug(f"{symbol} : Pre Order placed: {order} ")
362
421
  order_result = self.exchange.create_order(
363
422
  **order
364
423
  # symbol=symbol,
@@ -371,24 +430,24 @@ class Exchange:
371
430
  except ccxt.NetworkError as e:
372
431
  # 处理网络相关错误
373
432
  retry_count += 1
374
- self.logger.warning(f"{symbol} : 设置下单时发生网络错误,正在进行第{retry_count}次重试: {str(e)}")
433
+ self.logger.warning(f"{symbol} : 下单时发生网络错误,正在进行第{retry_count}次重试: {str(e)}")
375
434
  time.sleep(0.1) # 重试前等待1秒
376
435
  continue
377
436
  except ccxt.ExchangeError as e:
378
437
  # 处理交易所API相关错误
379
438
  retry_count += 1
380
- self.logger.warning(f"{symbol} : 设置下单时发生交易所错误,正在进行第{retry_count}次重试: {str(e)}")
439
+ self.logger.warning(f"{symbol} : 下单时发生交易所错误,正在进行第{retry_count}次重试: {str(e)}")
381
440
  time.sleep(0.1)
382
441
  continue
383
442
  except Exception as e:
384
443
  # 处理其他未预期的错误
385
444
  retry_count += 1
386
- self.logger.warning(f"{symbol} : 设置下单时发生未知错误,正在进行第{retry_count}次重试: {str(e)}")
445
+ self.logger.warning(f"{symbol} : 下单时发生未知错误,正在进行第{retry_count}次重试: {str(e)}")
387
446
  time.sleep(0.1)
388
447
  continue
389
448
  if retry_count >= max_retries:
390
449
  # 重试次数用完仍未成功设置止损单
391
- error_message = f"!! {symbol}: 设置止盈止损单时重试次数用完仍未成功设置成功。 "
450
+ error_message = f"!! {symbol}: 下单时重试次数用完仍未成功设置成功。 "
392
451
  self.logger.error(error_message)
393
452
  raise Exception(error_message)
394
453
  self.logger.debug(f"{symbol} : --------- ++ Order placed done. --------")
core/smc/SMCLiquidity.py CHANGED
@@ -1,7 +1,209 @@
1
1
  import logging
2
+ from re import S
3
+ import pandas as pd
4
+ from core.smc.SMCStruct import SMCStruct
5
+ from pandas.core.strings.accessor import F
6
+ from pandas.io.parquet import catch_warnings
7
+
8
+ class SMCLiquidity(SMCStruct):
9
+ EQUAL_HIGH_COL = "equal_high"
10
+ EQUAL_LOW_COL = "equal_low"
11
+ LIQU_HIGH_COL = "liqu_high"
12
+ LIQU_LOW_COL = "liqu_low"
13
+ EQUAL_HIGH_INDEX_KEY = "equal_high_index"
14
+ EQUAL_LOW_INDEX_KEY = "equal_low_index"
15
+ HAS_EQ_KEY = "has_EQ"
16
+ LIQU_HIGH_DIFF_COL = "liqu_high_diff"
17
+ LIQU_LOW_DIFF_COL = "liqu_low_diff"
18
+
2
19
 
3
- class SMCLiquidity:
4
20
  def __init__(self):
21
+ super().__init__()
5
22
  self.logger = logging.getLogger(__name__)
6
-
7
-
23
+
24
+
25
+ def _identify_liquidity_pivots(self, data, pivot_length=1):
26
+ """
27
+ 识别流动性的高点和低点
28
+ """
29
+
30
+ df = data.copy()
31
+
32
+ # 识别高点
33
+ df[self.LIQU_HIGH_COL] = 0
34
+ for i in range(pivot_length, len(df) - pivot_length):
35
+ if df[self.HIGH_COL].iloc[i] == max(df[self.HIGH_COL].iloc[i-pivot_length:i+pivot_length+1]):
36
+ df.loc[df.index[i], self.LIQU_HIGH_COL] = df[self.HIGH_COL].iloc[i]
37
+
38
+ # 识别低点
39
+ df[self.LIQU_LOW_COL] = 0
40
+ for i in range(pivot_length, len(df) - pivot_length):
41
+
42
+ if df[self.LOW_COL].iloc[i] == min(df[self.LOW_COL].iloc[i-pivot_length:i+pivot_length+1]):
43
+ df.loc[df.index[i], self.LIQU_LOW_COL] = df[self.LOW_COL].iloc[i]
44
+
45
+
46
+
47
+ return df
48
+
49
+ def find_EQH_EQL(self, data, trend, end_idx=-1, atr_offset=0.1) -> dict:
50
+ """_summary_
51
+ 识别等高等低流动性
52
+ Args:
53
+ data (_type_): _description_
54
+ trend (_type_): _description_
55
+ end_idx (int, optional): _description_. Defaults to -1.
56
+ atr_offset (float, optional): _description_. Defaults to 0.1.
57
+
58
+ Returns:
59
+ dict: _description_
60
+ """
61
+
62
+ df = data.copy() if end_idx == -1 else data.copy().iloc[:end_idx+1]
63
+
64
+ check_columns = [self.LIQU_HIGH_COL, self.LIQU_LOW_COL]
65
+
66
+ try:
67
+ self.check_columns(df, check_columns)
68
+ except ValueError as e:
69
+ self.logger.warning(f"DataFrame must contain columns {check_columns} : {str(e)}")
70
+ df = self._identify_liquidity_pivots(df)
71
+
72
+ df = df[(df[self.LIQU_HIGH_COL] > 0) | (df[self.LIQU_LOW_COL] > 0)]
73
+ # 初始化结果列
74
+ df[self.EQUAL_HIGH_COL] = 0
75
+ df[self.EQUAL_LOW_COL] = 0
76
+ df[self.ATR_COL] = self.calculate_atr(df)
77
+ # 跟踪前一个高点和低点
78
+ previous_high = None
79
+ previous_high_index = None
80
+ previous_high_pos = -1
81
+ previous_low = None
82
+ previous_low_index = None
83
+ previous_low_pos = -1
84
+ for i in range(len(df)-1, -1, -1):
85
+
86
+ offset = self.toDecimal(df[self.ATR_COL].iloc[i] * atr_offset)
87
+
88
+ if trend == self.BULLISH_TREND:
89
+ current_high = df[self.LIQU_HIGH_COL].iloc[i]
90
+ if current_high == 0:
91
+ continue
92
+
93
+ if previous_high is None:
94
+ previous_high = current_high
95
+ previous_high_index = df.index[i]
96
+ previous_high_pos = i
97
+ continue
98
+
99
+ max_val = max(current_high, previous_high)
100
+ min_val = min(current_high, previous_high)
101
+
102
+
103
+ if abs(max_val - min_val) <= offset: # EQH|EQL
104
+
105
+ df.loc[df.index[i], self.EQUAL_HIGH_COL] = previous_high_index
106
+ df.loc[df.index[previous_high_pos], self.EQUAL_HIGH_COL] = previous_high_index
107
+
108
+ else:
109
+ # 倒序遍历,等高线被高点破坏,则更新等高点位置
110
+ if current_high > previous_high:
111
+ previous_high = current_high
112
+ previous_high_index = df.index[i]
113
+ previous_high_pos = i
114
+
115
+
116
+
117
+ else:
118
+ current_low = df[self.LIQU_LOW_COL].iloc[i]
119
+ if current_low == 0:
120
+ continue
121
+
122
+ # current_low = df[self.EQUAL_LOW_COL].iloc[i]
123
+ if previous_low is None:
124
+ previous_low = current_low
125
+ previous_low_index = df.index[i]
126
+ previous_low_pos = i
127
+ continue
128
+
129
+ max_val = max(current_low, previous_low)
130
+ min_val = min(current_low, previous_low)
131
+
132
+
133
+
134
+
135
+ if abs(max_val - min_val) <= offset: # EQH|EQL
136
+
137
+ df.loc[df.index[i], self.EQUAL_LOW_COL] = previous_low_index
138
+ df.loc[df.index[previous_low_pos], self.EQUAL_LOW_COL] = previous_low_index
139
+
140
+ else:
141
+ # 倒序遍历,等高线被高点破坏,则更新等高点位置
142
+ if current_low < previous_low:
143
+ previous_low = current_low
144
+ previous_low_index = df.index[i]
145
+ previous_low_pos = i
146
+
147
+ # 筛选有效结构且在prd范围内的数据
148
+ last_EQ = {
149
+
150
+ }
151
+ if trend == self.BULLISH_TREND :
152
+ mask = df[self.EQUAL_HIGH_COL] > 0
153
+ valid_EQH_df = df[ mask ]
154
+ if not valid_EQH_df.empty:
155
+ last_EQ[self.HAS_EQ_KEY] = True
156
+ last_EQ[self.EQUAL_HIGH_COL] = valid_EQH_df.iloc[-1][self.LIQU_HIGH_COL]
157
+ last_EQ[self.EQUAL_HIGH_INDEX_KEY] = valid_EQH_df.iloc[-1][self.EQUAL_HIGH_COL]
158
+ else:
159
+ mask = df[self.EQUAL_LOW_COL] > 0
160
+ valid_EQL_df = df[ mask ]
161
+ if not valid_EQL_df.empty:
162
+ last_EQ[self.HAS_EQ_KEY] = True
163
+ last_EQ[self.EQUAL_LOW_COL] = valid_EQL_df.iloc[-1][self.LIQU_LOW_COL]
164
+ last_EQ[self.EQUAL_LOW_INDEX_KEY] = valid_EQL_df.iloc[-1][self.EQUAL_LOW_COL]
165
+
166
+ return last_EQ
167
+
168
+ def identify_dynamic_trendlines(self, data, trend, start_idx=-1, end_idx=-1, ratio=0.8) -> tuple:
169
+ """
170
+ 识别动态趋势线或隧道
171
+ Args:
172
+ data (pd.DataFrame): _description_
173
+
174
+ Returns:
175
+ pd.DataFrame: _description_
176
+ """
177
+
178
+ df = data.copy() if start_idx == -1 or end_idx == -1 else data.copy().iloc[start_idx-1:end_idx+2] #考虑poivt值,前后各增加一个
179
+
180
+ check_columns = [self.LIQU_HIGH_COL]
181
+
182
+ try:
183
+ self.check_columns(df, check_columns)
184
+ except ValueError as e:
185
+ self.logger.warning(f"DataFrame must contain columns {check_columns} : {str(e)}")
186
+ df = self._identify_liquidity_pivots(df)
187
+ diff_ratio = 0.0
188
+ if trend == self.BEARISH_TREND:
189
+ # 判断Bearish趋势是高点不断升高,
190
+ liqu_bear_df = df[df[self.LIQU_HIGH_COL] > 0]
191
+ liqu_bear_df[self.LIQU_HIGH_DIFF_COL] = liqu_bear_df[self.LIQU_HIGH_COL].diff()
192
+ # self.logger.info(f"dynamic_trendlines:\n {liqu_bear_df[[self.TIMESTAMP_COL,self.LIQU_HIGH_COL,self.LIQU_HIGH_DIFF_COL]]}")
193
+ diff_ratio = self.toDecimal(liqu_bear_df[self.LIQU_HIGH_DIFF_COL].dropna().lt(0).mean(),2)
194
+ if diff_ratio >= ratio:
195
+ return diff_ratio,True
196
+ else:
197
+ # Bullish趋势是低点不断降低
198
+ liqu_bullish_df = df[df[self.LIQU_LOW_COL] > 0]
199
+ liqu_bullish_df[self.LIQU_LOW_DIFF_COL] = liqu_bullish_df[self.LIQU_LOW_COL].diff()
200
+ # self.logger.info(f"dynamic_trendlines:\n {liqu_bullish_df[[self.TIMESTAMP_COL,self.LIQU_LOW_COL,self.LIQU_LOW_DIFF_COL]]}")
201
+ diff_ratio = self.toDecimal(liqu_bullish_df[self.LIQU_LOW_DIFF_COL].dropna().gt(0).mean(),2)
202
+ if diff_ratio >= ratio:
203
+ return diff_ratio,True
204
+
205
+ return diff_ratio,False
206
+
207
+
208
+
209
+
core/smc/SMCStruct.py CHANGED
@@ -270,10 +270,13 @@ class SMCStruct(SMCBase):
270
270
  获取最新的结构
271
271
  """
272
272
  check_columns = [self.STRUCT_COL]
273
- if not self.check_columns(df, check_columns):
274
- data = self.build_struct(df=df)
275
- else:
276
- data = df.copy()
273
+ # if not self.check_columns(df, check_columns):
274
+ try :
275
+ self.check_columns(df, check_columns)
276
+ except ValueError as e:
277
+ df = self.build_struct(df)
278
+
279
+ data = df.copy()
277
280
 
278
281
  # 筛选有效结构且在prd范围内的数据
279
282
  last_struct = None
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: openfund-core
3
- Version: 1.0.5
3
+ Version: 1.0.7
4
4
  Summary: Openfund-core.
5
5
  Requires-Python: >=3.9,<4.0
6
6
  Classifier: Programming Language :: Python :: 3
@@ -1,15 +1,15 @@
1
- core/Exchange.py,sha256=eanGV-8ZLDFxP2tV6xumo8c68yqFTUeseJA_dHMhYb0,20785
1
+ core/Exchange.py,sha256=MDhV71jmWyM1IGaUI-iSfZqrUG9RNOpSWqm0jNV_GXU,23173
2
2
  core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  core/main.py,sha256=E-VZzem7-0_J6EmOo9blLPokc5MRcgjqCbqAvbkPnWI,630
4
4
  core/smc/SMCBase.py,sha256=epRC5bWDymx7ZMIhn_bVJRjvBHItt6BCnYASO2fhSDg,4302
5
5
  core/smc/SMCFVG.py,sha256=QtqlW1oooYVA7CG5ld5X0Q5twX1XCELO118IlMUhX6M,2974
6
- core/smc/SMCLiquidity.py,sha256=lZt2IQk3TWaT-nA7he57dUxPdLEWW61jRZWLAzOTat0,119
6
+ core/smc/SMCLiquidity.py,sha256=EkfyBVXmAiRihof6w3YJvRHlbwUtqag8S6aVrV_XWvc,8100
7
7
  core/smc/SMCOrderBlock.py,sha256=Il5JKmVER2vT6AKZLo0mD4wRqV_Op9IBK3jB1SfgTqY,9894
8
8
  core/smc/SMCPDArray.py,sha256=Vn_nTBLaIhrBhxe_hX3Iycn0gY0tmYd_qaqNalztfmA,2841
9
- core/smc/SMCStruct.py,sha256=FZoh_F81YvONZObbDF7jzH8F6lDgo0-17tNQwOS3V_g,12260
9
+ core/smc/SMCStruct.py,sha256=BIp5CIipLtOq-Z7aXhHK8KwjdPZzg2_7TVANirO-HlY,12337
10
10
  core/smc/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
11
11
  core/utils/OPTools.py,sha256=tJ1Jq_Caab6OWaX12xn4_g9ryf98Rm5I1zsJEEU8NIQ,1002
12
- openfund_core-1.0.5.dist-info/METADATA,sha256=VZWu0jgtwFtrr5fqdgk3p28GZWGwoMQeNNHwFj5QHsk,1953
13
- openfund_core-1.0.5.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
14
- openfund_core-1.0.5.dist-info/entry_points.txt,sha256=g8GUw3cyKFtcG5VWs8geU5VBLqiWr59GElqERuH8zD0,48
15
- openfund_core-1.0.5.dist-info/RECORD,,
12
+ openfund_core-1.0.7.dist-info/METADATA,sha256=tLNUyBlVeEkAKu1QyHltTgtvMmGUH_OUuOgjTNmGq3I,1953
13
+ openfund_core-1.0.7.dist-info/WHEEL,sha256=fGIA9gx4Qxk2KDKeNJCbOEwSrmLtjWCwzBz351GyrPQ,88
14
+ openfund_core-1.0.7.dist-info/entry_points.txt,sha256=g8GUw3cyKFtcG5VWs8geU5VBLqiWr59GElqERuH8zD0,48
15
+ openfund_core-1.0.7.dist-info/RECORD,,
@@ -1,4 +1,4 @@
1
1
  Wheel-Version: 1.0
2
- Generator: poetry-core 2.0.1
2
+ Generator: poetry-core 2.1.2
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any