Files
AI-Trader/server.py
T

477 lines
18 KiB
Python
Raw Permalink 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 collections import deque, defaultdict
from typing import List, Dict, Optional, Set
from datetime import datetime
import threading
import asyncio
import json
from models import TradeInstruction
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):
# 线程锁
self.lock = threading.RLock()
# ==================== 行情模块(内部) ====================
# 存储层
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()
# 注册信号生成器
self._setup_signal_generators()
# ==================== 订单/指令模块 ====================
# 存储层
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:
处理结果
"""
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_id = self.trading_instruction_service.create_from_pending_order(order)
print(f"[TradingServer] 交易指令已创建: {instruction_id}")
except Exception as e:
print(f"[TradingServer] 创建交易指令失败: {e}")
import traceback
traceback.print_exc()
# ==================== EA 接口 ====================
def get_trades_by_symbol(self, symbol: str, price: Optional[float] = None) -> Dict:
"""
EA获取交易数据
返回:
- trades: 待执行的交易指令
- pending_orders: 待确认的订单
- close_tickets: 平仓指令
"""
# 处理价格(生成信号和决策)
process_result = {}
if price is not None:
process_result = self.process_price(symbol, price)
# 获取交易指令
trades = self.trading_instruction_service.fetch_instructions_for_ea(symbol, price)
# 获取待确认订单
pending_orders = self.pending_order_service.get_pending_orders_dict(symbol)
# 获取平仓指令
close_tickets = self.get_close_position_instructions(symbol)
return {
"trades": trades,
"pending_orders": pending_orders,
"close_tickets": close_tickets,
"process_result": process_result
}
# ==================== 交易员接口 ====================
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:
"""保存统计数据"""
self.statistics_service.process_statistics(stat_data)
def get_latest_statistics(self, count: int = 10) -> List[Dict]:
"""获取最新的统计数据"""
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]]:
"""获取所有待执行的交易指令"""
return self.trading_instruction_service.get_all_instructions_dict()
def clear_trades(self, symbol: Optional[str] = None) -> int:
"""清空交易指令"""
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:
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:
if not hasattr(self, '_close_position_instructions'):
return []
tickets = self._close_position_instructions.get(symbol, [])
self._close_position_instructions[symbol] = []
if 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(),
}