← Назад к главному
# 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