Askhat777 commited on
Commit
5055aee
·
verified ·
1 Parent(s): e82a908

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +55 -8
app.py CHANGED
@@ -47,6 +47,8 @@ HTF_TREND_CANDLE_LIMIT = 50 # Свечей 4h для анализа (5
47
  HTF_TREND_SMA_PERIOD = 20 # Период SMA на старшем ТФ (20 x 4ч ≈ 3.3 дня)
48
  HTF_TREND_SMA_SLOPE_LOOKBACK = 5 # На сколько свечей назад сравниваем SMA для определения наклона (растет/падает)
49
  HTF_TREND_MAX_BELOW_SMA_PCT = 8.0 # Макс. допустимое отклонение цены НИЖЕ SMA (%), если SMA при этом падает
 
 
50
 
51
  # ── Подтверждение объемом на входе ──
52
  # Разворотные индикаторы (RSI-дно/дивергенция/Боллинджер) сами по себе не отличают "реальный
@@ -64,6 +66,16 @@ BOOK_IMBALANCE_ENABLED = True
64
  BOOK_IMBALANCE_DEPTH_PCT = 1.0 # Анализируем стакан в пределах ±1% от текущей цены
65
  BOOK_IMBALANCE_MIN_RATIO = 1.2 # bid-ликвидность должна быть минимум в 1.2 раза больше ask-ликвидности
66
 
 
 
 
 
 
 
 
 
 
 
67
  # ── Убывающий тейк-профит для зависших позиций ──
68
  # use_timeout=False и профит-ротация (срабатывает только при net_profit > 0) означают, что
69
  # позиция, ушедшая в минус и не отросшая обратно к изначальной цели, может висеть в слоте
@@ -284,6 +296,10 @@ class BotState:
284
  self._network_error_lock = threading.Lock()
285
  self.recent_network_errors: list[float] = [] # метки времени недавних сетевых ошибок (для скользящего окна)
286
 
 
 
 
 
287
  def budget_per_slot(self) -> float:
288
  if self.num_slots <= 0:
289
  return 0.0
