Refactor: restructure market module with services, stores, and utils

This commit is contained in:
guaiwoluo2020
2026-03-19 09:47:41 +08:00
parent ef15b92342
commit b7ea37ad09
78 changed files with 10594 additions and 7645 deletions
+420 -200
View File
@@ -2,256 +2,476 @@
# -*- coding: utf-8 -*-
"""
交易服务核心类
内部聚合信号层,对外暴露策略层
"""
from collections import deque, defaultdict
from typing import List, Dict, Optional
from typing import List, Dict, Optional, Set
from datetime import datetime
import threading
import asyncio
import json
from models import TradeInstruction
from market.store import MarketStore
from market.pivot_detector import PivotDetector
from market.monitor import PivotMonitor, TradeConfig
from market.trend_analyzer import TrendAnalyzer
from market.pending_orders import PendingOrderManager
from market.models import KlineData, PivotPoint, LLMConfig, LLMAnalysisResult
from market.models import TechTrendState, TechResonanceResult, TechTradeSuggestion
from market.models import PendingOrder, TradingInstruction, TradingSignal, TradingDecision
from market.models import StatisticsData, PositionData, TradeDeal
from market.store import KlineStore, PivotStore, LLMStore, TechStore
from market.store import PendingOrderStore, TradingInstructionStore, SignalStore, StrategyStore
from market.store import StatisticsStore, PositionStore, TradeHistoryStore
from market.services import KlineService, PivotService, LLMService, TechService
from market.services import PendingOrderService, TradingInstructionService
from market.services import SignalService, StrategyService, RiskManager
from market.services import PivotSignalGenerator, KeyLevelSignalGenerator, AIEntrySignalGenerator
from market.services import StatisticsService, PositionService, TradeHistoryService
from market.trade_config import TradeConfig
from market.llm_analyzer import LLMAnalyzer
class TradingServer:
"""交易服务主类"""
"""
交易服务主类
架构:
- 内部:行情模块 → 信号层 → 策略层 → 订单/指令
- 对外:只暴露策略服务接口
"""
def __init__(self):
# 交易指令队列 - 按SYMBOL分类
# 结构: {"SYMBOL1": [TradeInstruction1, ...], "SYMBOL2": [...]}
self.trade_instructions = defaultdict(list)
# 平仓指令队列 - 按SYMBOL分类
# 结构: {"SYMBOL1": [ticket1, ticket2, ...], ...}
self.close_position_instructions = defaultdict(list)
# 统计数据历史 - 保留最新10条
# 结构: deque([{stat_data1}, {stat_data2}, ...], maxlen=10)
self.statistics_history = deque(maxlen=10)
# 线程锁 - 确保线程安全
# 线程锁
self.lock = threading.RLock()
# ==================== 行情模块 ====================
# K线存储
self.market_store = MarketStore()
# 转折点检测器
self.pivot_detector = PivotDetector()
# 待确认订单管理器(需要在 PivotMonitor 之前初始化)
self.pending_orders = PendingOrderManager()
# 设置订单确认回调
self.pending_orders.set_confirm_callback(self._on_order_confirmed)
# 大模型分析器(需要在 PivotMonitor 之前初始化)
self.llm_analyzer = LLMAnalyzer(self.market_store)
# 转折点监控器
self.pivot_monitor = PivotMonitor(self.market_store, self.pivot_detector, self.pending_orders, self.llm_analyzer)
# 设置统计数据历史引用(用于获取价差)
self.pivot_monitor.set_statistics_history(self.statistics_history)
# 趋势分析器
self.trend_analyzer = TrendAnalyzer()
self.trend_analyzer.set_statistics_history(self.statistics_history)
# 交易配置
# ==================== 行情模块(内部) ====================
# 存储
self.kline_store = KlineStore()
self.pivot_store = PivotStore()
self.llm_store = LLMStore()
self.tech_store = TechStore()
# 服务层
self.kline_service = KlineService(self.kline_store)
self.pivot_service = PivotService(self.pivot_store, self.kline_store)
self.llm_service = LLMService(self.llm_store, self.kline_service)
self.tech_service = TechService(self.tech_store, self.kline_store, self.pivot_store)
# LLM 分析器
self.llm_analyzer = LLMAnalyzer(self.llm_service)
# ==================== 统计/持仓/交易历史模块 ====================
# 存储层
self.statistics_store = StatisticsStore()
self.position_store = PositionStore()
self.trade_history_store = TradeHistoryStore()
# 服务层
self.statistics_service = StatisticsService(self.statistics_store)
self.position_service = PositionService(self.position_store)
self.trade_history_service = TradeHistoryService(self.trade_history_store)
# TechService 使用统计服务获取价差
self.tech_service.set_statistics_service(self.statistics_service)
# ==================== 信号层(内部,不暴露) ====================
# 存储层
self._signal_store = SignalStore()
# 服务层
self._signal_service = SignalService(self._signal_store)
# ==================== 策略层(对外暴露) ====================
# 存储层
self._strategy_store = StrategyStore()
# 风险管理器
self._risk_manager = RiskManager()
# 服务层
self.strategy_service = StrategyService(
self._strategy_store,
self._signal_service,
self._risk_manager
)
# ==================== 交易配置 ====================
self.trade_config = TradeConfig.get_instance()
print("[信息] 交易服务已初始化")
# 注册信号生成器
self._setup_signal_generators()
def _on_order_confirmed(self, order: Dict):
# ==================== 订单/指令模块 ====================
# 存储层
self.pending_order_store = PendingOrderStore()
self.trading_instruction_store = TradingInstructionStore()
# 服务层
self.pending_order_service = PendingOrderService(self.pending_order_store)
self.trading_instruction_service = TradingInstructionService(self.trading_instruction_store)
# 设置订单确认回调
self.pending_order_service.set_confirm_callback(self._on_order_confirmed)
# 更新策略服务的订单服务引用
self.strategy_service.set_pending_order_service(self.pending_order_service)
# 策略服务使用持仓服务进行风险管理
self.strategy_service.set_position_service(self.position_service)
# 风险管理器使用统计服务获取账户信息
self._risk_manager.set_statistics_service(self.statistics_service)
# ==================== WebSocket 广播 ====================
self._ws_clients: Set = set()
self._ws_lock = threading.Lock()
self._main_loop = None
# ==================== 决策历史 ====================
self._decision_history: deque = deque(maxlen=50)
# 兼容旧代码的别名
self.market_store = self.kline_store
self.pivot_detector = self.pivot_service
self.trend_analyzer = self.tech_service
self.pending_orders = self.pending_order_service
self.trade_instructions = defaultdict(list)
# 统计数据历史兼容(已迁移到 statistics_store
self.statistics_history = self.statistics_store._all_data
print("[TradingServer] 交易服务已初始化")
def _setup_signal_generators(self):
"""设置信号生成器"""
# 转折点信号生成器
pivot_generator = PivotSignalGenerator(
pivot_service=self.pivot_service,
pivot_store=self.pivot_store,
kline_store=self.kline_store
)
self._signal_service.register_generator("pivot", pivot_generator)
# 关键点位信号生成器
key_level_generator = KeyLevelSignalGenerator()
self._signal_service.register_generator("key_level", key_level_generator)
# AI入场信号生成器
ai_entry_generator = AIEntrySignalGenerator()
ai_entry_generator.set_llm_analyzer(self.llm_analyzer)
self._signal_service.register_generator("ai_entry", ai_entry_generator)
# ==================== WebSocket 管理 ====================
def set_event_loop(self, loop):
"""设置主事件循环引用"""
self._main_loop = loop
print("[TradingServer] 已设置主事件循环")
# 同时设置内部模块的事件循环
self.llm_analyzer.set_event_loop(loop)
def add_ws_client(self, client):
"""添加WebSocket客户端"""
with self._ws_lock:
self._ws_clients.add(client)
# 同时注册到内部模块
self.llm_analyzer.add_ws_client(client)
print(f"[TradingServer] WebSocket客户端已连接, 当前连接数: {len(self._ws_clients)}")
def remove_ws_client(self, client):
"""移除WebSocket客户端"""
with self._ws_lock:
self._ws_clients.discard(client)
# 同时从内部模块移除
self.llm_analyzer.remove_ws_client(client)
print(f"[TradingServer] WebSocket客户端已断开, 当前连接数: {len(self._ws_clients)}")
def get_ws_client_count(self) -> int:
"""获取WebSocket客户端数量"""
with self._ws_lock:
return len(self._ws_clients)
def _broadcast(self, data: Dict):
"""广播数据到所有WebSocket客户端"""
message = json.dumps(data, ensure_ascii=False)
with self._ws_lock:
clients = list(self._ws_clients)
if not clients:
return
if self._main_loop and self._main_loop.is_running():
for client in clients:
try:
asyncio.run_coroutine_threadsafe(
self._send_to_client(client, message),
self._main_loop
)
except Exception as e:
print(f"[TradingServer] 发送WebSocket消息失败: {e}")
async def _send_to_client(self, client, message: str):
"""发送消息到客户端"""
try:
await client.send_text(message)
except Exception as e:
print(f"[TradingServer] 发送消息到客户端失败: {e}")
with self._ws_lock:
self._ws_clients.discard(client)
def _broadcast_decision(self, decision: TradingDecision):
"""广播交易决策"""
self._broadcast({
"type": "trading_decision",
"data": decision.to_dict()
})
def _broadcast_pending_order(self, order: PendingOrder):
"""广播待确认订单"""
self._broadcast({
"type": "pending_order",
"data": order.to_dict()
})
# ==================== 价格处理与决策 ====================
def process_price(self, symbol: str, current_price: float) -> Dict:
"""
订单确认回调 - 将确认的订单加入交易队列
处理价格变动,生成决策
这是核心入口:
1. 信号层生成信号
2. 策略层综合决策
3. 自动执行决策(生成PendingOrder
Args:
symbol: 品种
current_price: 当前价格
Returns:
处理结果
"""
print(f"[TradingServer] _on_order_confirmed 被调用,订单: {order}")
result = {
"signals_generated": 0,
"decision": None,
"pending_order": None
}
if not self.trade_config.enabled:
return result
# 1. 信号层生成信号
signals = self._signal_service.generate_signals(symbol, current_price)
result["signals_generated"] = len(signals)
if signals:
print(f"[TradingServer] {symbol} 生成了 {len(signals)} 个信号")
# 2. 策略层做决策
decision = self.strategy_service.make_decision(symbol, current_price)
if decision:
# 记录决策历史
self._decision_history.append(decision)
result["decision"] = decision.to_dict()
# 广播决策
self._broadcast_decision(decision)
# 3. 自动执行决策(如果允许)
if decision.action != "none" and decision.status != "rejected":
order_id = self.strategy_service.execute_decision(decision)
if order_id:
result["pending_order"] = {
"order_id": order_id,
"symbol": decision.symbol,
"action": decision.action,
"price": decision.entry_price,
"volume": decision.volume,
"sl": decision.sl,
"tp": decision.tp
}
return result
# ==================== 订单确认回调 ====================
def _on_order_confirmed(self, order: PendingOrder):
"""订单确认回调"""
print(f"[TradingServer] 订单确认: {order.order_id}")
# 广播订单确认
self._broadcast_pending_order(order)
try:
# 创建交易指令
instruction = TradeInstruction(
symbol=order.get('symbol', ''),
action=order.get('action', 'b'),
mount=order.get('mount', 0.01),
price=order.get('price', 0),
sl=order.get('sl', 0),
tp=order.get('tp', 0),
description=order.get('description', '')
)
print(f"[TradingServer] 创建交易指令: symbol={instruction.symbol}, action={instruction.action}, mount={instruction.mount}, price={instruction.price}, sl={instruction.sl}, tp={instruction.tp}, description={instruction.description}")
# 添加到交易队列
result = self.add_trade_instruction([instruction])
print(f"[TradingServer] 订单已加入交易队列: {result}")
instruction_id = self.trading_instruction_service.create_from_pending_order(order)
print(f"[TradingServer] 交易指令已创建: {instruction_id}")
except Exception as e:
print(f"[TradingServer] 加入交易队列失败: {e}")
print(f"[TradingServer] 创建交易指令失败: {e}")
import traceback
traceback.print_exc()
def add_trade_instruction(self, instructions: List[TradeInstruction]) -> dict:
"""
添加交易指令
返回一个字典,包含添加和拒绝的数量。
在此处对缺失的 sl/tp 值进行补全:
- sl 若未设置保持0.0
- tp 若未设置则默认 0.005
同时按照规则进行价格检查:
* 买入指令: price 需大于 sl 且小于 tp
* 卖出指令: price 需小于 sl 且大于 tp
若 sl 或 tp 未设置(<=0),则忽略检查。
"""
with self.lock:
added = 0
rejected = 0
for instruction in instructions:
# 填充默认值
if instruction.sl is None:
instruction.sl = 0.0
if instruction.tp is None or instruction.tp <= 0.0:
instruction.tp = 0.005
# 验证价格与 SL/TP 关系
if instruction.sl > 0 and instruction.tp > 0:
if instruction.action.lower() == 'b':
if not (instruction.price > instruction.sl and instruction.price < instruction.tp):
print(f"[警告] 忽略无效买入指令: {instruction}")
rejected += 1
continue
elif instruction.action.lower() == 's':
if not (instruction.price < instruction.sl and instruction.price > instruction.tp):
print(f"[警告] 忽略无效卖出指令: {instruction}")
rejected += 1
continue
# 直接使用原始symbol,不做转换
symbol = instruction.symbol
self.trade_instructions[symbol].append(instruction)
added += 1
print(f"[信息] 已添加 {added} 条交易指令, 拒绝 {rejected}")
for symbol, trades in self.trade_instructions.items():
print(f" {symbol}: {len(trades)} 条待执行")
return {"added": added, "rejected": rejected}
# ==================== EA 接口 ====================
def get_trades_by_symbol(self, symbol: str, price: Optional[float] = None) -> Dict:
"""
获取指定SYMBOL的交易指令并删除
EA获取交易数据
同时调用策略检查(在 PivotMonitor 中执行)
返回: {"trades": [...], "pivot_alerts": [...]}
返回:
- trades: 待执行的交易指令
- pending_orders: 待确认的订单
- close_tickets: 平仓指令
"""
# 检查所有策略(关键点位、支撑压力、AI趋势
pivot_alerts = []
# 处理价格(生成信号和决策
process_result = {}
if price is not None:
pivot_alerts = self.pivot_monitor.check_and_alert(symbol, price)
if pivot_alerts:
print(f"[信息] {symbol} 当前价格 {price} 接近转折点")
process_result = self.process_price(symbol, price)
with self.lock:
# 调试:打印当前所有待执行指令
if len(self.trade_instructions) > 0:
print(f"[调试] get_trades_by_symbol 查询symbol={symbol}")
print(f"[调试] 当前trade_instructions keys: {list(self.trade_instructions.keys())}")
for k, v in self.trade_instructions.items():
print(f"[调试] {k}: {len(v)}")
# 获取交易指令
trades = self.trading_instruction_service.fetch_instructions_for_ea(symbol, price)
if symbol not in self.trade_instructions or len(self.trade_instructions[symbol]) == 0:
return {"trades": [], "pivot_alerts": pivot_alerts}
# 获取待确认订单
pending_orders = self.pending_order_service.get_pending_orders_dict(symbol)
# 获取所有指令并直接返回
trades = self.trade_instructions[symbol]
result = [{
"symbol": t.symbol,
"action": t.action.lower(),
"mount": t.mount,
"price": t.price,
"sl": t.sl,
"tp": t.tp,
"description": t.description or ""
} for t in trades]
# 获取平仓指令
close_tickets = self.get_close_position_instructions(symbol)
# 清空指令队列
self.trade_instructions[symbol] = []
return {
"trades": trades,
"pending_orders": pending_orders,
"close_tickets": close_tickets,
"process_result": process_result
}
if len(result) > 0:
print(f"[信息] 推送了 {len(result)}{symbol} 指令给EA")
# ==================== 交易员接口 ====================
return {"trades": result, "pivot_alerts": pivot_alerts}
def add_trade_instruction(self, instructions: List[TradeInstruction]) -> dict:
"""添加交易指令(交易员手动下单)"""
added = 0
rejected = 0
for instruction in instructions:
sl = instruction.sl if instruction.sl is not None else 0.0
tp = instruction.tp if instruction.tp is not None and instruction.tp > 0 else 0.005
# 验证止损止盈
if sl > 0 and tp > 0:
if instruction.action.lower() == 'b':
if not (instruction.price > sl and instruction.price < tp):
print(f"[警告] 忽略无效买入指令: {instruction}")
rejected += 1
continue
elif instruction.action.lower() == 's':
if not (instruction.price < sl and instruction.price > tp):
print(f"[警告] 忽略无效卖出指令: {instruction}")
rejected += 1
continue
self.trading_instruction_service.create_instruction(
symbol=instruction.symbol,
action=instruction.action,
price=instruction.price,
mount=instruction.mount,
sl=sl,
tp=tp,
description=instruction.description or "",
source="manual"
)
added += 1
print(f"[TradingServer] 已添加 {added} 条交易指令, 拒绝 {rejected}")
return {"added": added, "rejected": rejected}
# ==================== 统计数据 ====================
def save_statistics(self, stat_data: dict) -> None:
"""
保存统计数据
自动保留最新10条
"""
with self.lock:
self.statistics_history.append(stat_data)
print(f"[信息] 统计数据已记录 - {stat_data.get('timestamp', 'unknown')}")
print(f" 当前保存数据条数: {len(self.statistics_history)}")
"""保存统计数据"""
self.statistics_service.process_statistics(stat_data)
def get_latest_statistics(self, count: int = 10) -> List[Dict]:
"""
获取最新的统计数据
"""
with self.lock:
return list(self.statistics_history)[-count:]
"""获取最新的统计数据"""
stats = self.statistics_store.get_all_recent(count)
return [s.to_dict() for s in stats]
# ==================== 指令管理 ====================
def get_all_pending_trades(self) -> Dict[str, List[Dict]]:
"""
获取所有待执行的交易指令(不删除)
用于查询接口
"""
with self.lock:
result = {}
for symbol, trades in self.trade_instructions.items():
result[symbol] = [
{
"symbol": t.symbol,
"action": t.action.lower(),
"mount": t.mount,
"price": t.price,
"sl": t.sl,
"tp": t.tp,
"description": t.description or ""
}
for t in trades
]
return result
"""获取所有待执行的交易指令"""
return self.trading_instruction_service.get_all_instructions_dict()
def clear_trades(self, symbol: Optional[str] = None) -> int:
"""
清空交易指令
如果指定symbol则只清空该symbol
返回清空的指令数量
"""
with self.lock:
if symbol is None:
total = sum(len(trades) for trades in self.trade_instructions.values())
self.trade_instructions.clear()
print(f"[信息] 已清空所有交易指令,共 {total}")
return total
else:
count = len(self.trade_instructions.get(symbol, []))
if symbol in self.trade_instructions:
del self.trade_instructions[symbol]
print(f"[信息] 已清空 {symbol} 的交易指令,共 {count}")
return count
"""清空交易指令"""
if symbol is None:
count = self.trading_instruction_service.get_total_count()
self.trading_instruction_service.clear_all()
print(f"[TradingServer] 已清空所有交易指令,共 {count}")
return count
else:
return self.trading_instruction_service.clear_by_symbol(symbol)
def add_close_position_instruction(self, symbol: str, ticket: int) -> None:
"""
添加平仓指令
"""
"""添加平仓指令"""
with self.lock:
self.close_position_instructions[symbol].append(ticket)
print(f"[信息] 添加平仓指令: {symbol} ticket={ticket}")
if not hasattr(self, '_close_position_instructions'):
self._close_position_instructions = defaultdict(list)
self._close_position_instructions[symbol].append(ticket)
print(f"[TradingServer] 添加平仓指令: {symbol} ticket={ticket}")
def get_close_position_instructions(self, symbol: str) -> List[int]:
"""
获取并清空平仓指令
"""
"""获取并清空平仓指令"""
with self.lock:
tickets = self.close_position_instructions.get(symbol, [])
self.close_position_instructions[symbol] = []
if not hasattr(self, '_close_position_instructions'):
return []
tickets = self._close_position_instructions.get(symbol, [])
self._close_position_instructions[symbol] = []
if tickets:
print(f"[信息] 返回平仓指令: {symbol} tickets={tickets}")
print(f"[TradingServer] 返回平仓指令: {symbol} tickets={tickets}")
return tickets
# ==================== 决策历史 ====================
def get_decision_history(self, symbol: str = None, count: int = 20) -> List[Dict]:
"""获取决策历史"""
decisions = list(self._decision_history)
if symbol:
decisions = [d for d in decisions if d.symbol == symbol]
return [d.to_dict() for d in decisions[-count:]]
# ==================== LLM 分析接口(内部封装) ====================
def get_llm_analysis(self, symbol: str = None) -> Dict:
"""获取大模型分析结果"""
return self.llm_analyzer.get_analysis(symbol)
def get_llm_status(self) -> Dict:
"""获取大模型分析器状态"""
status = self.llm_analyzer.get_status()
status["interval_seconds"] = self.llm_analyzer.ANALYZE_INTERVAL
return status
def get_llm_config(self) -> Dict:
"""获取大模型配置"""
return self.llm_analyzer.get_config()
def trigger_llm_analysis(self) -> Dict:
"""手动触发大模型分析"""
return self.llm_analyzer.trigger_analysis()
def configure_llm(self, api_key: str = None, api_base: str = None, model: str = None) -> Dict:
"""配置大模型参数"""
return self.llm_analyzer.configure(api_key, api_base, model)
# ==================== 状态查询 ====================
def get_status(self) -> Dict:
"""获取服务状态"""
return {
"ws_clients": self.get_ws_client_count(),
"statistics": self.statistics_service.get_status(),
"positions": self.position_service.get_status(),
"trade_history": self.trade_history_service.get_status(),
"pending_orders": self.pending_order_service.get_status(),
"trading_instructions": self.trading_instruction_service.get_status(),
"strategy_service": self.strategy_service.get_status(),
}