← Назад к главному
# strategy_core.rs - Модуль первого входа в позицию ## 📋 Назначение модуля `CoreStrategy` отвечает за **первый вход** в позицию (SINGLE режим). Это критический модуль, который: 1. Проверяет что позиция полностью пустая (no LONG, no SHORT) 2. Загружает/рассчитывает параметры входа (qty, leverage, margin) 3. Проверяет что `DistanceToLIQ ≥ 100%` (безопасность от ликвидации) 4. **Принудительно устанавливает DUAL+CROSS режим** с проверкой 5. Открывает первую позицию (LONG или SHORT) Если условия не выполняются - возвращает пустой `actions[]` → `main.rs` завершает сессию → ищет новую пару. --- ## 🏗️ Структура модуля ### Объекты ```rust pub struct CoreStrategy { } ``` **Параметры входа (EntryParams):** ```rust pub struct EntryParams { pub margin_limit_usd: f64, // 2.0 USDT - лимит маржи на вход pub min_distance_to_liq_pct: f64, // 100.0% - минимальное расстояние до ликвидации } ``` ### Основные функции #### 1. `ensure_dual_cross_before_trade()` **Строки:** 44-134 **Назначение:** Принудительная установка DUAL+CROSS режима ПЕРЕД первым ордером **Критичность:** 🔥🔥🔥 КРИТИЧЕСКИ ВАЖНО! Без этого позиции открываются в ISOLATED режиме! **Алгоритм (5 шагов):** ``` A) Лог текущего состояния ДО ↓ get_dual_positions(contract) ↓ Проверяем mode и margin ↓ Если margin == 0 → CROSS уже установлен B) Включить DUAL mode ↓ set_dual_mode_enabled(true) C) Установить CROSS через dual_comp endpoint ↓ set_dual_cross_mode(contract, "CROSS") D) Установить leverage (ПОСЛЕ cross_mode!) ↓ update_dual_leverage(contract, leverage) ↓ Важно: leverage устанавливается ПОСЛЕ cross_mode! E) Лог состояния ПОСЛЕ + КРИТИЧЕСКАЯ ПРОВЕРКА ↓ get_dual_positions(contract) ↓ Проверяем margin == 0 ↓ Если margin != 0 → КРИТИЧЕСКАЯ ОШИБКА (ISOLATED вместо CROSS) ``` **Проверки:** - ✅ **margin == 0** → CROSS режим подтверждён - ❌ **margin != 0** → ISOLATED режим → ОШИБКА → НЕ торгуем **Возвращает:** - `Ok(())` - режим установлен корректно - `Err(String)` - ошибка, торговля запрещена **Пример лога:** ``` 🔧 === ENSURE DUAL+CROSS ДО ТОРГОВЛИ === 📊 A) ТЕКУЩЕЕ СОСТОЯНИЕ ДО: contract=MEMES_USDT, mode=dual, margin=0 ✅ CROSS уже установлен! (margin=0) 🔧 B) ВКЛЮЧАЕМ DUAL MODE: ✅ DUAL mode включен 🔧 C) УСТАНАВЛИВАЕМ CROSS для DUAL: ✅ CROSS для DUAL установлен 🔧 D) УСТАНАВЛИВАЕМ LEVERAGE 50.0x (DUAL+CROSS): ✅ DUAL leverage установлен (leverage=0, cross_leverage_limit=50.0) 📊 E) СОСТОЯНИЕ ПОСЛЕ (критическая проверка): contract=MEMES_USDT, mode=dual, margin=0 ✅ ПОДТВЕРЖДЕНО: Контракт в CROSS режиме! (margin=0) 🔧 === КОНЕЦ ENSURE DUAL+CROSS (УСПЕХ) === ``` --- #### 2. `CoreStrategy::process_with_real_limits()` **Строки:** 144-282 **Назначение:** Основная логика первого входа в позицию **Сигнатура:** ```rust pub async fn process_with_real_limits( &self, redis: Arc, // Redis клиент gate: &GateClient, // Gate.io API pos: &PositionSnapshot, // Текущее состояние позиции meta: &TradingMeta, // Метаданные контракта current_balance: f64, // Текущий баланс (equity) ) -> Vec ``` **Алгоритм (11 этапов):** ### Этап 1: Проверка пустой позиции ```rust if pos.long_qty > 0.0 || pos.short_qty > 0.0 { return actions; // CoreStrategy выключен если что-то открыто } ``` ### Этап 2: Проверка баланса и цены ```rust let equity_now = (current_balance + pos.realized_pnl).max(0.0); if equity_now <= 0.0 || meta.price <= 0.0 { return actions; } ``` ### Этап 3: Чтение сохранённого расчёта из Redis ```rust let _saved_calc = match entry_calculator::load_calculation(redis.clone(), contract).await { Ok(Some(calc)) => Some(calc), Ok(None) => { println!("⚠️ Сохранённый расчёт не найден, пересчитываем..."); None } Err(e) => { println!("❌ Ошибка загрузки расчёта из Redis: {:?}", e); None } }; ``` **Redis ключ:** `hb:entry_calc:{symbol}` ### Этап 4: Расчёт параметров входа ```rust let entry_params = EntryParams { margin_limit_usd: 2.0, // Макс маржа 2$ min_distance_to_liq_pct: 100.0, // DistanceToLIQ ≥ 100% }; let calc = match entry_calculator::calculate_entry(meta, equity_now, Some(entry_params)) { Ok(c) => c, Err(e) => { println!("❌ Пара {} не подходит для входа: {}", contract, e); return actions; // Пустой actions = не входить } }; ``` **Расчёт в entry_calculator:** ``` 1. max_leverage = meta.leverage_max (например, 50x) 2. price_contract = meta.price × meta.contract_multiplier 3. margin_per_contract = price_contract / max_leverage 4. qty_by_margin = floor(margin_limit / margin_per_contract) 5. qty = max(qty_by_margin, min_contracts) 6. qty = min(qty, max_contracts) 7. qty = floor(qty / min_contracts) × min_contracts (кратность) 8. qty = min(qty, 4300) (MARKET лимит) 9. margin_used = qty × margin_per_contract 10. distance_to_liq = (equity - margin_used) / margin_used × 100 11. Итеративное уменьшение qty до distance_to_liq ≥ 100% ``` ### Этап 5: Проверка DistanceToLIQ ≥ 100% ```rust if !calc.meets_liq_threshold { println!("❌ Пара {} не соответствует условиям: DistanceToLIQ {:.2}% < 100%", contract, calc.distance_to_liq); return actions; } ``` ### Этап 6: Сохранение расчёта в Redis ```rust if let Err(e) = entry_calculator::save_calculation(redis.clone(), contract, &calc).await { println!("⚠️ Ошибка сохранения расчёта в Redis: {:?}", e); } ``` **Redis ключ:** `hb:entry_calc:{symbol}` ### Этап 7: Проверка открытых позиций на бирже ```rust match gate.get_positions(contract).await { Ok(positions) => { if let Some(pos_array) = positions.as_array() { for p in pos_array { if let Some(size) = p["size"].as_i64() { if size != 0 { println!("⚠️ НАЙДЕНА ОТКРЫТАЯ ПОЗИЦИЯ на бирже: size={}, contract={}", size, contract); println!("⚠️ Бот НЕ открывает новые позиции. Закрой старые вручную или дай боту управлять."); return actions; } } } } } Err(e) => { println!("⚠️ Не удалось проверить позиции: {}", e); } } ``` **Зачем:** Защита от дублирования позиций (если пользователь открыл вручную) ### Этап 8: Принудительная установка DUAL+CROSS + MAX LEVERAGE ```rust if let Err(e) = ensure_dual_cross_before_trade(gate, contract, calc.leverage).await { println!("❌ КРИТИЧЕСКАЯ ОШИБКА: Не удалось установить DUAL+CROSS: {}", e); println!("❌ ТОРГОВЛЯ ПРЕКРАЩЕНА для защиты депозита!"); return actions; } ``` **Критично:** Без этого позиции открываются в ISOLATED режиме → отдельная маржа → неэффективно! ### Этап 9: Проверка qty ```rust if qty <= 0.0 { return actions; } ``` ### Этап 10: Логирование параметров входа ```rust println!( "🔹 CORE ENTRY: equity={:.2}, price={:.6}, lev={:.1}x (MAX), qty={:.1}, margin={:.2} ({:.2}%), DistanceToLIQ={:.2}%", equity_now, meta.price, calc.leverage, qty, margin, (margin / equity_now * 100.0), calc.distance_to_liq ); ``` ### Этап 11: Генерация действий (actions) ```rust // Leverage уже установлен в ensure_dual_cross_before_trade actions.push(LiveAction::SetBaseQty(qty)); if meta.contract_multiplier > 0.0 { actions.push(LiveAction::SetContractValue(meta.contract_multiplier)); } if meta.chg_pct_24h > 0.0 { actions.push(LiveAction::OpenLong(qty, 0.0)); } else { actions.push(LiveAction::OpenShort(qty, 0.0)); } ``` **Логика направления:** - `chg_pct_24h > 0` → LONG (пара росла за 24ч) - `chg_pct_24h < 0` → SHORT (пара падала за 24ч) --- ## 🔗 Интеграция с другими модулями ### 1. Redis **Читает:** - `hb:entry_calc:{symbol}` - сохранённый расчёт входа **Записывает:** - `hb:entry_calc:{symbol}` - новый расчёт входа ### 2. Gate.io API **Использует:** - `get_positions(contract)` - проверка открытых позиций - `get_dual_positions(contract)` - проверка DUAL режима - `set_dual_mode_enabled(true)` - включение DUAL режима - `set_dual_cross_mode(contract, "CROSS")` - установка CROSS - `update_dual_leverage(contract, leverage)` - установка плеча ### 3. entry_calculator **Использует:** - `calculate_entry()` - расчёт параметров входа - `save_calculation()` - сохранение в Redis - `load_calculation()` - загрузка из Redis ### 4. main.rs / live_step **Вызывается:** - Из `live_step()` когда позиция пустая - В начале каждой сессии LIVE **Возвращает:** - `Vec` - список действий для исполнения - Пустой вектор → не входить → завершить сессию --- ## 📊 Параметры и их источники | Параметр | Значение | Источник | |----------|----------|----------| | `margin_limit_usd` | 2.0 USDT | Hardcoded в EntryParams | | `min_distance_to_liq_pct` | 100.0% | Hardcoded в EntryParams | | `max_leverage` | meta.leverage_max | Gate.io API (TradingMeta) | | `price` | meta.price | Redis / Gate.io API | | `contract_multiplier` | meta.contract_multiplier | Redis / Gate.io API | | `min_contracts` | meta.min_contracts | Gate.io API | | `max_contracts` | meta.max_contracts | Gate.io API | | `qty` | Рассчитывается | entry_calculator | | `margin_used` | Рассчитывается | entry_calculator | | `distance_to_liq` | Рассчитывается | entry_calculator | | `chg_pct_24h` | meta.chg_pct_24h | Redis / Gate.io API | | Направление | LONG/SHORT | chg_pct_24h > 0 ? | --- ## 🔧 Пример работы ### Сценарий 1: Успешный вход в LONG **Входные данные:** ``` contract = "MEMES_USDT" price = 0.00223650 contract_multiplier = 100 leverage_max = 50.0 min_contracts = 1.0 max_contracts = 6500 equity_now = 100.0 USDT chg_pct_24h = +50.0% ``` **Шаги:** 1. ✅ Позиция пустая (no LONG, no SHORT) 2. ✅ Баланс = 100.0 USDT 3. ⚠️ Redis нет saved_calc → пересчитываем 4. 📐 Расчёт в entry_calculator: - price_contract = 0.00223650 × 100 = 0.22365 USDT - margin_per_contract = 0.22365 / 50.0 = 0.004473 USDT - qty_by_margin = floor(2.0 / 0.004473) = 447 - qty = max(447, 1) = 447 - qty = min(447, 6500) = 447 - qty = floor(447 / 1) × 1 = 447 - qty = min(447, 4300) = 447 (MARKET лимит OK) - margin_used = 447 × 0.004473 = 2.0 USDT - distance_to_liq = (100 - 2) / 2 × 100 = 4900% ✅ 5. ✅ DistanceToLIQ = 4900% ≥ 100% 6. 💾 Сохраняем calc в Redis 7. ✅ Позиций на бирже нет 8. 🔧 Установка DUAL+CROSS: - ✅ margin = 0 до (CROSS уже установлен) - ✅ DUAL mode включён - ✅ CROSS установлен - ✅ Leverage 50.0x установлен - ✅ margin = 0 после (подтверждён CROSS) 9. 📊 Лог: `🔹 CORE ENTRY: equity=100.00, price=0.002237, lev=50.0x (MAX), qty=447.0, margin=2.00 (2.00%), DistanceToLIQ=4900.00%` 10. ✅ chg_pct_24h = +50% → LONG 11. 📦 Actions: - `SetBaseQty(447.0)` - `SetContractValue(100.0)` - `OpenLong(447.0, 0.0)` ### Сценарий 2: Пара не подходит (DistanceToLIQ < 100%) **Входные данные:** ``` contract = "DUMB_USDT" price = 0.5 (дорогая монета) contract_multiplier = 1 leverage_max = 10.0 min_contracts = 1.0 equity_now = 100.0 USDT ``` **Шаги:** 1. ✅ Позиция пустая 2. ✅ Баланс = 100.0 USDT 3. 📐 Расчёт в entry_calculator: - price_contract = 0.5 × 1 = 0.5 USDT - margin_per_contract = 0.5 / 10.0 = 0.05 USDT - qty_by_margin = floor(2.0 / 0.05) = 40 - margin_used = 40 × 0.05 = 2.0 USDT - distance_to_liq = (100 - 2) / 2 × 100 = 4900% ✅ 4. ✅ OK (успешный вход) **Но если equity_now = 2.1 USDT:** ``` margin_used = 40 × 0.05 = 2.0 USDT distance_to_liq = (2.1 - 2) / 2 × 100 = 5% ❌ ``` 5. ❌ DistanceToLIQ = 5% < 100% → Пустой actions → Сессия завершена ### Сценарий 3: КРИТИЧЕСКАЯ ОШИБКА - ISOLATED вместо CROSS **Входные данные:** ``` contract = "FAIL_USDT" ``` **Шаги:** 1-7. ✅ Все проверки пройдены 8. 🔧 Установка DUAL+CROSS: - ✅ DUAL mode включён - ❌ CROSS установить НЕ удалось (API ошибка) - ❌ margin = 2.0 после (ISOLATED!) 9. ❌ КРИТИЧЕСКАЯ ОШИБКА: margin != 0 10. ❌ ТОРГОВЛЯ ПРЕКРАЩЕНА для защиты депозита! 11. 📦 Actions: пустой вектор → Сессия завершена --- ## ⚠️ Критические моменты ### 1. DistanceToLIQ ≥ 100% **Зачем:** Защита от ликвидации **Если DistanceToLIQ < 100%:** - ❌ Бот НЕ входит в позицию - ❌ Сессия завершается - ✅ Ищет новую пару **Пример проблемы:** ``` equity = 2.1 USDT margin_used = 2.0 USDT distance_to_liq = 5% Минимальное движение цены на 5% → ЛИКВИДАЦИЯ! ``` ### 2. DUAL+CROSS обязательный! **Зачем:** - **ISOLATED:** Каждая сторона (LONG/SHORT) использует отдельную маржу → неэффективно - **CROSS:** Общая маржа для обеих сторон → можно хеджировать эффективно **Если CROSS не установлен:** - ❌ КРИТИЧЕСКАЯ ОШИБКА - ❌ Торгогля ПРЕКРАЩЕНА - ✅ Защита депозита ### 3. MARKET лимит 4300 контрактов **Ограничение Gate.io:** MARKET ордер не может превышать 4300 контрактов **В коде (строки 154-159):** ```rust const MARKET_ORDER_MAX_QTY: f64 = 4300.0; if qty > MARKET_ORDER_MAX_QTY { println!(" ⚠️ Qty ({:.0}) > MARKET лимит ({:.0}), округляем вниз", qty, MARKET_ORDER_MAX_QTY); qty = (MARKET_ORDER_MAX_QTY / step_contracts).floor() * step_contracts; } ``` ### 4. Проверка открытых позиций **Зачем:** Защита от дублирования **Если позиция уже открыта вручную:** ``` ⚠️ НАЙДЕНА ОТКРЫТАЯ ПОЗИЦИЯ на бирже: size=500, contract=MEMES_USDT ⚠️ Бот НЕ открывает новые позиции. Закрой старые вручную или дай боту управлять. ``` **Actions:** пустой → Сессия завершена --- ## 🔄 Поток данных ``` main.rs ↓ live_step() ↓ CoreStrategy::process_with_real_limits() ↓ ├─→ Redis (load_calculation) │ └─→ hb:entry_calc:{symbol} │ ├─→ entry_calculator::calculate_entry() │ ├─→ TradingMeta (из Redis) │ ├─→ EntryParams (hardcoded) │ └─→ EntryCalculation │ ├─→ Redis (save_calculation) │ └─→ hb:entry_calc:{symbol} │ ├─→ Gate.io API │ ├─→ get_positions() (проверка) │ ├─→ ensure_dual_cross_before_trade() │ │ ├─→ get_dual_positions() (проверка ДО) │ │ ├─→ set_dual_mode_enabled() │ │ ├─→ set_dual_cross_mode() │ │ ├─→ update_dual_leverage() │ │ └─→ get_dual_positions() (проверка ПОСЛЕ) │ └─→ create_market_order() (в main.rs) │ └─→ Vec ├─→ SetBaseQty(qty) ├─→ SetContractValue(multiplier) └─→ OpenLong(qty, price) ИЛИ OpenShort(qty, price) ``` --- ## 📝 Логирование ### Уровни логов: **INFO:** ``` 📐 РАСЧЁТ ВХОДА: Пара: MEMES_USDT Цена монеты: 0.00223650 Монет в контракте (contract_multiplier): 100 Плечо: 50.0x (max из биржи) ... ``` **SUCCESS:** ``` ✅ РАСЧЁТ ВХОДА ЗАВЕРШЁН УСПЕШНО: Qty: 447 контрактов Leverage: 50.0x Margin: 2.000000 USDT DistanceToLIQ: 4900.00% (порог ≥ 100.00%) ``` **WARNING:** ``` ⚠️ Сохранённый расчёт не найден, пересчитываем... ⚠️ Ошибка сохранения расчёта в Redis: ... ⚠️ Не удалось проверить позиции: ... ``` **ERROR:** ``` ❌ Пара DUMB_USDT не подходит для входа: DistanceToLIQ 5.00% < 100% ❌ КРИТИЧЕСКАЯ ОШИБКА: Не удалось установить DUAL+CROSS: ... ❌ КРИТИЧЕСКАЯ ОШИБКА: margin не равен 0 - значит ISOLATED вместо CROSS ``` **ENTRY LOG:** ``` 🔹 CORE ENTRY: equity=100.00, price=0.002237, lev=50.0x (MAX), qty=447.0, margin=2.00 (2.00%), DistanceToLIQ=4900.00% ``` --- ## 🚀 Использование в коде ### Пример вызова в live_step.rs: ```rust // Проверка пустой позиции if pos.long_qty == 0.0 && pos.short_qty == 0.0 { // Первый вход через CoreStrategy let core_actions = core_strategy.process_with_real_limits( redis.clone(), gate, pos, meta, current_balance, ).await; if !core_actions.is_empty() { return core_actions; // Исполняем действия } else { // Пара не подходит → завершаем сессию println!("⚠️ Пара не подходит для входа"); session.end(SessionEndReason::PairNotSuitable); return vec![]; } } ``` --- ## 📊 Важные формулы ### 1. DistanceToLIQ ``` DistanceToLIQ = (Equity - MarginUsed) / MarginUsed × 100 ``` **Пример:** ``` Equity = 100.0 USDT MarginUsed = 2.0 USDT DistanceToLIQ = (100 - 2) / 2 × 100 = 4900% ``` **Значение:** Цена может упасть/расти на 4900% до ликвидации ### 2. Price Contract ``` Price_contract = Price_coin × Contract_multiplier ``` **Пример:** ``` Price_coin = 0.00223650 USDT (1 монета) Contract_multiplier = 100 (100 монет в 1 контракте) Price_contract = 0.00223650 × 100 = 0.22365 USDT (1 контракт) ``` ### 3. Margin per Contract ``` Margin_per_contract = Price_contract / Leverage ``` **Пример:** ``` Price_contract = 0.22365 USDT Leverage = 50.0x Margin_per_contract = 0.22365 / 50.0 = 0.004473 USDT ``` **Значение:** На 1 контракт нужно 0.004473 USDT маржи при 50x плече --- ## 🔍 Проверка работы ### Проверка 1: Правильная работа DUAL+CROSS 1. Запустить бота с пустой позицией 2. Проверить лог: ``` 🔧 === ENSURE DUAL+CROSS ДО ТОРГОВЛИ === 📊 A) ТЕКУЩЕЕ СОСТОЯНИЕ ДО: contract=MEMES_USDT, mode=dual, margin=0 ✅ CROSS уже установлен! (margin=0) ... 📊 E) СОСТОЯНИЕ ПОСЛЕ (критическая проверка): contract=MEMES_USDT, mode=dual, margin=0 ✅ ПОДТВЕРЖДЕНО: Контракт в CROSS режиме! (margin=0) ``` 3. Проверить вручную в Gate.io: - Futures → Positions - Убедиться что mode = "dual_cross" ### Проверка 2: Проверка DistanceToLIQ 1. Проверить лог: ``` ✅ РАСЧЁТ ВХОДА ЗАВЕРШЁН УСПЕШНО: Qty: 447 контрактов Leverage: 50.0x Margin: 2.000000 USDT DistanceToLIQ: 4900.00% (порог ≥ 100.00%) ``` 2. Проверить вручную: - Futures → Positions - Убедиться что "Distance to liquidation" ≥ 100% ### Проверка 3: Redis integration 1. Проверить что расчёт сохранён: ```bash redis-cli GET "hb:entry_calc:MEMES_USDT" | jq ``` 2. Ожидаемый JSON: ```json { "qty": 447.0, "leverage": 50.0, "margin_used": 2.0, "distance_to_liq": 4900.0, "meets_liq_threshold": true } ``` --- ## 📚 Связанные файлы | Файл | Связь | |------|-------| | `src/live/entry_calculator.rs` | Расчёт параметров входа | | `src/live/meta.rs` | TradingMeta структура | | `src/live/actions.rs` | LiveAction enum | | `src/gate.rs` | Gate.io API клиент | | `src/redis.rs` | Redis клиент | | `src/main.rs` | Вызов CoreStrategy | | `src/live/step.rs` | live_step orchestration | --- ## 🎯 Резюме **Что делает CoreStrategy:** 1. ✅ Входит в позицию ТОЛЬКО когда позиция пустая 2. ✅ Проверяет DistanceToLIQ ≥ 100% 3. ✅ Принудительно устанавливает DUAL+CROSS с проверкой 4. ✅ Ограничивает маржу до 2$ 5. ✅ Использует МАКСИМАЛЬНОЕ плечо из биржи 6. ✅ Проверяет что нет открытых позиций на бирже 7. ✅ Определяет направление по chg_pct_24h 8. ✅ Генерирует actions для исполнения **Если условия не выполнены:** - ❌ Возвращает пустой actions[] - ❌ main.rs завершает сессию - ✅ Ищет новую пару **Критичность:** 🔥🔥🔥 Без правильной работы CoreStrategy бот может: - Открыть позицию в ISOLATED режиме (отдельная маржа) - Войти с недостаточным DistanceToLIQ (риск ликвидации) - Дублировать позиции --- **Дата создания:** 2026-02-22 **Автор:** Claude Code Assistant **Версия:** 1.0