Files
exchange_monitor_sync/sync/order_sync.py
2025-12-02 22:05:54 +08:00

166 lines
6.7 KiB
Python

from .base_sync import BaseSync
from loguru import logger
from typing import List, Dict
import json
import time
from datetime import datetime, timedelta
class OrderSync(BaseSync):
"""订单数据同步器"""
async def sync(self):
"""同步订单数据"""
try:
# 获取所有账号
accounts = self.get_accounts_from_redis()
for k_id_str, account_info in accounts.items():
try:
k_id = int(k_id_str)
st_id = account_info.get('st_id', 0)
exchange_id = account_info['exchange_id']
if k_id <= 0 or st_id <= 0:
continue
# 从Redis获取最近N天的订单数据
orders = await self._get_recent_orders_from_redis(k_id, exchange_id)
# 同步到数据库
if orders:
success = self._sync_orders_to_db(k_id, st_id, orders)
if success:
logger.debug(f"订单同步成功: k_id={k_id}, 订单数={len(orders)}")
except Exception as e:
logger.error(f"同步账号 {k_id_str} 订单失败: {e}")
continue
logger.info("订单数据同步完成")
except Exception as e:
logger.error(f"订单同步失败: {e}")
async def _get_recent_orders_from_redis(self, k_id: int, exchange_id: str) -> List[Dict]:
"""从Redis获取最近N天的订单数据"""
try:
redis_key = f"{exchange_id}:orders:{k_id}"
# 计算最近N天的日期
from config.settings import SYNC_CONFIG
recent_days = SYNC_CONFIG['recent_days']
today = datetime.now()
recent_dates = []
for i in range(recent_days):
date = today - timedelta(days=i)
date_format = date.strftime('%Y-%m-%d')
recent_dates.append(date_format)
# 获取所有key
all_keys = self.redis_client.client.hkeys(redis_key)
orders_list = []
for key in all_keys:
key_str = key.decode('utf-8') if isinstance(key, bytes) else key
if key_str == 'positions':
continue
# 检查是否以最近N天的日期开头
for date_format in recent_dates:
if key_str.startswith(date_format + '_'):
try:
order_json = self.redis_client.client.hget(redis_key, key_str)
if order_json:
order = json.loads(order_json)
# 验证时间
order_time = order.get('time', 0)
if order_time >= int(time.time()) - recent_days * 24 * 3600:
orders_list.append(order)
break
except:
break
return orders_list
except Exception as e:
logger.error(f"获取Redis订单数据失败: k_id={k_id}, error={e}")
return []
def _sync_orders_to_db(self, k_id: int, st_id: int, orders_data: List[Dict]) -> bool:
"""同步订单数据到数据库"""
session = self.db_manager.get_session()
try:
# 准备批量数据
insert_data = []
for order_data in orders_data:
try:
order_dict = self._convert_order_data(order_data)
# 检查完整性
required_fields = ['order_id', 'symbol', 'side', 'time']
if not all(order_dict.get(field) for field in required_fields):
continue
insert_data.append(order_dict)
except Exception as e:
logger.error(f"转换订单数据失败: {order_data}, error={e}")
continue
if not insert_data:
return True
with session.begin():
# 使用参数化批量插入
sql = """
INSERT INTO deh_strategy_order_new
(st_id, k_id, asset, order_id, symbol, side, price, time,
order_qty, last_qty, avg_price, exchange_id)
VALUES
(:st_id, :k_id, :asset, :order_id, :symbol, :side, :price, :time,
:order_qty, :last_qty, :avg_price, :exchange_id)
ON DUPLICATE KEY UPDATE
side = VALUES(side),
price = VALUES(price),
time = VALUES(time),
order_qty = VALUES(order_qty),
last_qty = VALUES(last_qty),
avg_price = VALUES(avg_price)
"""
# 分块执行
from config.settings import SYNC_CONFIG
chunk_size = SYNC_CONFIG['chunk_size']
for i in range(0, len(insert_data), chunk_size):
chunk = insert_data[i:i + chunk_size]
session.execute(text(sql), chunk)
return True
except Exception as e:
logger.error(f"同步订单到数据库失败: k_id={k_id}, error={e}")
return False
finally:
session.close()
def _convert_order_data(self, data: Dict) -> Dict:
"""转换订单数据格式"""
return {
'st_id': int(data.get('st_id', 0)),
'k_id': int(data.get('k_id', 0)),
'asset': 'USDT',
'order_id': str(data.get('order_id', '')),
'symbol': data.get('symbol', ''),
'side': data.get('side', ''),
'price': float(data.get('price', 0)) if data.get('price') is not None else None,
'time': int(data.get('time', 0)) if data.get('time') is not None else None,
'order_qty': float(data.get('order_qty', 0)) if data.get('order_qty') is not None else None,
'last_qty': float(data.get('last_qty', 0)) if data.get('last_qty') is not None else None,
'avg_price': float(data.get('avg_price', 0)) if data.get('avg_price') is not None else None,
'exchange_id': None # 忽略该字段
}