← Назад к главному
# step.rs - Модуль оркестрации LIVE движка
## 📋 Назначение модуля
`step.rs` оркестрирует выполнение LIVE движка, вызывая стратегии и исполняя их действия через Gate.io API.
**Основная логика:**
1. **Обновление цены** из TradingMeta
2. **Risk check** - проверка рисков
3. **Вызов стратегий** (Core, Single, Both)
4. **Исполнение действий** через Gate.io API
5. **Логирование** в Redis и файлы
6. **Обновление состояния позиции**
**Используется в:**
- `main.rs` - главный цикл сессий (вызывает live_step)
---
## 🏗️ Структура модуля
### Структура `LiveStepContext`
**Строки:** 22-38
```rust
pub struct LiveStepContext {
pub machine: PositionMachine, // Фазовая машина позиций
pub core: CoreStrategy, // Стратегия первого входа
pub single: SingleStrategy, // Стратегия односторонней позиции
pub both: BothStrategy, // Стратегия BOTH позиции
}
```
**Назначение:** Хранение всех стратегий и фазовой машины
**Поля и их описание:**
| Поле | Тип | Описание |
|------|-----|----------|
| `machine` | PositionMachine | Фазовая машина (Flat, Single, Both, Boosted, WindDown, ExitReady) |
| `core` | CoreStrategy | Стратегия первого входа (пустая позиция) |
| `single` | SingleStrategy | Стратегия односторонней позиции |
| `both` | BothStrategy | Стратегия BOTH позиции |
---
## 🔄 Основные функции
### Функция `LiveStepContext::new()`
**Строки:** 30-37
**Сигнатура:**
```rust
pub fn new(cfg: &Config) -> Self
```
**Возвращает:** Новый `LiveStepContext` с инициализированными стратегиями
**Алгоритм:**
```rust
pub fn new(cfg: &Config) -> Self {
Self {
machine: PositionMachine::new(),
core: CoreStrategy::new(cfg),
single: SingleStrategy::new(),
both: BothStrategy::new(),
}
}
```
**Пример:**
```rust
let cfg = Config::from_file("config.toml").unwrap();
let ctx = LiveStepContext::new(&cfg);
ctx = LiveStepContext {
machine: PositionMachine { phase: Single, cut_count: 0, boosted_once: false },
core: CoreStrategy { },
single: SingleStrategy { did_cut_once: false, first_rebuild_price: 0.0 },
both: BothStrategy { },
}
```
---
### Функция `live_step()`
**Строки:** 43-145
**Сигнатура:**
```rust
pub async fn live_step(
redis: Arc,
ctx: &mut LiveStepContext,
pos: &mut PositionSnapshot,
meta_prev: &TradingMeta,
meta_now: &TradingMeta,
current_balance: f64,
logger: &mut crate::logger::Logger,
) -> Result<()>
```
**Возвращает:**
- `Ok(())` - шаг выполнен успешно
- `Err` - ошибка
**Алгоритм (8 этапов):**
---
#### Этап 1: Обновление цены
**Строки:** 55-56
```rust
pos.update_price(meta_now.price);
pos.update_price(meta_now.price);
```
**Назначение:** Обновление цены дважды (возможно для синхронизации)
**Пример:**
```
meta_now.price = 0.002060
pos.update_price(0.002060);
// pos.last_price = 0.002060
```
---
#### Этап 2: Risk check
**Строка:** 58
```rust
let _risk = risk_check(pos, meta_prev, meta_now);
```
**Назначение:** Проверка рисков (критичные изменения цены, маржа и т.д.)
**Пример:**
```rust
risk_check(pos, meta_prev, meta_now);
// Проверяет: резкое изменение цены, недостаточная маржа, и т.д.
// Если риск → возвращает ошибку → сессия завершается
```
---
#### Этап 3: CoreStrategy - первый вход
**Строки:** 60-67
```rust
// FIX: CORE работает только если позиция полностью пустая
if pos.long_qty == 0.0 && pos.short_qty == 0.0 {
let gate = GateClient::new();
let core_actions =
ctx.core.process_with_real_limits(redis.clone(), &gate, pos, meta_now, current_balance).await;
create_real_orders(redis.clone(), pos, &core_actions, &gate, logger).await;
}
```
**Условия:**
- `long_qty == 0` **и** `short_qty == 0` (пустая позиция)
**Действия:**
1. Создать Gate.io клиент
2. Вызвать `CoreStrategy::process_with_real_limits()`
3. Вызвать `create_real_orders()` для исполнения
**Пример:**
```
long_qty = 0, short_qty = 0 ✅
ctx.core.process_with_real_limits(...) -> Vec:
- SetBaseQty(1000)
- SetContractValue(100)
- SetLeverage(50.0)
- OpenLong(1000, 0.002000)
create_real_orders(...) → исполнение через Gate.io API
```
---
#### Этап 4: SingleStrategy - управление односторонней позицией
**Строки:** 69-94
```rust
// 🔥 SingleStrategy теперь возвращает actions!
let gate = GateClient::new();
let single_actions = ctx.single.process(pos, meta_now);
if !single_actions.is_empty() {
create_real_orders(redis.clone(), pos, &single_actions, &gate, logger).await;
}
```
**Условия:**
- Односторонняя позиция (LONG или SHORT)
**Действия:**
1. Вызвать `SingleStrategy::process()`
2. Если есть действия → `create_real_orders()`
**Пример:**
```
long_qty = 1000, short_qty = 0
ctx.single.process(pos, meta_now) -> Vec:
- CloseLong(500, 0.002060, true)
- OpenLong(500, 0.002060)
create_real_orders(...) → исполнение через Gate.io API
```
---
#### Этап 5: Определение entry_price для хеджа
**Строки:** 76-84
```rust
let entry_for_hedge = if pos.initial_entry_price > 0.0 {
pos.initial_entry_price
} else if pos.long_qty > 0.0 {
pos.entry_long
} else if pos.short_qty > 0.0 {
pos.entry_short
} else {
0.0
};
```
**Логика:**
1. Если `initial_entry_price > 0` → используем `initial_entry_price`
2. Иначе если `long_qty > 0` → используем `entry_long`
3. Иначе если `short_qty > 0` → используем `entry_short`
4. Иначе → `0.0`
**Примеры:**
```
Сценарий 1 (первый вход):
initial_entry_price = 0.002000
entry_for_hedge = 0.002000
Сценарий 2 (LONG уже открыт):
long_qty = 1000, short_qty = 0
entry_long = 0.002000
entry_for_hedge = 0.002000
Сценарий 3 (SHORT уже открыт):
long_qty = 0, short_qty = 1000
entry_short = 0.002000
entry_for_hedge = 0.002000
```
---
#### Этап 6: HedgeGuard - экстренный хедж
**Строки:** 86-94
```rust
// 🔥 HedgeGuard теперь возвращает actions!
if entry_for_hedge > 0.0 {
if let Some(sig) = HedgeGuard::check(pos, meta_now, entry_for_hedge) {
let hedge_actions = HedgeGuard::execute(pos, sig, meta_now.price);
if !hedge_actions.is_empty() {
create_real_orders(redis.clone(), pos, &hedge_actions, &gate, logger).await;
}
}
}
```
**Условия:**
- `entry_for_hedge > 0` (есть позиция)
**Действия:**
1. Вызвать `HedgeGuard::check()`
2. Если есть сигнал → `HedgeGuard::execute()`
3. Если есть действия → `create_real_orders()`
**Пример:**
```
long_qty = 1000, short_qty = 0
entry_long = 0.002000
current_price = 0.001970 (delta = -1.5%)
HedgeGuard::check(...) -> Some(HedgeSignal):
- long_qty: 1000
- short_qty: 1000
- add_long: 0.0
- add_short: 1000
- reason: "Auto-hedge LONG: delta -1.5%, add SHORT 1000"
HedgeGuard::execute(...) -> Vec:
- OpenShort(1000, 0.001970)
create_real_orders(...) → исполнение через Gate.io API
```
---
#### Этап 7: BothStrategy - управление BOTH позицией
**Строки:** 96-140
```rust
ctx.machine.update_phase(pos);
let both_actions = ctx.both.process(pos, meta_now, &mut ctx.machine);
if !both_actions.is_empty() {
create_real_orders(redis.clone(), pos, &both_actions, &gate, logger).await;
// 🔥 Логируем детальное состояние ПОСЛЕ выполнения действий
if pos.long_qty > 0.0 && pos.short_qty > 0.0 {
let cv = pos.contract_value.max(1.0);
let price = meta_now.price;
let pnl_long = if pos.entry_long > 0.0 {
pos.long_qty * (price - pos.entry_long) * cv
} else {
0.0
};
let pnl_short = if pos.entry_short > 0.0 {
pos.short_qty * (pos.entry_short - price) * cv
} else {
0.0
};
let notional_long = pos.long_qty * pos.entry_long * cv;
let notional_short = pos.short_qty * pos.entry_short * cv;
let pnl_long_pct = if notional_long > 0.0 {
(pnl_long / notional_long) * 100.0
} else {
0.0
};
let pnl_short_pct = if notional_short > 0.0 {
(pnl_short / notional_short) * 100.0
} else {
0.0
};
let notional_long = pos.long_qty * pos.entry_long * cv;
let notional_short = pos.short_qty * pos.entry_short * cv;
let pnl_long_pct = if notional_long > 0.0 {
(pnl_long / notional_long) * 100.0
} else {
0.0
};
let pnl_short_pct = if notional_short > 0.0 {
(pnl_short / notional_short) * 100.0
} else {
0.0
};
println!("📊 СТАТУС ПОСЛЕ ДЕЙСТВИЙ:");
println!(" LONG: {:+.2}% (qty={:.0}, entry={:.6}, realized={:.4})", pnl_long_pct, pos.long_qty, pos.entry_long, pos.realized_pnl);
println!(" SHORT: {:+.2}% (qty={:.0}, entry={:.6})", pnl_short_pct, pos.short_qty, pos.entry_short);
println!(" mode={:?}", pos.mode);
} else {
println!("📊 СТАТУС ПОСЛЕ ДЕЙСТВИЙ: LONG={} SHORT={} mode={:?} realized_pnl={:.4}",
pos.long_qty, pos.short_qty, pos.mode, pos.realized_pnl);
}
}
```
**Условия:** Обе стороны активны (или одна после закрытия другой)
**Действия:**
1. Обновить фазу: `machine.update_phase(pos)`
2. Вызвать `BothStrategy::process()`
3. Если есть действия → `create_real_orders()`
4. Логировать детальное состояние
**Пример:**
```
long_qty = 1000, short_qty = 1000
ctx.both.process(pos, meta_now, &mut machine) -> Vec:
- CloseLong(1000, 0.002060, true)
- CloseShort(500, 0.002060, false)
- OpenLong(1000, 0.002060)
create_real_orders(...) → исполнение через Gate.io API
После:
long_qty = 1000, short_qty = 500
Лог:
📊 СТАТУС ПОСЛЕ ДЕЙСТВИЙ:
LONG: +2.50% (qty=1000.0, entry=0.002060, realized=0.0300)
SHORT: -2.50% (qty=500.0, entry=0.002000)
mode=Both
```
---
#### Этап 8: Запись лога в Redis
**Строка:** 142
```rust
write_live_log(redis, pos, meta_now, &ctx.machine).await?;
```
**Назначение:** Запись текущего состояния в Redis
**Пример:**
```rust
write_live_log(redis, pos, meta_now, &machine).await;
// Записывает состояние позиции в Redis:
// - hb:log:{symbol}: JSON с деталями
```
---
### Функция `create_real_orders()`
**Строки:** 150-480
**Сигнатура:**
```rust
async fn create_real_orders(
_redis: Arc,
pos: &mut PositionSnapshot,
actions: &[LiveAction],
gate: &GateClient,
logger: &mut crate::logger::Logger,
)
```
**Назначение:** Исполнение действий через Gate.io API (MARKET ордера)
**Алгоритм (4 фазы):**
---
#### Фаза 0: Rate limiting
**Строки:** 160-170
```rust
static LAST_ORDER_TIME: Mutex