← Назад к главному
# meta.rs - Модуль торговых метаданных
## 📋 Назначение модуля
`TradingMeta` хранит торговые метаданные контракта из Redis.
**Основная логика:**
1. **Хранение метаданных** контракта (цена, комиссии, параметры контракта и т.д.)
2. **Загрузка из Redis** через `fetch_trading_meta()`
3. **Значения по умолчанию** через `default_meta()` (fallback)
**Используется во всех LIVE модулях:**
- `strategy_core.rs` - вход в позицию
- `strategy_single.rs` - управление односторонней позицией
- `strategy_both.rs` - управление BOTH позицией
- `volatility_cycle.rs` - резки
- `hedge_guard.rs` - экстренный хедж
- `entry_calculator.rs` - расчёт входа
---
## 🏗️ Структура модуля
### Структура `TradingMeta`
**Строки:** 14-37
```rust
#[derive(Clone, Debug, Serialize, Deserialize)]
pub struct TradingMeta {
pub symbol: String, // Символ контракта (например, "MEMES_USDT")
pub price: f64, // Текущая цена монеты
pub bid: f64, // Bid цена (покупка)
pub ask: f64, // Ask цена (продажа)
pub bid_qty_depth: f64, // Объём в bid стакане
pub ask_qty_depth: f64, // Объём в ask стакане
pub chg_pct_24h: f64, // Изменение за 24ч (%)
pub contract_multiplier: f64, // Множитель контракта (монет в 1 контракте)
pub min_contracts: f64, // Минимальное количество контрактов (step size)
pub max_contracts: f64, // Максимальное количество контрактов
pub leverage_min: f64, // Минимальное плечо
pub leverage_max: f64, // Максимальное плечо
pub taker_fee_rate: f64, // Taker комиссия
pub maker_fee_rate: f64, // Maker комиссия
pub funding_rate: f64, // Funding rate
pub funding_rate_indicative: f64, // Индикативный funding rate
pub maintenance_rate: f64, // Maintenance rate (для ликвидации)
pub order_price_round: String, // Округление цены
pub order_price_deviate: String, // Допустимое отклонение цены
pub slippage_buy_1000_pct: f64, // Проскальзывание при покупке 1000 контрактов (%)
pub slippage_sell_1000_pct: f64, // Проскальзывание при продаже 1000 контрактов (%)
pub ts: i64, // Timestamp (Unix epoch seconds)
}
```
**Назначение:** Хранение всех необходимых метаданных контракта
**Поля и их описание:**
| Поле | Тип | Описание | Пример |
|------|-----|----------|---------|
| `symbol` | String | Символ контракта | "MEMES_USDT" |
| `price` | f64 | Текущая цена монеты | 0.0022365 |
| `bid` | f64 | Bid цена | 0.001722 |
| `ask` | f64 | Ask цена | 0.001727 |
| `bid_qty_depth` | f64 | Объём bid стакана | 1022.73 |
| `ask_qty_depth` | f64 | Объём ask стакана | 977.26 |
| `chg_pct_24h` | f64 | Изменение за 24ч (%) | +50.0 |
| `contract_multiplier` | f64 | Монет в 1 контракте | 100.0 |
| `min_contracts` | f64 | Минимальное количество контрактов | 1.0 |
| `max_contracts` | f64 | Максимальное количество контрактов | 6500.0 |
| `leverage_min` | f64 | Минимальное плечо | 1.0 |
| `leverage_max` | f64 | Максимальное плечо | 50.0 |
| `taker_fee_rate` | f64 | Taker комиссия | 0.00075 |
| `maker_fee_rate` | f64 | Maker комиссия | -0.00005 |
| `funding_rate` | f64 | Funding rate | 0.000012 |
| `funding_rate_indicative` | f64 | Индикативный funding rate | 0.000012 |
| `maintenance_rate` | f64 | Maintenance rate | 0.15 |
| `order_price_round` | String | Округление цены | "0.000001" |
| `order_price_deviate` | String | Допустимое отклонение | "0.2" |
| `slippage_buy_1000_pct` | f64 | Проскальзывание покупки (%) | 0.5 |
| `slippage_sell_1000_pct` | f64 | Проскальзывание продажи (%) | 0.5 |
| `ts` | i64 | Timestamp | 1771747635 |
---
## 🔄 Основные функции
### Функция `fetch_trading_meta()`
**Строки:** 40-83
**Сигнатура:**
```rust
pub async fn fetch_trading_meta(
redis: Arc,
symbol: &str,
) -> Result
```
**Возвращает:**
- `Ok(TradingMeta)` - метаданные загружены
- `Err` - ошибка (например, ключ не найден)
**Алгоритм (4 этапа):**
#### Этап 1: Подключение к Redis
```rust
// 🔧 ИСПРАВЛЕНО: было redis.conn(), должно быть get_conn()
let mut conn = redis.get_conn().await?;
```
**Примечание:** Исправлена ошибка (было `redis.conn()`, должно быть `redis.get_conn()`)
---
#### Этап 2: Формирование ключа
```rust
let key = format!("hb:meta:{}", symbol);
```
**Пример:**
```
symbol = "MEMES_USDT"
key = "hb:meta:MEMES_USDT"
```
---
#### Этап 3: Чтение из Redis
```rust
let raw: Option = conn.get(&key).await?;
let raw = match raw {
Some(v) => v,
None => return Err(anyhow::anyhow!("meta not found: {}", key)),
};
```
**Логика:**
- Если ключ существует → читаем значение
- Если ключ не существует → возвращаем ошибку
**Пример:**
```
Redis:
GET hb:meta:MEMES_USDT
→ {"symbol":"MEMES_USDT","price":0.0022365,...}
raw = Some("{\"symbol\":\"MEMES_USDT\",\"price\":0.0022365,...}")
```
---
#### Этап 4: Парсинг JSON в TradingMeta
```rust
let tick_data: serde_json::Value = serde_json::from_str(&raw)?;
Ok(TradingMeta {
symbol: symbol.to_string(),
price: tick_data["price"].as_f64().unwrap_or(0.0),
bid: tick_data["bid"].as_f64().unwrap_or(0.0),
ask: tick_data["ask"].as_f64().unwrap_or(0.0),
bid_qty_depth: tick_data["bid_qty_depth"].as_f64().unwrap_or(0.0),
ask_qty_depth: tick_data["ask_qty_depth"].as_f64().unwrap_or(0.0),
chg_pct_24h: tick_data["chg_pct_24h"].as_f64().unwrap_or(0.0),
contract_multiplier: tick_data["contract_multiplier"].as_f64().unwrap_or(1.0),
min_contracts: tick_data["min_contracts"].as_f64().unwrap_or(1.0),
max_contracts: tick_data["max_contracts"].as_f64().unwrap_or(1_000_000.0),
leverage_min: tick_data["leverage_min"].as_f64().unwrap_or(1.0),
leverage_max: tick_data["leverage_max"].as_f64().unwrap_or(100.0),
taker_fee_rate: tick_data["taker_fee_rate"].as_f64().unwrap_or(0.0003),
maker_fee_rate: tick_data["maker_fee_rate"].as_f64().unwrap_or(0.0002),
funding_rate: tick_data["funding_rate"].as_f64().unwrap_or(0.0001),
funding_rate_indicative: tick_data["funding_rate_indicative"].as_f64().unwrap_or(0.0001),
maintenance_rate: tick_data["maintenance_rate"].as_f64().unwrap_or(0.01),
order_price_round: tick_data["order_price_round"].as_str().unwrap_or("0.000001").to_string(),
order_price_deviate: tick_data["order_price_deviate"].as_str().unwrap_or("0.2").to_string(),
slippage_buy_1000_pct: tick_data["slippage_buy_1000_pct"].as_f64().unwrap_or(0.0),
slippage_sell_1000_pct: tick_data["slippage_sell_1000_pct"].as_f64().unwrap_or(0.0),
ts: tick_data["timestamp"]
.as_i64()
.unwrap_or_else(|| Utc::now().timestamp()),
})
```
**Логика:**
1. Парсинг JSON строки в `serde_json::Value`
2. Извлечение каждого поля с `as_f64().unwrap_or(default_value)`
3. Если поле отсутствует → используется значение по умолчанию
4. Если `timestamp` отсутствует → используется `Utc::now().timestamp()`
**Пример JSON:**
```json
{
"symbol": "MEMES_USDT",
"price": 0.00223650,
"bid": 0.001722,
"ask": 0.001727,
"bid_qty_depth": 1022.73,
"ask_qty_depth": 977.26,
"chg_pct_24h": 50.0,
"contract_multiplier": 100.0,
"min_contracts": 1.0,
"max_contracts": 6500.0,
"leverage_min": 1.0,
"leverage_max": 50.0,
"taker_fee_rate": 0.00075,
"maker_fee_rate": -0.00005,
"funding_rate": 0.000012,
"funding_rate_indicative": 0.000012,
"maintenance_rate": 0.15,
"order_price_round": "0.000001",
"order_price_deviate": "0.2",
"slippage_buy_1000_pct": 0.0,
"slippage_sell_1000_pct": 0.0,
"timestamp": 1771747635
}
```
**Пример TradingMeta:**
```rust
TradingMeta {
symbol: "MEMES_USDT",
price: 0.0022365,
bid: 0.001722,
ask: 0.001727,
bid_qty_depth: 1022.73,
ask_qty_depth: 977.26,
chg_pct_24h: 50.0,
contract_multiplier: 100.0,
min_contracts: 1.0,
max_contracts: 6500.0,
leverage_min: 1.0,
leverage_max: 50.0,
taker_fee_rate: 0.00075,
maker_fee_rate: -0.00005,
funding_rate: 0.000012,
funding_rate_indicative: 0.000012,
maintenance_rate: 0.15,
order_price_round: "0.000001".to_string(),
order_price_deviate: "0.2".to_string(),
slippage_buy_1000_pct: 0.0,
slippage_sell_1000_pct: 0.0,
ts: 1771747635,
}
```
---
### Функция `default_meta()`
**Строки:** 86-111
**Сигнатура:**
```rust
pub fn default_meta(symbol: &str) -> TradingMeta
```
**Возвращает:** `TradingMeta` со значениями по умолчанию
**Назначение:** Fallback если не удалось загрузить из Redis
**Алгоритм:**
```rust
pub fn default_meta(symbol: &str) -> TradingMeta {
TradingMeta {
symbol: symbol.to_string(),
price: 0.0,
bid: 0.0,
ask: 0.0,
bid_qty_depth: 0.0,
ask_qty_depth: 0.0,
chg_pct_24h: 0.0,
contract_multiplier: 1.0,
min_contracts: 1.0,
max_contracts: 1_000_000.0,
leverage_min: 1.0,
leverage_max: 100.0,
taker_fee_rate: 0.0003,
maker_fee_rate: 0.0002,
funding_rate: 0.0001,
funding_rate_indicative: 0.0001,
maintenance_rate: 0.01,
order_price_round: "0.000001".to_string(),
order_price_deviate: "0.2".to_string(),
slippage_buy_1000_pct: 0.0,
slippage_sell_1000_pct: 0.0,
ts: Utc::now().timestamp(),
}
}
```
**Пример:**
```rust
let meta = default_meta("MEMES_USDT");
meta = TradingMeta {
symbol: "MEMES_USDT",
price: 0.0,
bid: 0.0,
ask: 0.0,
bid_qty_depth: 0.0,
ask_qty_depth: 0.0,
chg_pct_24h: 0.0,
contract_multiplier: 1.0,
min_contracts: 1.0,
max_contracts: 1_000_000.0,
leverage_min: 1.0,
leverage_max: 100.0,
taker_fee_rate: 0.0003,
maker_fee_rate: 0.0002,
funding_rate: 0.0001,
funding_rate_indicative: 0.0001,
maintenance_rate: 0.01,
order_price_round: "0.000001".to_string(),
order_price_deviate: "0.2".to_string(),
slippage_buy_1000_pct: 0.0,
slippage_sell_1000_pct: 0.0,
ts: 1645560000, // текущее время
}
```
---
## 📊 Значения по умолчанию
| Поле | Значение по умолчанию | Описание |
|------|---------------------|----------|
| `price` | 0.0 | Нет цены |
| `bid` | 0.0 | Нет bid |
| `ask` | 0.0 | Нет ask |
| `bid_qty_depth` | 0.0 | Нет объёма в bid |
| `ask_qty_depth` | 0.0 | Нет объёма в ask |
| `chg_pct_24h` | 0.0 | Нет изменения |
| `contract_multiplier` | 1.0 | 1 монета в 1 контракте |
| `min_contracts` | 1.0 | Минимально 1 контракт |
| `max_contracts` | 1_000_000.0 | Огромный максимум |
| `leverage_min` | 1.0 | Минимально 1x |
| `leverage_max` | 100.0 | Максимум 100x |
| `taker_fee_rate` | 0.0003 | 0.03% |
| `maker_fee_rate` | 0.0002 | 0.02% |
| `funding_rate` | 0.0001 | 0.01% |
| `funding_rate_indicative` | 0.0001 | 0.01% |
| `maintenance_rate` | 0.01 | 1% |
| `order_price_round` | "0.000001" | Округление до 6 знаков |
| `order_price_deviate` | "0.2" | Допустимое отклонение 20% |
| `slippage_buy_1000_pct` | 0.0 | Нет проскальзывания |
| `slippage_sell_1000_pct` | 0.0 | Нет проскальзывания |
| `ts` | `Utc::now().timestamp()` | Текущее время |
---
## 🎯 Примеры использования
### Пример 1: Загрузка метаданных из Redis
```rust
use crate::live::meta::fetch_trading_meta;
use std::sync::Arc;
async fn example() {
let redis = Arc::new(redis_client);
let symbol = "MEMES_USDT";
match fetch_trading_meta(redis.clone(), &symbol).await {
Ok(meta) => {
println!("Загружены метаданные для {}:", meta.symbol);
println!(" Цена: {}", meta.price);
println!(" Плечо: {}x - {}x", meta.leverage_min, meta.leverage_max);
println!(" Контрактов: min={}, max={}", meta.min_contracts, meta.max_contracts);
}
Err(e) => {
println!("Ошибка загрузки метаданных: {}", e);
}
}
}
```
### Пример 2: Использование метаданных в стратегии
```rust
pub fn process(meta: &TradingMeta, pos: &PositionSnapshot) {
// Проверяем текущую цену
if meta.price > 0.0 {
let current_price = meta.price;
println!("Текущая цена: {}", current_price);
}
// Проверяем объём стакана
let total_depth = meta.bid_qty_depth + meta.ask_qty_depth;
println!("Общий объём стакана: {}", total_depth);
// Проверяем комиссии
let fee = meta.taker_fee_rate.abs().max(meta.maker_fee_rate.abs());
println!("Максимальная комиссия: {}", fee);
// Проверяем shoulder
let max_leverage = meta.leverage_max;
println!("Максимальное плечо: {}x", max_leverage);
}
```
### Пример 3: Использование при входе в позицию
```rust
pub fn calculate_entry(meta: &TradingMeta, balance: f64) -> Result {
// Используем contract_multiplier
let price_contract = meta.price * meta.contract_multiplier;
// Используем leverage_max
let max_leverage = meta.leverage_max;
let margin_per_contract = price_contract / max_leverage;
// Используем min_contracts и max_contracts
let min_qty = meta.min_contracts;
let max_qty = meta.max_contracts;
// Расчёт qty
let mut qty = (balance / margin_per_contract).floor();
qty = qty.max(min_qty);
qty = qty.min(max_qty);
Ok(EntryCalculation { qty, ... })
}
```
---
## ⚠️ Критические моменты
### 1. Обработка отсутствия полей
**Логика:**
```rust
price: tick_data["price"].as_f64().unwrap_or(0.0),
```
**Что это значит:**
- Если поле `"price"` отсутствует → используется `0.0`
- Если поле есть, но не является числом → используется `0.0`
**Пример:**
```json
{
"symbol": "MEMES_USDT",
// "price": 0.0022365, // поле отсутствует
"bid": 0.001722
}
```
```rust
price = tick_data["price"].as_f64().unwrap_or(0.0);
// = 0.0 (используется значение по умолчанию)
```
---
### 2. Обработка отсутствия timestamp
**Логика:**
```rust
ts: tick_data["timestamp"]
.as_i64()
.unwrap_or_else(|| Utc::now().timestamp()),
```
**Что это значит:**
- Если поле `"timestamp"` отсутствует → используется текущее время
**Пример:**
```rust
ts = tick_data["timestamp"].as_i64().unwrap_or_else(|| Utc::now().timestamp());
// = 1645560000 (текущее время)
```
---
### 3. Исправление ошибки `redis.conn()` → `redis.get_conn()`
**Было:**
```rust
let mut conn = redis.conn().await?;
```
**Стало:**
```rust
let mut conn = redis.get_conn().await?;
```
**Почему:** Правильный метод для подключения к Redis
---
## 📊 Redis ключ
### Формат ключа
```
hb:meta:{symbol}
```
### Примеры
```
hb:meta:MEMES_USDT
hb:meta:DOGE_USDT
hb:meta:PEPE_USDT
```
---
## 🔗 Интеграция с другими модулями
### 1. Redis
**Используется:** Для хранения и загрузки метаданных
**Ключ:** `hb:meta:{symbol}`
---
### 2. monitor.rs
**Используется:** Для записи метаданных в Redis
**Пример:**
```rust
let tick_data = json!({
"symbol": symbol,
"price": price,
"bid": bid,
...
});
redis.set(format!("hb:meta:{}", symbol), tick_data).await?;
```
---
### 3. strategy_core.rs
**Используется:** Для входа в позицию
**Используемые поля:**
```rust
meta.price
meta.contract_multiplier
meta.leverage_min
meta.leverage_max
meta.min_contracts
meta.max_contracts
meta.chg_pct_24h
```
---
### 4. strategy_single.rs
**Используется:** Для управления односторонней позицией
**Используемые поля:**
```rust
meta.price
meta.taker_fee_rate
meta.maker_fee_rate
meta.funding_rate
```
---
### 5. strategy_both.rs
**Используется:** Для управления BOTH позицией
**Используемые поля:**
```rust
meta.price
meta.taker_fee_rate
meta.maker_fee_rate
meta.funding_rate
```
---
### 6. volatility_cycle.rs
**Используется:** Для резок
**Используемые поля:**
```rust
meta.price
meta.taker_fee_rate
meta.maker_fee_rate
meta.funding_rate
meta.min_contracts
```
---
### 7. hedge_guard.rs
**Используется:** Для экстренного хеджа
**Используемые поля:**
```rust
meta.price
meta.bid_qty_depth
meta.ask_qty_depth
```
---
### 8. entry_calculator.rs
**Используется:** Для расчёта входа
**Используемые поля:**
```rust
meta.price
meta.contract_multiplier
meta.leverage_min
meta.leverage_max
meta.min_contracts
meta.max_contracts
```
---
## 🔄 Поток данных
```
monitor.rs (MONITOR режим)
↓
├─→ fetch_all_tickers() → Gate.io API
├─→ filter_pump_pairs()
├─→ pick_best_pair()
└─→ parse_loop()
↓
Redis:
SET hb:meta:{symbol} {JSON}
SET hb:ticks:{symbol} [JSON, JSON, ...]
SET hb:last:{symbol} {JSON}
SET hb:entry_calc:{symbol} {JSON}
live_step() (LIVE режим)
↓
├─→ fetch_trading_meta()
│ ├─→ Redis GET hb:meta:{symbol}
│ ├─→ Parse JSON → TradingMeta
│ └─→ return TradingMeta
│
└─→ Использование в стратегиях:
├─→ strategy_core
├─→ strategy_single
├─→ strategy_both
├─→ volatility_cycle
├─→ hedge_guard
└─→ entry_calculator
```
---
## 🚀 Использование в коде
### Пример 1: Загрузка метаданных
```rust
use crate::live::meta::{fetch_trading_meta, default_meta};
async fn load_meta(redis: Arc, symbol: &str) -> TradingMeta {
match fetch_trading_meta(redis.clone(), symbol).await {
Ok(meta) => meta,
Err(e) => {
println!("Ошибка загрузки: {}, используем default", e);
default_meta(symbol)
}
}
}
```
### Пример 2: Проверка объёма стакана
```rust
fn check_depth(meta: &TradingMeta) {
let total_depth = meta.bid_qty_depth + meta.ask_qty_depth;
if total_depth < 1000.0 {
println!("⚠️ Пустой стакан: {}", total_depth);
} else {
println!("✅ Объём стакана: {}", total_depth);
}
}
```
### Пример 3: Расчёт комиссий
```rust
fn calculate_fees(meta: &TradingMeta, notional: f64) -> f64 {
let taker_fee = meta.taker_fee_rate.abs();
let maker_fee = meta.maker_fee_rate.abs();
let fee_rate = taker_fee.max(maker_fee);
let fee = notional * fee_rate;
fee
}
```
---
## 📝 Логирование
**Примечание:** `meta.rs` сам не логирует, логи выводятся в вызывающих модулях
---
## 📚 Связанные файлы
| Файл | Связь |
|------|-------|
| `src/redis.rs` | Redis клиент |
| `src/monitor.rs` | Запись метаданных в Redis |
| `src/live/strategy_core.rs` | Использует метаданные |
| `src/live/strategy_single.rs` | Использует метаданные |
| `src/live/strategy_both.rs` | Использует метаданные |
| `src/live/volatility_cycle.rs` | Использует метаданные |
| `src/live/hedge_guard.rs` | Использует метаданные |
| `src/live/entry_calculator.rs` | Использует метаданные |
---
## 🎯 Резюме
**Что делает meta.rs:**
1. ✅ Хранит все необходимые метаданные контракта
2. ✅ Загружает метаданные из Redis через `fetch_trading_meta()`
3. ✅ Предоставляет значения по умолчанию через `default_meta()`
4. ✅ Обрабатывает отсутствующие поля (использует defaults)
5. ✅ Обрабатывает отсутствующий timestamp (использует текущее время)
**Основные метаданные:**
- Цена, bid, ask
- Объём стакана
- Параметры контракта (multiplier, min/max contracts)
- Плечо (min/max)
- Комиссии (taker, maker, funding)
- Настройки ордеров (rounding, deviation)
- Проскальзывание (slippage)
**Используется:**
- Во всех LIVE стратегиях
- В entry_calculator
- В hedge_guard
**Redis ключ:** `hb:meta:{symbol}`
---
**Дата создания:** 2026-02-22
**Автор:** Claude Code Assistant
**Версия:** 1.0