← Назад к главному
# state.rs - Модуль состояния позиции ## 📋 Назначение модуля `PositionSnapshot` хранит текущее состояние позиции и предоставляет методы для расчёта PnL, маржи и управления позицией. **Основная логика:** 1. **Хранение состояния позиции** (количество, entry цены, PnL, маржа) 2. **Расчёт PnL** (реализованный и нереализованный) 3. **Расчёт маржи** (long_margin, short_margin, used_margin) 4. **Управление позицией** (open_long, open_short, close_long, close_short) 5. **Определение режима** (Flat, Single, Both, Recovery) 6. **Проверка хеджа** (is_hedged, delta) **Используется во всех LIVE модулях:** - `strategy_core.rs` - вход в позицию - `strategy_single.rs` - управление односторонней позицией - `strategy_both.rs` - управление BOTH позицией - `volatility_cycle.rs` - резки - `hedge_guard.rs` - экстренный хедж - `position_machine.rs` - фазовое управление --- ## 🏗️ Структура модуля ### Enum `LiveMode` **Строки:** 3-9 ```rust #[derive(Clone, Debug, Serialize, Deserialize)] pub enum LiveMode { Flat, // Пустая позиция (нет LONG и нет SHORT) Single, // Односторонняя позиция (LONG или SHORT) Both, // BOTH режим (LONG + SHORT) Recovery, // Режим восстановления (не используется) } ``` **Назначение:** Определяет текущий режим позиции **Значения:** - `Flat` → `long_qty == 0` **и** `short_qty == 0` - `Single` → `long_qty > 0` **xor** `short_qty > 0` - `Both` → `long_qty > 0` **и** `short_qty > 0` - `Recovery` → (зарезервировано для будущего использования) --- ### Структура `PositionSnapshot` **Строки:** 12-48 ```rust #[derive(Clone, Debug, Serialize, Deserialize)] pub struct PositionSnapshot { pub symbol: String, // Символ контракта pub long_qty: f64, // Количество LONG контрактов pub short_qty: f64, // Количество SHORT контрактов pub entry_long: f64, // Entry цена LONG pub entry_short: f64, // Entry цена SHORT pub initial_entry_price: f64, // Цена первого входа pub hedge_entry_price: f64, // Цена хеджа pub first_side: String, // Первая сторона ("LONG" или "SHORT") pub entry_leverage: f64, // Плечо при входе pub contract_value: f64, // Значение контракта pub long_margin: f64, // Маржа LONG pub short_margin: f64, // Маржа SHORT pub used_margin: f64, // Использованная маржа pub last_price: f64, // Последняя цена pub realized_pnl: f64, // Реализованный PnL pub cut_streak: u32, // Счётчик резок подряд pub base_qty: f64, // Базовое количество (зеркальная часть) pub min_contract_qty: f64, // Минимальное количество контрактов pub last_entry_time: i64, // Время последнего входа (Unix timestamp) pub hedge_cooldown_sec: i64, // Cooldown период после входа (секунды) pub last_cooldown_log_time: i64, // Время последнего лога cooldown (чтобы не спамить) pub last_profit_rebuy_price: f64, // Цена последней перезакупки прибыльной стороны (для разворота -1%) pub transitioning_to_single_from_both: bool, // Флаг: переходим из BOTH в Single (после полной резки убыточной стороны) pub mode: LiveMode, // Текущий режим (Flat, Single, Both, Recovery) } ``` **Назначение:** Хранение состояния позиции **Поля и их описание:** | Поле | Тип | Описание | |------|-----|----------| | `symbol` | String | Символ контракта (например, "MEMES_USDT") | | `long_qty` | f64 | Количество LONG контрактов | | `short_qty` | f64 | Количество SHORT контрактов | | `entry_long` | f64 | Entry цена LONG | | `entry_short` | f64 | Entry цена SHORT | | `initial_entry_price` | f64 | Цена первого входа | | `hedge_entry_price` | f64 | Цена хеджа | | `first_side` | String | Первая сторона ("LONG" или "SHORT") | | `entry_leverage` | f64 | Плечо при входе | | `contract_value` | f64 | Значение контракта | | `long_margin` | f64 | Маржа LONG | | `short_margin` | f64 | Маржа SHORT | | `used_margin` | f64 | Использованная маржа | | `last_price` | f64 | Последняя цена | | `realized_pnl` | f64 | Реализованный PnL | | `cut_streak` | u32 | Счётчик резок подряд | | `base_qty` | f64 | Базовое количество (зеркальная часть) | | `min_contract_qty` | f64 | Минимальное количество контрактов | | `last_entry_time` | i64 | Время последнего входа | | `hedge_cooldown_sec` | i64 | Cooldown период (секунды) | | `last_cooldown_log_time` | i64 | Время последнего лога cooldown | | `last_profit_rebuy_price` | f64 | Цена последней перезакупки прибыльной стороны | | `transitioning_to_single_from_both` | bool | Флаг перехода из BOTH в Single | | `mode` | LiveMode | Текущий режим | --- ## 🔄 Основные методы ### Метод `new()` **Строки:** 51-88 **Сигнатура:** ```rust pub fn new(symbol: &str) -> Self ``` **Возвращает:** Новый `PositionSnapshot` с начальными значениями **Начальные значения:** ```rust PositionSnapshot { symbol: symbol.to_string(), long_qty: 0.0, short_qty: 0.0, entry_long: 0.0, entry_short: 0.0, initial_entry_price: 0.0, hedge_entry_price: 0.0, first_side: "".to_string(), entry_leverage: 0.0, contract_value: 1.0, long_margin: 0.0, short_margin: 0.0, used_margin: 0.0, last_price: 0.0, realized_pnl: 0.0, cut_streak: 0, base_qty: 0.0, min_contract_qty: 1.0, last_entry_time: 0, hedge_cooldown_sec: 5, // 5 секунд cooldown по умолчанию last_cooldown_log_time: 0, // Инициализация last_profit_rebuy_price: 0.0, // Инициализация transitioning_to_single_from_both: false, // Инициализация mode: LiveMode::Flat, } ``` **Пример:** ```rust let pos = PositionSnapshot::new("MEMES_USDT"); pos = PositionSnapshot { symbol: "MEMES_USDT", long_qty: 0.0, short_qty: 0.0, ... mode: Flat, } ``` --- ### Метод `update_price()` **Строки:** 90-92 **Сигнатура:** ```rust pub fn update_price(&mut self, px: f64) ``` **Назначение:** Обновление последней цены **Алгоритм:** ```rust self.last_price = px; ``` **Пример:** ```rust pos.update_price(0.002060); // pos.last_price = 0.002060 ``` --- ### Метод `unrealized()` **Строки:** 94-96 **Сигнатура:** ```rust pub fn unrealized(&self) -> f64 ``` **Возвращает:** Нереализованный PnL по текущей цене **Алгоритм:** ```rust self.unrealized_at_price(self.last_price) ``` **Пример:** ```rust pos.last_price = 0.002060; pos.long_qty = 1000; pos.entry_long = 0.002000; pos.short_qty = 0; pos.unrealized() = 0.06 USDT ``` --- ### Метод `unrealized_at_price()` **Строки:** 98-110 **Сигнатура:** ```rust pub fn unrealized_at_price(&self, price: f64) -> f64 ``` **Возвращает:** Нереализованный PnL по указанной цене **Алгоритм:** ```rust pub fn unrealized_at_price(&self, price: f64) -> f64 { let mut p = 0.0; if self.long_qty > 0.0 && self.entry_long > 0.0 { p += (price - self.entry_long) * self.long_qty * self.contract_value; } if self.short_qty > 0.0 && self.entry_short > 0.0 { p += (self.entry_short - price) * self.short_qty * self.contract_value; } p } ``` **Формулы:** - `pnl_long = (price - entry_long) × long_qty × contract_value` - `pnl_short = (entry_short - price) × short_qty × contract_value` - `pnl_total = pnl_long + pnl_short` **Пример 1 (LONG):** ``` long_qty = 1000 entry_long = 0.002000 price = 0.002060 contract_value = 1.0 pnl_long = (0.002060 - 0.002000) × 1000 × 1.0 = 0.06 USDT pnl_short = 0 pnl_total = 0.06 USDT ✅ ``` **Пример 2 (SHORT):** ``` short_qty = 1000 entry_short = 0.002000 price = 0.001940 contract_value = 1.0 pnl_long = 0 pnl_short = (0.002000 - 0.001940) × 1000 × 1.0 = 0.06 USDT pnl_total = 0.06 USDT ✅ ``` **Пример 3 (BOTH):** ``` long_qty = 1000 entry_long = 0.002000 short_qty = 1000 entry_short = 0.002000 price = 0.002060 contract_value = 1.0 pnl_long = (0.002060 - 0.002000) × 1000 × 1.0 = 0.06 USDT pnl_short = (0.002000 - 0.002060) × 1000 × 1.0 = -0.06 USDT pnl_total = 0.06 + (-0.06) = 0.0 USDT ``` --- ### Метод `total_pnl()` **Строки:** 112-114 **Сигнатура:** ```rust pub fn total_pnl(&self) -> f64 ``` **Возвращает:** Общий PnL (реализованный + нереализованный) **Алгоритм:** ```rust self.realized_pnl + self.unrealized() ``` **Формула:** ``` total_pnl = realized_pnl + unrealized_pnl ``` **Пример:** ``` realized_pnl = 0.03 USDT (фиксирована прибыль) unrealized_pnl = 0.02 USDT (текущая прибыль) total_pnl = 0.03 + 0.02 = 0.05 USDT ✅ ``` --- ### Метод `total_pnl_at_price()` **Строки:** 116-118 **Сигнатура:** ```rust pub fn total_pnl_at_price(&self, price: f64) -> f64 ``` **Возвращает:** Общий PnL по указанной цене **Алгоритм:** ```rust self.realized_pnl + self.unrealized_at_price(price) ``` **Формула:** ``` total_pnl = realized_pnl + unrealized_pnl_at_price ``` **Пример:** ``` realized_pnl = 0.03 USDT price = 0.002060 unrealized_pnl_at_price = 0.06 USDT total_pnl = 0.03 + 0.06 = 0.09 USDT ✅ ``` --- ### Метод `pnl_long()` **Строки:** 120-126 **Сигнатура:** ```rust pub fn pnl_long(&self) -> f64 ``` **Возвращает:** PnL LONG по текущей цене **Алгоритм:** ```rust if self.long_qty > 0.0 && self.entry_long > 0.0 { (self.last_price - self.entry_long) * self.long_qty * self.contract_value } else { 0.0 } ``` **Формула:** ``` pnl_long = (last_price - entry_long) × long_qty × contract_value ``` **Пример:** ``` long_qty = 1000 entry_long = 0.002000 last_price = 0.002060 contract_value = 1.0 pnl_long = (0.002060 - 0.002000) × 1000 × 1.0 = 0.06 USDT ✅ ``` --- ### Метод `pnl_short()` **Строки:** 128-134 **Сигнатура:** ```rust pub fn pnl_short(&self) -> f64 ``` **Возвращает:** PnL SHORT по текущей цене **Алгоритм:** ```rust if self.short_qty > 0.0 && self.entry_short > 0.0 { (self.entry_short - self.last_price) * self.short_qty * self.contract_value } else { 0.0 } ``` **Формула:** ``` pnl_short = (entry_short - last_price) × short_qty × contract_value ``` **Пример:** ``` short_qty = 1000 entry_short = 0.002000 last_price = 0.001940 contract_value = 1.0 pnl_short = (0.002000 - 0.001940) × 1000 × 1.0 = 0.06 USDT ✅ ``` --- ### Метод `unrealized_long()` **Строки:** 136-142 **Сигнатура:** ```rust pub fn unrealized_long(&self) -> f64 ``` **Возвращает:** Нереализованный PnL LONG по текущей цене **Алгоритм:** ```rust if self.long_qty > 0.0 && self.entry_long > 0.0 { (self.last_price - self.entry_long) * self.long_qty * self.contract_value } else { 0.0 } ``` **Формула:** Та же что и `pnl_long()` --- ### Метод `unrealized_short()` **Строки:** 144-150 **Сигнатура:** ```rust pub fn unrealized_short(&self) -> f64 ``` **Возвращает:** Нереализованный PnL SHORT по текущей цене **Алгоритм:** ```rust if self.short_qty > 0.0 && self.entry_short > 0.0 { (self.entry_short - self.last_price) * self.short_qty * self.contract_value } else { 0.0 } ``` **Формула:** Та же что и `pnl_short()` --- ### Метод `delta()` **Строки:** 152-154 **Сигнатура:** ```rust pub fn delta(&self) -> f64 ``` **Возвращает:** Разница между LONG и SHORT (net позиция) **Алгоритм:** ```rust self.long_qty - self.short_qty ``` **Формула:** ``` delta = long_qty - short_qty ``` **Примеры:** ``` long_qty = 1000, short_qty = 0 delta = 1000 - 0 = 1000 (LONG больше) long_qty = 0, short_qty = 1000 delta = 0 - 1000 = -1000 (SHORT больше) long_qty = 1000, short_qty = 1000 delta = 1000 - 1000 = 0 (зеркальный хедж) long_qty = 1200, short_qty = 1000 delta = 1200 - 1000 = 200 (LONG больше) ``` --- ### Метод `is_hedged()` **Строки:** 156-158 **Сигнатура:** ```rust pub fn is_hedged(&self) -> bool ``` **Возвращает:** `true` если позиция полностью захеджирована (зеркальный) **Алгоритм:** ```rust (self.long_qty - self.short_qty).abs() < f64::EPSILON ``` **Логика:** - Если `|long_qty - short_qty| < epsilon` → зеркальный хедж - `epsilon` → очень маленькое число (например, 1e-10) **Примеры:** ``` long_qty = 1000, short_qty = 1000 is_hedged() = true ✅ (зеркальный) long_qty = 1200, short_qty = 1000 is_hedged() = false ❌ (не зеркальный) long_qty = 0, short_qty = 0 is_hedged() = true ✅ (пусто, но формально зеркальный) ``` --- ### Метод `recalc_margins()` **Строки:** 160-181 **Сигнатура:** ```rust pub fn recalc_margins(&mut self) ``` **Назначение:** Пересчёт маржи (long_margin, short_margin, used_margin) **Алгоритм:** ```rust if self.entry_leverage > 0.0 { if self.long_qty > 0.0 && self.entry_long > 0.0 { let nominal = self.long_qty * self.entry_long * self.contract_value; self.long_margin = nominal / self.entry_leverage; } else { self.long_margin = 0.0; } if self.short_qty > 0.0 && self.entry_short > 0.0 { let nominal = self.short_qty * self.entry_short * self.contract_value; self.short_margin = nominal / self.entry_leverage; } else { self.short_margin = 0.0; } } else { self.long_margin = 0.0; self.short_margin = 0.0; } self.used_margin = self.long_margin + self.short_margin; ``` **Формулы:** ``` long_margin = (long_qty × entry_long × contract_value) / entry_leverage short_margin = (short_qty × entry_short × contract_value) / entry_leverage used_margin = long_margin + short_margin ``` **Пример:** ``` long_qty = 1000 entry_long = 0.002000 entry_leverage = 50.0 contract_value = 1.0 nominal = 1000 × 0.002000 × 1.0 = 2.0 USDT long_margin = 2.0 / 50.0 = 0.04 USDT short_qty = 0 short_margin = 0.0 used_margin = 0.04 + 0.0 = 0.04 USDT ``` --- ### Метод `open_long()` **Строки:** 183-219 **Сигнатура:** ```rust pub fn open_long(&mut self, qty: f64, price: f64) ``` **Назначение:** Открытие LONG позиции **Алгоритм (8 этапов):** #### Этап 1: Проверка qty ```rust if qty <= 0.0 { return; } ``` --- #### Этап 2: Первый вход или добавление ```rust if self.long_qty == 0.0 { self.entry_long = price; self.long_qty = qty; } else { let prev_qty = self.long_qty; let prev_entry = self.entry_long; let new_qty = prev_qty + qty; let new_entry = (prev_entry * prev_qty + price * qty) / new_qty; self.long_qty = new_qty; self.entry_long = new_entry; } ``` **Логика:** - Если `long_qty == 0` → первый вход: - `entry_long = price` - `long_qty = qty` - Иначе → добавление (dollar-cost averaging): - `new_entry = (prev_entry × prev_qty + price × qty) / new_qty` - Средняя цена **Пример 1 (Первый вход):** ``` qty = 1000 price = 0.002000 long_qty == 0 ✅ entry_long = 0.002000 long_qty = 1000 ``` **Пример 2 (Добавление):** ``` prev_qty = 1000 prev_entry = 0.002000 qty = 500 price = 0.002060 new_qty = 1000 + 500 = 1500 new_entry = (0.002000 × 1000 + 0.002060 × 500) / 1500 = (2.0 + 1.03) / 1500 = 3.03 / 1500 = 0.002020 entry_long = 0.002020 (средняя цена) long_qty = 1500 ``` --- #### Этап 3: Установка base_qty ```rust if self.base_qty <= 0.0 { self.base_qty = self.long_qty; } ``` --- #### Этап 4: Установка first_side ```rust if self.first_side.is_empty() { self.first_side = "LONG".into(); self.initial_entry_price = price; } ``` --- #### Этап 5: Определение режима ```rust self.mode = if self.short_qty > 0.0 { LiveMode::Both } else { LiveMode::Single }; ``` --- #### Этап 6: Обновление времени входа ```rust self.last_entry_time = chrono::Utc::now().timestamp(); ``` --- #### Этап 7: Пересчёт маржи ```rust self.recalc_margins(); ``` --- ### Метод `open_short()` **Строки:** 221-257 **Сигнатура:** ```rust pub fn open_short(&mut self, qty: f64, price: f64) ``` **Назначение:** Открытие SHORT позиции **Алгоритм:** Аналогичен `open_long()` для SHORT **Пример:** ``` qty = 1000 price = 0.002000 entry_short = 0.002000 short_qty = 1000 base_qty = 1000 first_side = "SHORT" mode = Single ``` --- ### Метод `close_long()` **Строки:** 259-284 **Сигнатура:** ```rust pub fn close_long(&mut self, qty: f64) ``` **Назначение:** Закрытие LONG позиции **Алгоритм (5 этапов):** #### Этап 1: Проверка qty ```rust if qty <= 0.0 || self.long_qty <= 0.0 { return; } ``` --- #### Этап 2: Полное закрытие или частичное ```rust if qty >= self.long_qty { self.long_qty = 0.0; self.entry_long = 0.0; self.long_margin = 0.0; } else { let prev_qty = self.long_qty; let ratio = (prev_qty - qty) / prev_qty; self.long_qty -= qty; self.long_margin *= ratio.max(0.0); } ``` **Логика:** - Если `qty >= long_qty` → полное закрытие: - `long_qty = 0` - `entry_long = 0` - `long_margin = 0` - Иначе → частичное закрытие: - `new_qty = long_qty - qty` - `ratio = new_qty / prev_qty` - `new_margin = old_margin × ratio` **Пример 1 (Полное закрытие):** ``` long_qty = 1000 qty = 1000 qty >= long_qty? 1000 >= 1000? ✅ long_qty = 0 entry_long = 0 long_margin = 0 ``` **Пример 2 (Частичное закрытие):** ``` long_qty = 1000 long_margin = 0.04 USDT qty = 500 qty >= long_qty? 500 >= 1000? ❌ new_qty = 1000 - 500 = 500 ratio = 500 / 1000 = 0.5 new_margin = 0.04 × 0.5 = 0.02 USDT long_qty = 500 long_margin = 0.02 USDT ``` --- #### Этап 3: Пересчёт маржи ```rust self.recalc_margins(); ``` --- #### Этап 4: Определение режима ```rust self.mode = if self.long_qty > 0.0 && self.short_qty > 0.0 { LiveMode::Both } else if self.long_qty > 0.0 || self.short_qty > 0.0 { LiveMode::Single } else { LiveMode::Flat }; ``` --- ### Метод `close_short()` **Строки:** 286-311 **Сигнатура:** ```rust pub fn close_short(&mut self, qty: f64) ``` **Назначение:** Закрытие SHORT позиции **Алгоритм:** Аналогичен `close_long()` для SHORT --- ### Метод `close_all_at_price()` **Строки:** 313-334 **Сигнатура:** ```rust pub fn close_all_at_price(&mut self, price: f64) ``` **Назначение:** Полное закрытие всех позиций по указанной цене **Алгоритм:** ```rust pub fn close_all_at_price(&mut self, price: f64) { let cv = self.contract_value.max(1.0); if self.long_qty > 0.0 && self.entry_long > 0.0 { self.realized_pnl += (price - self.entry_long) * self.long_qty * cv; } if self.short_qty > 0.0 && self.entry_short > 0.0 { self.realized_pnl += (self.entry_short - price) * self.short_qty * cv; } self.long_qty = 0.0; self.short_qty = 0.0; self.entry_long = 0.0; self.entry_short = 0.0; self.long_margin = 0.0; self.short_margin = 0.0; self.used_margin = 0.0; self.mode = LiveMode::Flat; } ``` **Пример:** ``` long_qty = 1000, entry_long = 0.002000 short_qty = 1000, entry_short = 0.002000 realized_pnl = 0.0 price = 0.002060 contract_value = 1.0 LONG: realized_pnl += (0.002060 - 0.002000) × 1000 × 1.0 realized_pnl += 0.06 SHORT: realized_pnl += (0.002000 - 0.002060) × 1000 × 1.0 realized_pnl += -0.06 realized_pnl = 0.0 + 0.06 + (-0.06) = 0.0 long_qty = 0, short_qty = 0 mode = Flat ``` --- ## ⚠️ Критические моменты ### 1. Dollar-cost averaging при добавлении позиции **Алгоритм:** ```rust new_entry = (prev_entry × prev_qty + price × qty) / (prev_qty + qty) ``` **Пример:** ``` prev_qty = 1000, prev_entry = 0.002000 qty = 500, price = 0.002060 new_entry = (0.002000 × 1000 + 0.002060 × 500) / 1500 = (2.0 + 1.03) / 1500 = 3.03 / 1500 = 0.002020 Средняя цена = 0.002020 ``` --- ### 2. Уменьшение маржи при частичном закрытии **Алгоритм:** ```rust ratio = (prev_qty - qty) / prev_qty new_margin = old_margin × ratio ``` **Пример:** ``` prev_qty = 1000, long_margin = 0.04 USDT qty = 500 new_qty = 500 ratio = 500 / 1000 = 0.5 new_margin = 0.04 × 0.5 = 0.02 USDT ``` --- ### 3. Определение режима **Логика:** ```rust mode = if long_qty > 0 && short_qty > 0 { Both } else if long_qty > 0 || short_qty > 0 { Single } else { Flat } ``` **Примеры:** ``` long_qty = 1000, short_qty = 0 → mode = Single ✅ long_qty = 0, short_qty = 1000 → mode = Single ✅ long_qty = 1000, short_qty = 1000 → mode = Both ✅ long_qty = 0, short_qty = 0 → mode = Flat ✅ ``` --- ## 📊 Важные формулы ### 1. PnL LONG ``` pnl_long = (last_price - entry_long) × long_qty × contract_value ``` ### 2. PnL SHORT ``` pnl_short = (entry_short - last_price) × short_qty × contract_value ``` ### 3. Total PnL ``` total_pnl = realized_pnl + unrealized_pnl ``` ### 4. Delta ``` delta = long_qty - short_qty ``` ### 5. Long Margin ``` long_margin = (long_qty × entry_long × contract_value) / entry_leverage ``` ### 6. Short Margin ``` short_margin = (short_qty × entry_short × contract_value) / entry_leverage ``` ### 7. Used Margin ``` used_margin = long_margin + short_margin ``` --- ## 🔍 Примеры использования ### Пример 1: Полный цикл LONG → BOTH → Flat ```rust let mut pos = PositionSnapshot::new("MEMES_USDT"); // Открыть LONG pos.open_long(1000.0, 0.002000); // long_qty = 1000, entry_long = 0.002000, mode = Single // Открыть SHORT (хедж) pos.open_short(1000.0, 0.002000); // long_qty = 1000, short_qty = 1000, mode = Both // Закрыть LONG pos.close_long(500.0); // long_qty = 500, mode = Both // Закрыть всё pos.close_all_at_price(0.002060); // long_qty = 0, short_qty = 0, mode = Flat // realized_pnl = 0.06 + (-0.06) = 0.0 ``` ### Пример 2: Расчёт PnL ```rust pos.update_price(0.002060); let pnl_long = pos.pnl_long(); let pnl_short = pos.pnl_short(); let pnl_total = pos.total_pnl(); println!("PnL LONG: {}", pnl_long); println!("PnL SHORT: {}", pnl_short); println!("PnL TOTAL: {}", pnl_total); ``` --- ## 📚 Связанные файлы | Файл | Связь | |------|-------| | `src/live/strategy_core.rs` | Использует PositionSnapshot | | `src/live/strategy_single.rs` | Использует PositionSnapshot | | `src/live/strategy_both.rs` | Использует PositionSnapshot | | `src/live/volatility_cycle.rs` | Использует PositionSnapshot | | `src/live/hedge_guard.rs` | Использует PositionSnapshot | | `src/live/position_machine.rs` | Использует PositionSnapshot | --- ## 🎯 Резюме **Что делает PositionSnapshot:** 1. ✅ Хранит состояние позиции (количество, entry, PnL, маржа) 2. ✅ Рассчитывает PnL (реализованный и нереализованный) 3. ✅ Рассчитывает маржу (long, short, used) 4. ✅ Управляет позицией (open_long, open_short, close_long, close_short) 5. ✅ Определяет режим (Flat, Single, Both) 6. ✅ Проверяет хедж (is_hedged) 7. ✅ Рассчитывает delta (разница между LONG и SHORT) **Основные методы:** - `update_price()` - обновление цены - `unrealized()` - нереализованный PnL - `total_pnl()` - общий PnL - `pnl_long() / pnl_short()` - PnL по сторонам - `open_long() / open_short()` - открытие позиции - `close_long() / close_short()` - закрытие позиции - `close_all_at_price()` - полное закрытие - `recalc_margins()` - пересчёт маржи --- **Дата создания:** 2026-02-22 **Автор:** Claude Code Assistant **Версия:** 1.0