openfund-maker 1.0.12__py3-none-any.whl → 1.0.13__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/ThreeLineOrderBot.py +112 -74
- maker/main.py +1 -1
- {openfund_maker-1.0.12.dist-info → openfund_maker-1.0.13.dist-info}/METADATA +1 -1
- {openfund_maker-1.0.12.dist-info → openfund_maker-1.0.13.dist-info}/RECORD +6 -6
- {openfund_maker-1.0.12.dist-info → openfund_maker-1.0.13.dist-info}/WHEEL +0 -0
- {openfund_maker-1.0.12.dist-info → openfund_maker-1.0.13.dist-info}/entry_points.txt +0 -0
maker/ThreeLineOrderBot.py
CHANGED
@@ -38,7 +38,29 @@ class ThreeLineOrdergBot:
|
|
38
38
|
self.logger = logger
|
39
39
|
self.position_mode = self.get_position_mode() # 获取持仓模式
|
40
40
|
|
41
|
-
|
41
|
+
def getMarket(self,symbol):
|
42
|
+
self.exchange.load_markets()
|
43
|
+
return self.exchange.market(symbol)
|
44
|
+
|
45
|
+
def get_tick_size(self,symbol):
|
46
|
+
return float(self.getMarket(symbol)['precision']['price'])
|
47
|
+
|
48
|
+
# 获取价格精度
|
49
|
+
def get_precision_length(self,symbol) -> int:
|
50
|
+
tick_size = self.get_tick_size(symbol)
|
51
|
+
return len(f"{tick_size:.10f}".rstrip('0').split('.')[1]) if '.' in f"{tick_size:.10f}" else 0
|
52
|
+
|
53
|
+
# def decimal_to_precision(self,symbol,price) -> float:
|
54
|
+
# from ccxt.base.decimal_to_precision import DECIMAL_PLACES, TICK_SIZE, NO_PADDING, TRUNCATE, ROUND, ROUND_UP, ROUND_DOWN, SIGNIFICANT_DIGITS
|
55
|
+
# tick_size = self.get_tick_size(symbol)
|
56
|
+
# new_px = self.exchange.decimal_to_precision(
|
57
|
+
# n=float(price),
|
58
|
+
# precision=tick_size,
|
59
|
+
# rounding_mode=ROUND,
|
60
|
+
# counting_mode=TICK_SIZE
|
61
|
+
# )
|
62
|
+
|
63
|
+
# return new_px
|
42
64
|
|
43
65
|
def get_position_mode(self):
|
44
66
|
try:
|
@@ -145,32 +167,31 @@ class ThreeLineOrdergBot:
|
|
145
167
|
atr = sum(trs[-period:]) / period
|
146
168
|
return atr
|
147
169
|
|
148
|
-
def calculate_sma_pandas(self,kLines,period):
|
170
|
+
def calculate_sma_pandas(self,symbol, kLines, period) -> pd.Series:
|
149
171
|
"""
|
150
172
|
使用 pandas 计算 SMA
|
151
173
|
:param KLines K线
|
152
174
|
:param period: SMA 周期
|
153
175
|
:return: SMA 值
|
154
176
|
"""
|
177
|
+
precision= self.get_precision_length(symbol)
|
155
178
|
df = pd.DataFrame(kLines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
156
|
-
sma = df['close'].rolling(window=period).mean()
|
179
|
+
sma = df['close'].rolling(window=period).mean().round(precision)
|
157
180
|
return sma
|
158
|
-
|
159
|
-
|
160
|
-
def calculate_ema_pandas(self,kLines, period):
|
181
|
+
|
182
|
+
def calculate_ema_pandas(self,symbol, kLines, period) -> pd.Series:
|
161
183
|
"""
|
162
184
|
使用 pandas 计算 EMA
|
163
185
|
:param KLines K线
|
164
186
|
:param period: EMA 周期
|
165
187
|
:return: EMA 值
|
166
188
|
"""
|
167
|
-
|
189
|
+
precision= self.get_precision_length(symbol)
|
168
190
|
df = pd.DataFrame(kLines, columns=['timestamp', 'open', 'high', 'low', 'close', 'volume'])
|
169
191
|
# 计算EMA
|
170
|
-
ema = df['close'].ewm(span=period, adjust=False).mean()
|
192
|
+
ema = df['close'].ewm(span=period, adjust=False).mean().round(precision)
|
171
193
|
return ema
|
172
194
|
|
173
|
-
|
174
195
|
def calculate_average_amplitude(self,klines, period=60):
|
175
196
|
amplitudes = []
|
176
197
|
for i in range(len(klines) - period, len(klines)):
|
@@ -247,16 +268,6 @@ class ThreeLineOrdergBot:
|
|
247
268
|
price_precision = market['precision']['price']
|
248
269
|
adjusted_price = self.round_price_to_tick(price, price_precision)
|
249
270
|
|
250
|
-
# okx api
|
251
|
-
# if instId not in self.instrument_info_dict:
|
252
|
-
# tick_size = float(self.instrument_info_dict[instId]['tickSz'])
|
253
|
-
# adjusted_price = self.round_price_to_tick(price, tick_size)
|
254
|
-
|
255
|
-
# response = public_api.convert_contract_coin(type='1', instId=instId, sz=str(amount_usdt), px=str(adjusted_price), unit='usdt', opType='open')
|
256
|
-
|
257
|
-
# 使用ccxt进行单位换算:将USDT金额转换为合约张数
|
258
|
-
# contract_amount = self.exchange.amount_to_precision(symbol, amount_usdt / float(adjusted_price))
|
259
|
-
# if float(contract_amount) > 0:
|
260
271
|
if amount_usdt > 0:
|
261
272
|
if side == 'buy':
|
262
273
|
pos_side = 'long'
|
@@ -304,75 +315,102 @@ class ThreeLineOrdergBot:
|
|
304
315
|
self.logger.info(f"--------- ++ {symbol} Order placed done! --------")
|
305
316
|
|
306
317
|
# 定义根据均线斜率判断 K 线方向的函数: 0 空 1 多 -1 平
|
307
|
-
def judge_k_line_direction(self,symbol, pair_config, ema) -> int:
|
308
|
-
|
309
|
-
|
318
|
+
def judge_k_line_direction(self,symbol, pair_config, ema:pd.Series) -> int:
|
319
|
+
"""
|
320
|
+
判断K线方向
|
321
|
+
Args:
|
322
|
+
symbol: 交易对
|
323
|
+
pair_config: 配置参数
|
324
|
+
ema: EMA数据
|
325
|
+
Returns:
|
326
|
+
int: -1:平, 0:空, 1:多
|
327
|
+
"""
|
328
|
+
def check_ema_range(ema_data: pd.Series, period: int, limit: float, tick_size: float) -> bool:
|
329
|
+
"""检查EMA是否在指定范围内震荡"""
|
330
|
+
ema_window = ema_data[-period:]
|
331
|
+
price_range = ema_window.max() - ema_window.min()
|
332
|
+
return abs(price_range) <= limit * tick_size
|
333
|
+
|
334
|
+
def get_trend_direction(slope: float) -> int:
|
335
|
+
"""根据斜率判断趋势方向"""
|
336
|
+
if slope > 0:
|
337
|
+
return 1 # 上升趋势
|
338
|
+
elif slope < 0:
|
339
|
+
return 0 # 下降趋势
|
340
|
+
return -1 # 震荡趋势
|
341
|
+
|
342
|
+
# 获取配置参数
|
343
|
+
tick_size = self.get_tick_size(symbol)
|
310
344
|
ema_range_period = int(pair_config.get('ema_range_period', 3))
|
311
345
|
ema_range_limit = float(pair_config.get('ema_range_limit', 1))
|
312
|
-
# 20250210 判断EMA均线是否走平,用EMA周期内的极差计算,极差在阈值范围内为平
|
313
|
-
ema_ranges = ema.rolling(window=ema_range_period).max() - ema.rolling(window=ema_range_period).min()
|
314
|
-
ema_slope = ema.diff()
|
315
|
-
latest_ema_slope = ema_slope.iloc[-1]
|
316
|
-
latest_ema_range = ema_ranges.abs().iloc[-1]
|
317
346
|
|
318
|
-
|
319
|
-
|
320
|
-
if latest_ema_range <= threshold: # 判断EMA极差范围
|
347
|
+
# 判断是否在震荡区间
|
348
|
+
if check_ema_range(ema, ema_range_period, ema_range_limit, tick_size):
|
321
349
|
direction = -1
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
direction =
|
326
|
-
|
350
|
+
else:
|
351
|
+
# 计算最新斜率并判断方向
|
352
|
+
latest_slope = ema.diff().iloc[-1]
|
353
|
+
direction = get_trend_direction(latest_slope)
|
354
|
+
|
355
|
+
self.logger.debug(f"{symbol}: 极差={abs(ema[-ema_range_period:].max() - ema[-ema_range_period:].min()):.9f} "
|
356
|
+
f"斜率={ema.diff().iloc[-1]:.9f}, K线方向 {direction}")
|
327
357
|
|
328
|
-
self.logger.debug(f"{symbol}: 极差={latest_ema_range:.9f} 斜率={latest_ema_slope:.9f}, K线方向 {direction}")
|
329
|
-
|
330
358
|
return direction
|
331
359
|
|
332
360
|
def judge_cross_direction(self,fastklines,slowklines) :
|
333
|
-
|
334
|
-
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
339
|
-
|
340
|
-
|
341
|
-
death_cross = (
|
342
|
-
|
343
|
-
#
|
344
|
-
|
345
|
-
|
346
|
-
|
347
|
-
|
348
|
-
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
357
|
-
|
358
|
-
|
359
|
-
|
360
|
-
|
361
|
+
# 创建DataFrame
|
362
|
+
df = pd.DataFrame({
|
363
|
+
'fast': fastklines,
|
364
|
+
'slow': slowklines
|
365
|
+
})
|
366
|
+
|
367
|
+
# 判断金叉和死叉
|
368
|
+
df['golden_cross'] = (df['fast'] > df['slow']) & (df['fast'].shift(1) < df['slow'].shift(1))
|
369
|
+
df['death_cross'] = (df['fast'] < df['slow']) & (df['fast'].shift(1) > df['slow'].shift(1))
|
370
|
+
|
371
|
+
# 从后往前找最近的交叉点
|
372
|
+
last_golden = df['golden_cross'].iloc[::-1].idxmax() if df['golden_cross'].any() else None
|
373
|
+
last_death = df['death_cross'].iloc[::-1].idxmax() if df['death_cross'].any() else None
|
374
|
+
|
375
|
+
# 判断最近的交叉类型
|
376
|
+
if last_golden is None and last_death is None:
|
377
|
+
return {
|
378
|
+
'cross': -1, # 无交叉
|
379
|
+
'index': None
|
380
|
+
}
|
381
|
+
|
382
|
+
# 如果金叉更近或只有金叉
|
383
|
+
if last_golden is not None and (last_death is None or last_golden > last_death):
|
384
|
+
return {
|
385
|
+
'cross': 1, # 金叉
|
386
|
+
'index': last_golden
|
387
|
+
}
|
388
|
+
# 如果死叉更近或只有死叉
|
389
|
+
else:
|
390
|
+
return {
|
391
|
+
'cross': 0, # 死叉
|
392
|
+
'index': last_death
|
393
|
+
}
|
361
394
|
|
362
395
|
def judge_ma_apex(self,symbol, fastklines,slowklines) -> bool:
|
363
|
-
|
364
|
-
df
|
365
|
-
|
396
|
+
|
397
|
+
df = pd.DataFrame({
|
398
|
+
'ema': fastklines,
|
399
|
+
'sma': slowklines
|
400
|
+
})
|
366
401
|
# 快线和慢线的差值
|
402
|
+
# 将ema和sma转换为tick_size精度
|
403
|
+
# df['diff'] = df['ema'].apply(lambda x: float(self.round_price_to_tick(x, tick_size))) - df['sma'].apply(lambda x: float(self.round_price_to_tick(x, tick_size)))
|
367
404
|
df['diff'] = df['ema']-df['sma']
|
368
405
|
# 计算斜率,【正】表示两线距离扩张,【负】表示两线距离收缩
|
369
|
-
df['slope'] = df['diff'].abs().diff().round(
|
406
|
+
df['slope'] = df['diff'].abs().diff().round(4)
|
407
|
+
df['flag'] = df['slope'] <= 0.0
|
370
408
|
|
371
|
-
self.logger.debug(f"{symbol}: slopes = {df['slope'].iloc[-6:-1]
|
409
|
+
self.logger.debug(f"{symbol}: slopes = \n{df[['ema','sma','diff','slope','flag']].iloc[-6:-1]} ")
|
372
410
|
# 检查最后两个斜率是否都为负
|
373
411
|
# 取slopes最新的第2个和第3个值进行判断
|
374
|
-
|
375
|
-
return all(slope
|
412
|
+
|
413
|
+
return all(slope <= 0.0 for slope in df['slope'].iloc[-3:-1])
|
376
414
|
|
377
415
|
|
378
416
|
def calculate_range_diff(self,prices:pd.Series) -> float:
|
@@ -436,8 +474,8 @@ class ThreeLineOrdergBot:
|
|
436
474
|
sma_length = pair_config.get('sma', 50)
|
437
475
|
|
438
476
|
# 增加 金叉死叉 方向确认的 20250209
|
439
|
-
fastk = self.calculate_ema_pandas(klines, period=ema_length)
|
440
|
-
slowk = self.calculate_sma_pandas(klines, period=sma_length)
|
477
|
+
fastk = self.calculate_ema_pandas(symbol, klines, period=ema_length)
|
478
|
+
slowk = self.calculate_sma_pandas(symbol, klines, period=sma_length)
|
441
479
|
|
442
480
|
cross_direction = self.judge_cross_direction(fastklines=fastk,slowklines=slowk)
|
443
481
|
# 更新交叉状态
|
maker/main.py
CHANGED
@@ -28,7 +28,7 @@ def main():
|
|
28
28
|
import importlib.metadata
|
29
29
|
version = importlib.metadata.version("openfund-maker")
|
30
30
|
|
31
|
-
wick_reversal_config_path =
|
31
|
+
wick_reversal_config_path = 'config.json'
|
32
32
|
with open(wick_reversal_config_path, 'r') as f:
|
33
33
|
config_data = json.load(f)
|
34
34
|
|
@@ -1,13 +1,13 @@
|
|
1
|
-
maker/ThreeLineOrderBot.py,sha256=e-
|
1
|
+
maker/ThreeLineOrderBot.py,sha256=e-KVx6a4cW2Afe0kNOKcA8isDObnzM_7wEmIlfw9tcI,25741
|
2
2
|
maker/WickReversalOrderBot.py,sha256=Oc6wChdWu39lfWh3NRHM8BqvaRIYDNZiDR6PDnE9XUM,17374
|
3
3
|
maker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
maker/config.py,sha256=YPxghO5i0vgRg9Cja8kGj9O7pgSbbtzOgf3RexqXXwY,1188
|
5
|
-
maker/main.py,sha256=
|
5
|
+
maker/main.py,sha256=Ad1LpVfoP0JHTaKpDPku2sVqb1vvbKczwf9eIEIAU0c,1874
|
6
6
|
maker/main_m.py,sha256=0PzDTnuBrxfpy5WDfsIHKAzZ_7pkuvuqqeWik0vpWio,15522
|
7
7
|
maker/okxapi.py,sha256=_9G0U_o0ZC8NxaT6PqpiLgxBm9gPobC9PsFHZE1c5w0,553
|
8
8
|
maker/zhen.py.bak,sha256=HNkrQbJts8G9umE9chEFsc0cLQApcM9KOVNMYPpkBXM,10918
|
9
9
|
maker/zhen_2.py,sha256=4IaHVtTCMSlrLGSTZrWpW2q-f7HZsUNRkW_-5QgWv24,10509
|
10
|
-
openfund_maker-1.0.
|
11
|
-
openfund_maker-1.0.
|
12
|
-
openfund_maker-1.0.
|
13
|
-
openfund_maker-1.0.
|
10
|
+
openfund_maker-1.0.13.dist-info/METADATA,sha256=-3hwWTGK021ZzoGPD0rzO6hLFpiP5QOGskNyJevdHuE,1966
|
11
|
+
openfund_maker-1.0.13.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
12
|
+
openfund_maker-1.0.13.dist-info/entry_points.txt,sha256=gKMytICEKcMRFQDFkHZLnIpID7UQFoTIM_xcpiiV6Ns,50
|
13
|
+
openfund_maker-1.0.13.dist-info/RECORD,,
|
File without changes
|
File without changes
|