50939212be
Signed-off-by: TIANHE <TIANHE@GMAIL.COM>
221 lines
9.2 KiB
Python
221 lines
9.2 KiB
Python
"""
|
|
自动反思与验证服务
|
|
用于记录分析预测,并在未来自动验证结果,实现闭环学习
|
|
"""
|
|
import sqlite3
|
|
import os
|
|
import json
|
|
from datetime import datetime, timedelta
|
|
from typing import List, Dict, Any, Optional
|
|
from app.utils.logger import get_logger
|
|
from .memory import AgentMemory
|
|
from .tools import AgentTools
|
|
|
|
logger = get_logger(__name__)
|
|
|
|
class ReflectionService:
|
|
"""反思服务:管理分析记录的存储和验证"""
|
|
|
|
def __init__(self, db_path: Optional[str] = None):
|
|
if db_path is None:
|
|
# 默认数据库路径
|
|
db_dir = os.path.join(os.path.dirname(__file__), '..', '..', '..', 'data', 'memory')
|
|
os.makedirs(db_dir, exist_ok=True)
|
|
db_path = os.path.join(db_dir, 'reflection_records.db')
|
|
|
|
self.db_path = db_path
|
|
self.tools = AgentTools()
|
|
self._init_database()
|
|
|
|
def _init_database(self):
|
|
"""初始化数据库表"""
|
|
try:
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
|
|
# 创建分析记录表
|
|
cursor.execute('''
|
|
CREATE TABLE IF NOT EXISTS analysis_records (
|
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
market TEXT NOT NULL,
|
|
symbol TEXT NOT NULL,
|
|
initial_price REAL,
|
|
decision TEXT,
|
|
confidence INTEGER,
|
|
reasoning TEXT,
|
|
analysis_date TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
target_check_date TIMESTAMP,
|
|
status TEXT DEFAULT 'PENDING', -- PENDING, COMPLETED, FAILED
|
|
final_price REAL,
|
|
actual_return REAL,
|
|
check_result TEXT
|
|
)
|
|
''')
|
|
|
|
# 创建索引
|
|
cursor.execute('''
|
|
CREATE INDEX IF NOT EXISTS idx_status_date ON analysis_records(status, target_check_date)
|
|
''')
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
except Exception as e:
|
|
logger.error(f"初始化反思数据库失败: {e}")
|
|
|
|
def record_analysis(self, market: str, symbol: str, price: float,
|
|
decision: str, confidence: int, reasoning: str,
|
|
check_days: int = 7):
|
|
"""
|
|
记录一次分析,以便未来验证
|
|
|
|
Args:
|
|
market: 市场
|
|
symbol: 代码
|
|
price: 当前价格
|
|
decision: 决策 (BUY/SELL/HOLD)
|
|
confidence: 置信度
|
|
reasoning: 理由
|
|
check_days: 几天后验证 (默认7天)
|
|
"""
|
|
try:
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
|
|
target_date = datetime.now() + timedelta(days=check_days)
|
|
|
|
cursor.execute('''
|
|
INSERT INTO analysis_records
|
|
(market, symbol, initial_price, decision, confidence, reasoning, target_check_date)
|
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
''', (market, symbol, price, decision, confidence, reasoning, target_date))
|
|
|
|
conn.commit()
|
|
conn.close()
|
|
logger.info(f"Recorded analysis for reflection: {market}:{symbol}, will verify after {check_days} day(s)")
|
|
except Exception as e:
|
|
logger.error(f"记录分析失败: {e}")
|
|
|
|
def run_verification_cycle(self):
|
|
"""
|
|
执行验证周期:检查到期的记录,验证结果,并写入记忆
|
|
"""
|
|
logger.info("开始执行自动反思验证周期...")
|
|
|
|
try:
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
|
|
# 1. 查找所有已到期且未处理的记录
|
|
cursor.execute('''
|
|
SELECT id, market, symbol, initial_price, decision, confidence, reasoning, analysis_date
|
|
FROM analysis_records
|
|
WHERE status = 'PENDING' AND target_check_date <= CURRENT_TIMESTAMP
|
|
''')
|
|
|
|
records = cursor.fetchall()
|
|
|
|
if not records:
|
|
logger.info("没有需要验证的记录")
|
|
conn.close()
|
|
return
|
|
|
|
logger.info(f"发现 {len(records)} 条待验证记录")
|
|
|
|
# 初始化记忆系统(用于写入验证结果)
|
|
trader_memory = AgentMemory('trader_agent')
|
|
|
|
for record in records:
|
|
record_id, market, symbol, initial_price, decision, confidence, reasoning, analysis_date = record
|
|
|
|
try:
|
|
# 2. 获取当前最新价格
|
|
current_price_data = self.tools.get_current_price(market, symbol)
|
|
current_price = current_price_data.get('price')
|
|
|
|
if not current_price:
|
|
logger.warning(f"无法获取 {market}:{symbol} 的当前价格,跳过")
|
|
continue
|
|
|
|
# 3. 计算收益和结果
|
|
if not initial_price or initial_price == 0:
|
|
actual_return = 0.0
|
|
else:
|
|
actual_return = (current_price - initial_price) / initial_price * 100
|
|
|
|
# 评估结果
|
|
result_desc = ""
|
|
is_good_prediction = False
|
|
|
|
if decision == "BUY":
|
|
if actual_return > 2.0:
|
|
result_desc = "Correct: price rose after BUY"
|
|
is_good_prediction = True
|
|
elif actual_return < -2.0:
|
|
result_desc = "Wrong: price fell after BUY"
|
|
else:
|
|
result_desc = "Neutral: limited price movement"
|
|
elif decision == "SELL":
|
|
if actual_return < -2.0:
|
|
result_desc = "Correct: price fell after SELL"
|
|
is_good_prediction = True
|
|
elif actual_return > 2.0:
|
|
result_desc = "Wrong: price rose after SELL"
|
|
else:
|
|
result_desc = "Neutral: limited price movement"
|
|
else: # HOLD
|
|
if -2.0 <= actual_return <= 2.0:
|
|
result_desc = "Correct: limited movement during HOLD"
|
|
is_good_prediction = True
|
|
else:
|
|
result_desc = f"Deviated: large movement during HOLD ({actual_return:.2f}%)"
|
|
|
|
# 4. 写入记忆系统 (Let the agent learn)
|
|
memory_situation = f"{market}:{symbol} auto-verified (analysis_date: {analysis_date})"
|
|
memory_recommendation = f"Decision: {decision} (confidence {confidence}), reasoning: {(reasoning or '')[:120]}"
|
|
memory_result = f"Verification: {result_desc}; return={actual_return:.2f}% (initial {initial_price} -> final {current_price})"
|
|
|
|
trader_memory.add_memory(
|
|
memory_situation,
|
|
memory_recommendation,
|
|
memory_result,
|
|
actual_return,
|
|
metadata={
|
|
"market": market,
|
|
"symbol": symbol,
|
|
"timeframe": "1D",
|
|
"features": {
|
|
"source": "auto_verify",
|
|
"decision": decision,
|
|
"confidence": confidence,
|
|
"initial_price": initial_price,
|
|
"final_price": current_price,
|
|
"analysis_date": str(analysis_date),
|
|
"result_desc": result_desc,
|
|
"is_good_prediction": bool(is_good_prediction),
|
|
},
|
|
}
|
|
)
|
|
|
|
# 5. 更新记录状态
|
|
cursor.execute('''
|
|
UPDATE analysis_records
|
|
SET status = 'COMPLETED', final_price = ?, actual_return = ?, check_result = ?
|
|
WHERE id = ?
|
|
''', (current_price, actual_return, result_desc, record_id))
|
|
|
|
conn.commit()
|
|
logger.info(f"验证完成 {market}:{symbol}: {result_desc}")
|
|
|
|
except Exception as inner_e:
|
|
logger.error(f"处理记录 {record_id} 失败: {inner_e}")
|
|
# 标记为失败,避免重复处理
|
|
# cursor.execute("UPDATE analysis_records SET status = 'FAILED' WHERE id = ?", (record_id,))
|
|
# conn.commit()
|
|
|
|
conn.close()
|
|
logger.info("反思验证周期结束")
|
|
|
|
except Exception as e:
|
|
logger.error(f"执行验证周期失败: {e}")
|
|
|