Files
AI-Trader/market/store/signal_store.py
T

179 lines
6.1 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
信号存储模块
"""
from typing import List, Dict, Optional
from datetime import datetime
import threading
from collections import defaultdict
from ..models import TradingSignal, SignalSource, SignalStatus
class SignalStore:
"""信号存储(只负责数据CRUD"""
def __init__(self, default_ttl: int = 300):
# 按品种分类的信号: {symbol: [TradingSignal, ...]}
self._signals_by_symbol: Dict[str, List[TradingSignal]] = defaultdict(list)
# 按ID索引
self._signals_by_id: Dict[str, TradingSignal] = {}
# 线程锁
self._lock = threading.RLock()
# 默认信号有效期
self.default_ttl = default_ttl
print("[SignalStore] 信号存储已初始化")
# ==================== 添加信号 ====================
def add_signal(self, signal: TradingSignal) -> str:
"""添加信号"""
with self._lock:
signal.DEFAULT_TTL = self.default_ttl
self._signals_by_symbol[signal.symbol].append(signal)
self._signals_by_id[signal.signal_id] = signal
print(f"[SignalStore] 添加信号: {signal.signal_id} {signal.symbol} {signal.action} (来源: {signal.source})")
return signal.signal_id
# ==================== 查询信号 ====================
def get_signal_by_id(self, signal_id: str) -> Optional[TradingSignal]:
"""根据ID获取信号"""
with self._lock:
return self._signals_by_id.get(signal_id)
def get_active_signals(self, symbol: str = None) -> List[TradingSignal]:
"""获取活跃信号"""
with self._lock:
if symbol:
signals = self._signals_by_symbol.get(symbol, [])
else:
signals = list(self._signals_by_id.values())
# 过滤活跃信号
active = [s for s in signals if s.is_active()]
return sorted(active, key=lambda x: x.created_at, reverse=True)
def get_active_signals_by_source(self, symbol: str, source: str) -> List[TradingSignal]:
"""获取指定来源的活跃信号"""
signals = self.get_active_signals(symbol)
return [s for s in signals if s.source == source]
def get_signals_dict(self, symbol: str = None) -> List[Dict]:
"""获取信号字典列表"""
signals = self.get_active_signals(symbol)
return [s.to_dict() for s in signals]
def get_signal_count(self, symbol: str = None, source: str = None) -> int:
"""获取信号数量"""
if source:
return len(self.get_active_signals_by_source(symbol or "", source))
return len(self.get_active_signals(symbol))
# ==================== 更新信号状态 ====================
def mark_signal_used(self, signal_id: str) -> bool:
"""标记信号为已使用"""
with self._lock:
signal = self._signals_by_id.get(signal_id)
if signal:
signal.mark_used()
return True
return False
def mark_signal_expired(self, signal_id: str) -> bool:
"""标记信号为已过期"""
with self._lock:
signal = self._signals_by_id.get(signal_id)
if signal:
signal.mark_expired()
return True
return False
# ==================== 清理过期信号 ====================
def cleanup_expired(self) -> int:
"""清理过期信号"""
with self._lock:
expired_ids = []
for signal_id, signal in self._signals_by_id.items():
if signal.is_expired() and signal.status == SignalStatus.ACTIVE:
signal.mark_expired()
expired_ids.append(signal_id)
# 从存储中移除过期信号
for signal_id in expired_ids:
signal = self._signals_by_id[signal_id]
symbol = signal.symbol
self._signals_by_symbol[symbol] = [
s for s in self._signals_by_symbol[symbol] if s.signal_id != signal_id
]
del self._signals_by_id[signal_id]
if expired_ids:
print(f"[SignalStore] 清理过期信号: {len(expired_ids)}条")
return len(expired_ids)
# ==================== 清空 ====================
def clear_by_symbol(self, symbol: str) -> int:
"""清空指定品种的信号"""
with self._lock:
signals = self._signals_by_symbol.get(symbol, [])
count = len(signals)
for signal in signals:
if signal.signal_id in self._signals_by_id:
del self._signals_by_id[signal.signal_id]
if symbol in self._signals_by_symbol:
del self._signals_by_symbol[symbol]
return count
def clear_all(self) -> int:
"""清空所有信号"""
with self._lock:
count = len(self._signals_by_id)
self._signals_by_symbol.clear()
self._signals_by_id.clear()
return count
# ==================== 统计 ====================
def get_signal_stats(self, symbol: str) -> Dict:
"""获取信号统计"""
signals = self.get_active_signals(symbol)
buy_signals = [s for s in signals if s.action == "buy"]
sell_signals = [s for s in signals if s.action == "sell"]
by_source = {}
for source in [SignalSource.PIVOT, SignalSource.KEY_LEVEL, SignalSource.AI_ENTRY]:
by_source[source] = len([s for s in signals if s.source == source])
return {
"symbol": symbol,
"total": len(signals),
"buy_count": len(buy_signals),
"sell_count": len(sell_signals),
"by_source": by_source,
}
# ==================== 状态 ====================
def get_status(self) -> Dict:
"""获取存储状态"""
with self._lock:
return {
"total_signals": len(self._signals_by_id),
"symbols": list(self._signals_by_symbol.keys()),
}