@@ -676,14 +692,16 @@ def estimate_buy_slippage(exchange: ccxt.binance, symbol: str, budget_usdt: floa
676
  return avg_price, slippage_pct
677
 
678
 
679
- def check_book_liquidity_imbalance(exchange: ccxt.binance, symbol: str, current_price: float, state: BotState) -> bool:
680
  """
681
  Проверяет дисбаланс ликвидности в стакане в пределах BOOK_IMBALANCE_DEPTH_PCT% от текущей
682
  цены: суммарная ликвидность на покупку (bids) должна ощутимо превышать ликвидность на
683
  продажу (asks) - это признак реального спроса ("поддержки") прямо сейчас, а не просто
684
  технического сигнала по свечам. Используется как ДОПОЛНИТЕЛЬНОЕ подтверждение точки входа
685
- поверх RSI/дивергенции/Боллинджера, а не вместо них. П��и ошибке сети/пустом стакане -
686
- считаем подтверждение не пройденным (безопаснее пропустить сомнительный вход).
 
 
687
  """
688
  try:
689
  order_book = safe_call(exchange.fetch_order_book, symbol, 50, state=state, label=f"book-imbalance {symbol}")
@@ -708,7 +726,7 @@ def check_book_liquidity_imbalance(exchange: ccxt.binance, symbol: str, current_
708
  return True
709
 
710
  ratio = bid_liquidity / ask_liquidity
711
- return ratio >= BOOK_IMBALANCE_MIN_RATIO
712
 
713
 
714
  def compute_target_slots(current_balance: float) -> int:
@@ -941,7 +959,7 @@ def get_allowed_symbols(exchange: ccxt.binance, max_notional: float = MIN_NOTION
941
  return allowed
942
 
943
 
944
- def check_htf_trend_ok(exchange: ccxt.binance, symbol: str, state: BotState) -> bool:
945
  """
946
  Фильтр старшего тренда (4h) перед входом - защита от "падающих ножей".
947
  Разворотные сигналы на 1m/15m ловят только краткосрочный микро-отскок и ничего не знают
@@ -985,6 +1003,26 @@ def check_htf_trend_ok(exchange: ccxt.binance, symbol: str, state: BotState) ->
985
  return True
986
 
987
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
988
  def screen_full_market(
989
  exchange: ccxt.binance,
990
  allowed_symbols: list[str],
@@ -1194,18 +1232,27 @@ def screen_full_market(
1194
  if not is_entry_triggered:
1195
  continue
1196
 
 
 
 
 
 
 
 
 
1197
  # ── Подтверждение объемом ──
1198
  # Разворотные индикаторы (RSI-дно, дивергенция, Боллинджер) сами по себе НЕ отличают
1199
  # "настоящий отскок с реальным спросом" от "монета просто затихла и продолжит валиться
1200
  # дальше на тонком объеме". Требуем, чтобы объем на последней 1m свече был заметно выше
1201
  # среднего за последние VOLUME_CONFIRMATION_LOOKBACK свечей - это признак того, что в
1202
  # разворот заходит реальный объем покупателей, а не штиль перед новым проливом.
 
1203
  if VOLUME_CONFIRMATION_ENABLED:
1204
  volumes_1m = [k[5] for k in ohlcv_1m]
1205
  vol_window = volumes_1m[-(VOLUME_CONFIRMATION_LOOKBACK + 1):-1]
1206
  avg_vol = sum(vol_window) / len(vol_window) if vol_window else 0
1207
  last_vol = volumes_1m[-1]
1208
- if avg_vol <= 0 or last_vol < avg_vol * VOLUME_CONFIRMATION_MULTIPLIER:
1209
  continue
1210
 
1211
  # Вычисляем адаптивную просадку на основе суточной волатильности
@@ -1222,9 +1269,9 @@ def screen_full_market(
1222
  # продажу) - монете, скорее всего, еще есть куда падать даже после "дна" по индикаторам.
1223
  # Проверяем это ПОСЛЕДНИМ (после всех остальных, более дешевых фильтров), т.к. это
1224
  # отдельный сетевой запрос стакана на каждого кандидата, а до этой строки доходят уже
1225
- # немногие.
1226
  if BOOK_IMBALANCE_ENABLED:
1227
- if not check_book_liquidity_imbalance(exchange, sym, c["ask"], state):
1228
  continue
1229
 
1230
  # Считаем итоговый скоринг (чем ниже rsi_1h и rsi_val_1m, тем выше балл)
 
47
  HTF_TREND_SMA_PERIOD = 20 # Период SMA на старшем ТФ (20 x 4ч ≈ 3.3 дня)
48
  HTF_TREND_SMA_SLOPE_LOOKBACK = 5 # На сколько свечей назад сравниваем SMA для определения наклона (растет/падает)
49
  HTF_TREND_MAX_BELOW_SMA_PCT = 8.0 # Макс. допустимое отклонение цены НИЖЕ SMA (%), если SMA при этом падает
50
+ HTF_TREND_CACHE_TTL_SEC = 1800 # Кэш результата на 30 минут - 4h свеча меняется медленно, незачем
51
+ # дергать 4h-эндпоинт на КАЖДОМ скрине (раз в ~90-270с) для той же монеты
52
 
53
  # ── Подтверждение объемом на входе ──
54
  # Разворотные индикаторы (RSI-дно/дивергенция/Боллинджер) сами по себе не отличают "реальный
 
66
  BOOK_IMBALANCE_DEPTH_PCT = 1.0 # Анализируем стакан в пределах ±1% от текущей цены
67
  BOOK_IMBALANCE_MIN_RATIO = 1.2 # bid-ликвидность должна быть минимум в 1.2 раза больше ask-ликвидности
68
 
69
+ # ── "Мем-тир" - ужесточение для аномально волатильных монет ──
70
+ # Кейс PUMP: RSI-дно + всплеск объема сработали на резкий, но ЛОЖНЫЙ импульс посреди бокового
71
+ # шума - типично для низкокапных/мемных монет, где объемные всплески чаще манипулятивные, чем
72
+ # признак реального разворота. Вместо хрупкого списка тикеров определяем "мем-тир" ПО ПОВЕДЕНИЮ
73
+ # (аномальная суточная волатильность) и для таких монет требуем заметно более сильное
74
+ # подтверждение объемом и стаканом, чем для обычных кандидатов.
75
+ HIGH_VOLATILITY_MEME_THRESHOLD = 0.15 # 24h волатильность (high-low)/low, выше которой считаем монету мем-тиром
76
+ MEME_TIER_VOLUME_MULTIPLIER = 2.0 # Для мем-тира: объем свечи должен быть минимум в 2x выше среднего (вместо 1.3x)
77
+ MEME_TIER_BOOK_MIN_RATIO = 1.8 # Для мем-тира: bid-ликвидность должна перевешивать ask сильнее (вместо 1.2x)
78
+
79
  # ── Убывающий тейк-профит для зависших позиций ──
80
  # use_timeout=False и профит-ротация (срабатывает только при net_profit > 0) означают, что
81
  # позиция, ушедшая в минус и не отросшая обратно к изначальной цели, может висеть в слоте
 
296
  self._network_error_lock = threading.Lock()
297
  self.recent_network_errors: list[float] = [] # метки времени недавних сетевых ошибок (для скользящего окна)
298
 
299
+ # ── Кэш фильтра старшего тренда (4h) - снижает нагрузку на API ──
300
+ self._htf_cache_lock = threading.Lock()
301
+ self.htf_trend_cache: dict[str, tuple[float, bool]] = {} # symbol -> (когда проверяли, прошел ли фильтр)
302
+
303
  def budget_per_slot(self) -> float:
304
  if self.num_slots <= 0:
305
  return 0.0
 
692
  return avg_price, slippage_pct
693
 
694
 
695
+ def check_book_liquidity_imbalance(exchange: ccxt.binance, symbol: str, current_price: float, state: BotState, min_ratio: float = BOOK_IMBALANCE_MIN_RATIO) -> bool:
696
  """
697
  Проверяет дисбаланс ликвидности в стакане в пределах BOOK_IMBALANCE_DEPTH_PCT% от текущей
698
  цены: суммарная ликвидность на покупку (bids) должна ощутимо превышать ликвидность на
699
  продажу (asks) - это признак реального спроса ("поддержки") прямо сейчас, а не просто
700
  технического сигнала по свечам. Используется как ДОПОЛНИТЕЛЬНОЕ подтверждение точки входа
701
+ поверх RSI/дивергенции/Боллинджера, а не вместо них. min_ratio настраиваемый - для
702
+ аномально волатильных ("мем-тир") монет вызывающая сторона передает более строгий порог.
703
+ При ошибке сети/пустом стакане - считаем подтверждение не пройденным (безопаснее пропустить
704
+ сомнительный вход).
705
  """
706
  try:
707
  order_book = safe_call(exchange.fetch_order_book, symbol, 50, state=state, label=f"book-imbalance {symbol}")
 
726
  return True
727
 
728
  ratio = bid_liquidity / ask_liquidity
729
+ return ratio >= min_ratio
730
 
731
 
732
  def compute_target_slots(current_balance: float) -> int:
 
959
  return allowed
960
 
961
 
962
+ def _compute_htf_trend_ok(exchange: ccxt.binance, symbol: str, state: BotState) -> bool:
963
  """
964
  Фильтр старшего тренда (4h) перед входом - защита от "падающих ножей".
965
  Разворотные сигналы на 1m/15m ловят только краткосрочный микро-отскок и ничего не знают
 
1003
  return True
1004
 
1005
 
1006
+ def check_htf_trend_ok(exchange: ccxt.binance, symbol: str, state: BotState) -> bool:
1007
+ """
1008
+ Кэширующая обертка над _compute_htf_trend_ok. 4h свеча физически не может измениться за
1009
+ 90-270 секунд между скринами, поэтому дергать 4h-эндпоинт на КАЖДОМ цикле для одной и той же
1010
+ монеты - чистый перерасход лимитов API (именно это внесло вклад в 429 Too Many Requests).
1011
+ Результат кэшируется на HTF_TREND_CACHE_TTL_SEC (30 минут) на символ.
1012
+ """
1013
+ now = time.time()
1014
+ with state._htf_cache_lock:
1015
+ cached = state.htf_trend_cache.get(symbol)
1016
+ if cached is not None and (now - cached[0]) < HTF_TREND_CACHE_TTL_SEC:
1017
+ return cached[1]
1018
+
1019
+ result = _compute_htf_trend_ok(exchange, symbol, state)
1020
+
1021
+ with state._htf_cache_lock:
1022
+ state.htf_trend_cache[symbol] = (now, result)
1023
+ return result
1024
+
1025
+
1026
  def screen_full_market(
1027
  exchange: ccxt.binance,
1028
  allowed_symbols: list[str],
 
1232
  if not is_entry_triggered:
1233
  continue
1234
 
1235
+ # ── "Мем-тир": аномально волатильные монеты требуют более строгого подтверждения ──
1236
+ # Определяем ПО ПОВЕДЕНИЮ (суточная волатильность), а не по хрупкому списку тикеров -
1237
+ # кейс PUMP показал, что на таких монетах RSI-дно + обычный всплеск объема могут
1238
+ # сработать на ложный, манипулятивный импульс, а не на реальный разворот.
1239
+ is_meme_tier = c["volatility_24h"] >= HIGH_VOLATILITY_MEME_THRESHOLD
1240
+ vol_multiplier = MEME_TIER_VOLUME_MULTIPLIER if is_meme_tier else VOLUME_CONFIRMATION_MULTIPLIER
1241
+ book_min_ratio = MEME_TIER_BOOK_MIN_RATIO if is_meme_tier else BOOK_IMBALANCE_MIN_RATIO
1242
+
1243
  # ── Подтверждение объемом ──
1244
  # Разворотные индикаторы (RSI-дно, дивергенция, Боллинджер) сами по себе НЕ отличают
1245
  # "настоящий отскок с реальным спросом" от "монета просто затихла и продолжит валиться
1246
  # дальше на тонком объеме". Требуем, чтобы объем на последней 1m свече был заметно выше
1247
  # среднего за последние VOLUME_CONFIRMATION_LOOKBACK свечей - это признак того, что в
1248
  # разворот заходит реальный объем покупателей, а не штиль перед новым проливом.
1249
+ # Для мем-тира порог заметно строже (vol_multiplier).
1250
  if VOLUME_CONFIRMATION_ENABLED:
1251
  volumes_1m = [k[5] for k in ohlcv_1m]
1252
  vol_window = volumes_1m[-(VOLUME_CONFIRMATION_LOOKBACK + 1):-1]
1253
  avg_vol = sum(vol_window) / len(vol_window) if vol_window else 0
1254
  last_vol = volumes_1m[-1]
1255
+ if avg_vol <= 0 or last_vol < avg_vol * vol_multiplier:
1256
  continue
1257
 
1258
  # Вычисляем адаптивную просадку на основе суточной волатильности
 
1269
  # продажу) - монете, скорее всего, еще есть куда падать даже после "дна" по индикаторам.
1270
  # Проверяем это ПОСЛЕДНИМ (после всех остальных, более дешевых фильтров), т.к. это
1271
  # отдельный сетевой запрос стакана на каждого кандидата, а до этой строки доходят уже
1272
+ # немногие. Для мем-тира требуем более выраженный перевес (book_min_ratio).
1273
  if BOOK_IMBALANCE_ENABLED:
1274
+ if not check_book_liquidity_imbalance(exchange, sym, c["ask"], state, min_ratio=book_min_ratio):
1275
  continue
1276
 
1277
  # Считаем итоговый скоринг (чем ниже rsi_1h и rsi_val_1m, тем выше балл)