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