Документ описывает текущую фактическую стратегию проекта после аудита конфигурации. Активный маршрут по умолчанию — ema_pullback для HTX USDT-M futures, с двумя профилями: long и short.
Бот запускается через python bot.py. По умолчанию BOT_PROFILES=long,short, поэтому CombinedHtxFuturesBot поднимает оба профиля в одном процессе.
Combined mode делает три важные вещи:
- использует один общий exchange-wrapper с кэшем market data;
- разделяет account-PnL runtime между профилями;
- помечает символ занятым для второго профиля, если у первого уже есть позиция, entry orders или exit orders;
- после рестарта учитывает не только локальный state, но и видимые биржевые позиции/ордера другого профиля, чтобы пустой state не создавал ложный
unexpected_*для противоположной стороны.
Combined mode требует одинаковые HTX API credentials для всех включенных профилей.
Активная реализация ориентирована на HTX linear USDT-M futures.
default_type=swap;- quote currency:
USDT; - margin mode:
cross; - position mode:
one-way; - entry side:
buyдля long,sellдля short; - exit side:
sellдля long,buyдля short.
Плечо в конфиге разделено по смыслу:
LEVERAGE— внутренний множитель sizing/notional caps;ACCOUNT_LEVERAGE— ручное live-плечо аккаунта, если его нужно явно подсказать боту.
Бот не исходит из того, что может безопасно менять плечо на бирже при старте. В live он использует leverage, прочитанный с HTX, либо ACCOUNT_LEVERAGE.
Периоды задаются в минутах, затем конвертируются в количество свечей выбранного timeframe.
| Слой | Параметры | Timeframe | По умолчанию |
|---|---|---|---|
| Macro fast | EMA_MACRO_FAST_MINUTES=2880 |
1h |
EMA48 |
| Macro slow | EMA_MACRO_SLOW_MINUTES=7200 |
1h |
EMA120 |
| Pullback fast | EMA_PULLBACK_FAST_MINUTES=120 |
5m |
EMA24 |
| Pullback slow | EMA_PULLBACK_SLOW_MINUTES=360 |
5m |
EMA72 |
| Trigger fast | EMA_TRIGGER_FAST_MINUTES=120 |
5m |
EMA24 |
| Trigger slow | EMA_TRIGGER_SLOW_MINUTES=360 |
5m |
EMA72 |
Используются только закрытые свечи. Текущая незакрытая свеча не участвует в расчете сигнала.
Long-сигнал считается пригодным для нового входа, когда одновременно выполняется базовая логика:
EMA48H > EMA120H
EMA24x5m > EMA72x5m
rs60 >= EMA_LONG_MIN_RS60, если EMA_USE_RS_CONFIRMATION=true
btc_return_30m >= EMA_BTC_LONG_MIN_RETURN_30M, если EMA_USE_BTC_RISK_FILTER=true
market_structure_valid=true
Pullback recovery проверяет недавнее восстановление после состояния против направления сделки. По умолчанию это quality penalty, а не обязательная третья стадия входа; EMA_ENTRY_REQUIRE_PULLBACK_RECOVERY=true возвращает его в hard gate. Возраст recovery-cross ограничен EMA_PULLBACK_RECOVERY_MAX_CROSS_AGE_MINUTES, а история поиска — EMA_PULLBACK_RECOVERY_LOOKBACK_MINUTES.
Short-сигнал зеркален:
EMA48H < EMA120H
EMA24x5m < EMA72x5m
rs60 <= EMA_SHORT_MAX_RS60, если EMA_USE_RS_CONFIRMATION=true
btc_return_30m <= EMA_BTC_SHORT_MAX_RETURN_30M, если EMA_USE_BTC_RISK_FILTER=true
market_structure_valid=true
Для short относительная сила и EMA-разрывы приводятся к направлению позиции через directional value, чтобы quality gates работали симметрично.
Сигнал содержит score, rs30, rs60, EMA-значения, BTC context, volatility context, macro context и external-price context.
Флаги сигнала разделены по назначению:
valid— направленный сигнал собран и не противоречит базовой структуре стратегии;entry_valid— пройдены условия именно для нового входа;add_valid— сигнал достаточно здоров для добора уже открытой позиции, даже если полный new-entry gate сейчас не проходит.
market_structure_valid по умолчанию требует EMA_CHOP_FILTER_ENABLED=true с chop <= EMA_CHOP_MAX на trigger-свечах и EMA_VOLUME_CONFIRMATION_ENABLED=true с отношением свежего объёма к базовой средней не ниже EMA_VOLUME_MIN_RATIO. Volume gate дополнительно учитывает single-candle spike (EMA_VOLUME_SPIKE_*) и простой volume profile/value area (EMA_VOLUME_PROFILE_*): направленный spike может подтвердить восстановление после отката, а сильный adverse spike с пробоем value-area против профиля блокирует entry_valid и add_valid как риск начала разворота. Этот gate применяется к entry_valid и add_valid, но не сбрасывает valid, чтобы уже открытая позиция не теряла управление выходами из-за шумного рынка.
Для нового входа дополнительно проверяются:
ENTRY_MIN_SCORE;ENTRY_MIN_RS60_ABS;ENTRY_MIN_RS30_ABS;ENTRY_MAX_NEW_LADDERS_PER_SIGNAL;ENTRY_RATE_LIMIT_LADDERSзаENTRY_RATE_LIMIT_WINDOW_MINUTES;- crowded-market правила:
ENTRY_CROWDED_SIGNAL_FRACTION,ENTRY_CROWDED_MIN_SIGNALS,ENTRY_CROWDED_*.
Когда рынок “crowded”, бот ужесточает пороги и уменьшает число новых ladder entries на один сигнал. Это защищает от массового открытия однотипных позиций при синхронном движении альтов.
ExternalPriceFeed использует MEXC book ticker как reference для HTX.
Активные проверки:
- валидность HTX/MEXC bid-ask и минимальный notional стакана;
- freshness через
EXTERNAL_PRICE_MAX_PRICE_AGE_MSиEXTERNAL_PRICE_STALE_AFTER_MS; - premium/discount HTX относительно MEXC;
- divergence за 1 минуту между HTX и MEXC;
- directional 1m gate для entry и averaging;
- impulse score bonus при движении MEXC в сторону сделки;
- tighten exit ladder при неблагоприятном HTX premium/discount.
Если reference устарел, поведение задается парой:
EXTERNAL_PRICE_DISABLE_TRADING_IF_REFERENCE_STALE
EXTERNAL_PRICE_IGNORE_REFERENCE_IF_STALE
По умолчанию stale reference не обязан полностью блокировать торговлю, но невалидный context записывается в external_price_feed.csv и signal_analytics.
Начальный вход ставится только если:
- символ входит в
entry_symbols; - нет открытой позиции;
- нет активных entry orders;
- позиция не frozen и не zombie;
- нет cooldown;
- сигнал прошел quality gate;
- macro overlay не запретил новые входы;
- combined mode не зарезервировал символ другим профилем;
- risk caps и external entry checks разрешили сделку.
Бюджет:
base_margin_budget = account_equity * EMA_POSITION_BUDGET_FRACTION
По умолчанию:
EMA_POSITION_BUDGET_FRACTION=0.02
EMA_ENTRY_LADDER_FRACTIONS=0.50,0.50
EMA_ENTRY_LADDER_OFFSETS=0.0,0.01
Фактический margin budget дополнительно ограничивается:
MIN_QUOTE_RESERVE;MAX_ACTIVE_POSITIONS;EMA_MAX_POSITION_MARGIN_FRACTION;EMA_MAX_TOTAL_MARGIN_FRACTION;- exchange minimum amount/notional;
- доступной free margin;
- macro budget multiplier;
- volatility/account multipliers, если включены.
В live entry orders отправляются как limit/post-only, если POST_ONLY_ENABLED=true.
Активный механизм добора — EMA averaging. Он управляется параметрами EMA_AVERAGING_*.
Добор возможен только если:
EMA_AVERAGING_ENABLED=true;- позиция открыта и имеет entry price;
- breakeven еще не активирован;
- позиция не старше
EMA_BREAKEVEN_AFTER_HOURSв части добора; - нет активных entry orders;
- позиция не frozen/zombie;
- у позиции есть exit ladder;
- сигнал валиден и
add_valid=true; - macro context не запретил averaging;
- BTC/external directional gates не блокируют добор;
- выдержан
EMA_AVERAGING_INTERVAL_HOURS; - не превышен
EMA_MAX_AVERAGING_STAGES. pullback_valid=true, еслиEMA_AVERAGING_REQUIRE_PULLBACK_RECOVERY=true.
Порог drawdown:
stage_1 = max(AVERAGING_DRAWDOWN_STEP_1, EMA_AVERAGING_MIN_DRAWDOWN_STEP)
stage_2 = max(AVERAGING_DRAWDOWN_STEP_2, EMA_AVERAGING_MIN_DRAWDOWN_STEP * 2)
...
Effective threshold дополнительно расширяется, если сигнал содержит ATR или daily volatility:
atr_floor = atr_rate * EMA_AVERAGING_MIN_ATR_MULTIPLIER * stage
daily_vol_floor = daily_volatility * EMA_AVERAGING_MIN_DAILY_VOLATILITY_FRACTION * stage
effective_threshold = max(configured_stage, min_stage_floor, atr_floor, daily_vol_floor)
По умолчанию:
EMA_AVERAGING_DRAWDOWN_STEP=0.01
EMA_AVERAGING_MIN_DRAWDOWN_STEP=0.01
EMA_AVERAGING_MIN_ATR_MULTIPLIER=1.0
EMA_AVERAGING_MIN_DAILY_VOLATILITY_FRACTION=0.18
EMA_AVERAGING_REQUIRE_PULLBACK_RECOVERY=true
EMA_MAX_AVERAGING_STAGES=2
Размер добора:
base_notional = initial_entry_notional
ratio = max(current_position_notional / base_notional, 1)
desired_notional = current_position_notional * EMA_AVERAGING_BASE_FRACTION
Если добор разрешен account-PnL context, применяется ACCOUNT_AVERAGING_BUDGET_SCALE.
EMA_AVERAGING_BASE_FRACTION также читает legacy alias EMA_AVERAGING_POSITION_FRACTION; это доля от текущей позиции, а не от начального входа. EMA_AVERAGING_POWER сохраняется как legacy-настройка для совместимости логов, но размер добора не может превышать явно заданную долю текущей позиции.
По умолчанию:
EMA_AVERAGING_BASE_FRACTION=0.50
EMA_AVERAGING_POWER=1.0
ACCOUNT_AVERAGING_BUDGET_SCALE=0.50
Бот ведет общий account-PnL runtime для combined profiles и пишет account_pnl.csv.
Две opt-in функции:
account_profit_unload: частично закрывает прибыльные позиции, когда общий account PnL находится в верхнем диапазоне;account_averaging: разрешает/масштабирует доборы только около account-PnL trough и после прекращения падения PnL.
Ключевые параметры:
ACCOUNT_PNL_ENABLED=true
ACCOUNT_PROFIT_UNLOAD_ENABLED=false
ACCOUNT_AVERAGING_ENABLED=false
ACCOUNT_PNL_WINDOW_MINUTES=360
ACCOUNT_PNL_SAMPLE_INTERVAL_SEC=30
Выходы ставятся reduce-only. Активный default — exchange-side hard stop-loss плюс adaptive exit ladder.
Hard stop-loss ставится как HTX/ccxt TPSL stopLossPrice с market close и reduceOnly=true. Размер стопа не больше текущего position_size. Фиксированный fallback включён по умолчанию:
HARD_STOP_LOSS_ENABLED=true
HARD_STOP_LOSS_PCT=0.02
HARD_STOP_LOSS_ATR_ENABLED=true
HARD_STOP_LOSS_ATR_MULTIPLIER=2.0
HARD_STOP_LOSS_ATR_MAX_PCT=0.03
Если сигнал содержит atr_rate, effective stop distance = max(fixed pct, ATR * multiplier), но ATR-компонент ограничивается HARD_STOP_LOSS_ATR_MAX_PCT. После рестарта, пока ATR недоступен, используется фиксированный stop-loss. Если HTX сообщает, что closeable amount зарезервирован TP ladder, бот отменяет tracked TP ladder и повторяет hard stop первым; TP ladder затем может быть восстановлен только если биржа даёт closeable amount.
Режим выбирается по отношению текущего notional позиции к initial notional:
- normal: до
EMA_EXIT_MEDIUM_POSITION_RATIO; - medium: до
EMA_EXIT_HEAVY_POSITION_RATIO; - heavy: выше heavy threshold.
Параметры по умолчанию:
EMA_ADAPTIVE_EXIT_ENABLED=true
EMA_EXIT_NORMAL_LADDER_FRACTIONS=0.35,0.25,0.25,0.15
EMA_EXIT_NORMAL_LADDER_MARKUPS=0.008,0.016,0.030,0.050
EMA_EXIT_MEDIUM_LADDER_FRACTIONS=0.45,0.30,0.15,0.10
EMA_EXIT_MEDIUM_LADDER_MARKUPS=0.004,0.010,0.020,0.035
EMA_EXIT_HEAVY_LADDER_FRACTIONS=0.60,0.25,0.15
EMA_EXIT_HEAVY_LADDER_MARKUPS=0.003,0.008,0.015
EMA_EXIT_RUNNER_ENABLED=true
EMA_EXIT_TRAILING_ENABLED=true
EMA_EXIT_TRAILING_FIXED_FRACTION=0.30
EMA_EXIT_TRAILING_ACTIVATION_MARKUP=0.020
EMA_EXIT_TRAILING_PULLBACK=0.010
EMA_EXIT_TRAILING_ATR_MULTIPLIER=1.5
EMA_EXIT_TRAILING_MIN_PULLBACK=0.006
EMA_EXIT_TRAILING_MAX_PULLBACK=0.030
EMA_EXIT_TRAILING_TAKE_PROFIT_MARKUP=0.050
EMA_EXIT_RUNNER_PROFIT_LOCK_ENABLED=true
EMA_EXIT_RUNNER_USE_AGGRESSIVE_LIMIT=true
Normal mode по умолчанию фиксирует часть позиции через reduce-only TP ladder и оставляет runner-остаток: fixed fraction закрывается по первому normal markup, runner активируется после EMA_EXIT_TRAILING_ACTIVATION_MARKUP и закрывается при откате от лучшей цены или при достижении дальнего take-profit markup. Pullback расширяется от atr_rate/volatility в signal и ограничивается min/max настройками. После активации runner hard stop-loss подтягивается к breakeven, чтобы прибыльная позиция не превратилась обратно в убыточную. Для averaged split ladder runner не включается: базовая и recovery-части остаются полностью покрыты fixed reduce-only exits.
Profit floor учитывает комиссии:
profit_floor >= (buy_fee_rate + sell_fee_rate) * min_profit_fee_multiplier
Если HTX отклоняет reduce-only ladder с причиной, что closeable amount уже зарезервирован существующими close orders, бот переводит ladder в pending mode (pending_closeable:*) и не повторяет постановку только из-за истечения таймаута, пока snapshot показывает position_available=0 и position_frozen>0. Retry возобновляется, когда появляется closeable amount, меняется размер позиции или видимые close orders можно принять/отменить.
После HARD_TIME_EXIT_AFTER_HOURS=96 включается bounded-loss маршрут: бот может постепенно перестраивать reduce-only выход с ограничением HARD_TIME_EXIT_MAX_LOSS_ON_NOTIONAL=0.03, начиная с HARD_TIME_EXIT_CLOSE_FRACTION=0.25 и увеличивая долю каждые HARD_TIME_EXIT_STEP_MINUTES.
Breakeven заменяет обычный exit ladder после заданного времени удержания:
EMA_BREAKEVEN_ENABLED=true
EMA_BREAKEVEN_AFTER_HOURS=48
EMA_BREAKEVEN_REPRICE_MINUTES=15
EMA_BREAKEVEN_FEE_BUFFER=0.0002
EMA_BREAKEVEN_EXIT_FRACTIONS=1.0
При активации:
- новые entry orders отменяются;
frozen_no_more_buys=true;- sell ladder переводится в mode
breakeven; - reduce-only exit ставится около entry price с fee buffer;
- stale breakeven ladder периодически переставляется.
В live режиме есть защитные cleanup-ветки:
DUST_CLOSE_ENABLEDзакрывает слишком маленькую позицию reduce-only market;TINY_ENTRY_CLOSE_ENABLEDзакрывает микроскопический частичный entry, если он слишком мал относительно planned budget.
Эти cleanup-действия отправляют реальные reduce-only market orders, поэтому они покрываются unit-тестами через mock/stub exchange.
Macro overlay сравнивает XAUT/BTC context через RSI и может:
- считать
macro_direction_scoreкак directional bias между long/short профилями; - усиливать профиль, которому помогает macro context, через capped
long_budget_multiplier/short_budget_multiplier; - снижать budget multiplier;
- расширять ladder multiplier;
- запрещать новые входы в panic/risk-off;
- запрещать averaging;
- ускорять breakeven через time-exit multiplier.
По умолчанию XAUT используется только как macro/reference input и не добавляется в список торгуемых монет.
Профили пишут отдельные state/log файлы:
long/bot_futures_state.json;short/bot_futures_short_state.json;- trade event CSV;
- cycle stats CSV;
signal_analytics.csv;signal_analytics.jsonl;diagnostics.csv;diagnostics.jsonl;account_pnl.csv;external_price_feed.csv;bot_futures_macro.csv.
signal_analytics.csv содержит текущую EMA-схему: ema50, ema100, ema1d, ema2d, ema25d, ema50d, а также компоненты macro_gap, trigger_gap, pullback_depth.
Все diagnostics/signal analytics/runtime CSV/JSONL файлы являются локальными артефактами аудита и не должны попадать в git. Если старые diagnostics со signed HTX URL уже были опубликованы или отправлены третьим лицам, API key нужно ротировать до live-старта.
После аудита из config.py удалены параметры, которые не имели реального runtime-потребителя:
- дубли EMA/ladder полей в
SignalSettings,SellSettingsиStrategySettings; - legacy entry-expansion thresholds и multipliers;
- неиспользуемые time-exit/reprice/dynamic-time-exit поля;
- старые неиспользуемые controlled-loss ladder поля;
- неиспользуемые external-price поля
use_existing_trading_universe,only_usdt_pairs,reconnect_on_stale_ms,tighten_ladder_factor; - неиспользуемый monitoring TTL.
Оставлены выключенные по умолчанию, но реально подключенные механики: volatility sizing/recovery, BTC risk multiplier, funding-aware exit, dynamic profit floor, hard/controlled/absolute force exit helpers.
Controlled-loss exit при активации двигает цену закрытия от CONTROLLED_LOSS_MIN_MOVE_FRACTION к reference price за CONTROLLED_LOSS_RAMP_MINUTES; скорость ramp ускоряется при отрицательном directional trend_ema_gap/macro_gap, неблагоприятном macro overlay и adverse local volatility spike (atr_rate/realized volatility против CONTROLLED_LOSS_VOLATILITY_REFERENCE или VOLATILITY_REFERENCE). При volatility spike progress становится exponential_volatility, а ladder может перестроиться до обычного stale reprice, если новый loss_move_fraction вырос минимум на CONTROLLED_LOSS_VOLATILITY_REPRICE_MIN_MOVE_DELTA; обычный stale ladder всё ещё перестраивается через CONTROLLED_LOSS_REPRICE_MINUTES.
Перед live-запуском:
- Прогнать
python -m pytest -q. - Проверить ключевые runtime-сценарии через mock/stub exchange без подключения к live-аккаунту.
- Проверить корневой
.env: profile overrides с префиксамиLONG_/SHORT_не должны противоречить общей маршрутизации API-аккаунтов. - Убедиться, что
EMA_POSITION_BUDGET_FRACTION,EMA_MAX_POSITION_MARGIN_FRACTION,EMA_MAX_TOTAL_MARGIN_FRACTIONостаются консервативными. - Проверить HTX account leverage или задать
ACCOUNT_LEVERAGE. - Проверить, что runtime diagnostics/signal analytics файлы не staged и не tracked git.
- После этого запускать бот с минимальными бюджетами и внимательно наблюдать первые циклы.