You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
buffer_platform/app/analysis_db.py

173 lines
6.1 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

"""
期货智析数据库 - 独立存储
"""
import logging
from pathlib import Path
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, declarative_base
from datetime import datetime
from app.mysql_database import MySQLSessionLocal
from app.storage_manager import get_storage_manager
logger = logging.getLogger(__name__)
# 数据目录
DATA_DIR = Path(__file__).resolve().parent.parent / "data"
DATA_DIR.mkdir(parents=True, exist_ok=True)
ANALYSIS_DB_PATH = DATA_DIR / "futures_analysis.db"
analysis_engine = create_engine(
f"sqlite:///{ANALYSIS_DB_PATH}",
connect_args={"check_same_thread": False},
pool_pre_ping=True,
)
AnalysisSessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=analysis_engine)
AnalysisBase = declarative_base()
def get_analysis_db():
"""获取期货智析数据库会话。
根据 MySQL 可用性动态选择后端:
- MySQL 可用时返回 MySQL Session
- MySQL 不可用时返回 SQLite Session兜底
"""
storage = get_storage_manager()
if MySQLSessionLocal is not None and storage.check_mysql():
db = None
try:
db = MySQLSessionLocal()
yield db
except Exception as e:
logger.warning(f"创建 MySQL analysis session 失败,降级到 SQLite: {e}")
if db is not None:
try:
db.close()
except Exception:
pass
db = AnalysisSessionLocal()
yield db
finally:
if db is not None:
try:
db.close()
except Exception:
pass
else:
db = AnalysisSessionLocal()
try:
yield db
finally:
db.close()
def init_analysis_db():
"""初始化期货智析数据库表SQLite 兜底)。"""
# 确保导入所有模型类,使其注册到 AnalysisBase
from app import analysis_models
# 直接导入 analysis_models 模块中的所有类
from app.analysis_models import (
FuturesAnalysis, WatchedSymbol, AIModelConfig, AnalysisSettings,
AIAnalysisCache, ReviewDate, SymbolRanking, TradingPlan,
SymbolScoreV2, TradingPlanV2, SectorHeat, ReviewPlanV2,
TradeRecord, TradeImportBatch,
TradePair, TradeReflection, DailyReflection, TradeTag, TradeRecordTag, TradeExperience,
)
AnalysisBase.metadata.create_all(bind=analysis_engine)
# 迁移为已有表添加新增列SQLite 不支持 IF NOT EXISTS用异常处理
_migrate_add_columns()
# 初始化预设标签
_init_preset_tags()
def init_analysis_mysql(mysql_engine):
"""初始化期货智析数据库表MySQL"""
from app import analysis_models
from app.analysis_models import (
FuturesAnalysis, WatchedSymbol, AIModelConfig, AnalysisSettings,
AIAnalysisCache, ReviewDate, SymbolRanking, TradingPlan,
SymbolScoreV2, TradingPlanV2, SectorHeat, ReviewPlanV2,
TradeRecord, TradeImportBatch,
TradePair, TradeReflection, DailyReflection, TradeTag, TradeRecordTag, TradeExperience,
)
AnalysisBase.metadata.create_all(bind=mysql_engine)
logger.info("MySQL analysis 表结构初始化完成")
def _migrate_add_columns():
"""为已有表添加新增列(兼容旧数据库)"""
import sqlite3
conn = sqlite3.connect(str(ANALYSIS_DB_PATH))
cursor = conn.cursor()
migrations = [
("symbol_scores_v2", "data_date", "VARCHAR(16)"),
("review_plans_v2", "actual_data_date", "VARCHAR(16)"),
("review_plans_v2", "data_date_matches", "INTEGER"),
]
for table, column, col_type in migrations:
try:
cursor.execute(f"ALTER TABLE {table} ADD COLUMN {column} {col_type}")
except sqlite3.OperationalError:
pass # 列已存在
conn.commit()
conn.close()
def _init_preset_tags():
"""初始化预设标签"""
from app.analysis_models import TradeTag
from sqlalchemy import inspect
session = get_analysis_session()
try:
# 检查表是否存在
inspector = inspect(session.get_bind())
if 'trade_tags' not in inspector.get_table_names():
return
# 检查是否已有预设标签
existing = session.query(TradeTag).filter_by(is_preset=True).first()
if existing:
return
preset_tags = [
# 操作类
TradeTag(name="追涨杀跌", category="operation", is_preset=True),
TradeTag(name="逆市操作", category="operation", is_preset=True),
TradeTag(name="提前入场", category="operation", is_preset=True),
TradeTag(name="延迟出场", category="operation", is_preset=True),
TradeTag(name="加仓过早", category="operation", is_preset=True),
TradeTag(name="减仓过晚", category="operation", is_preset=True),
# 心态类
TradeTag(name="情绪化交易", category="mindset", is_preset=True),
TradeTag(name="恐惧平仓", category="mindset", is_preset=True),
TradeTag(name="贪婪持仓", category="mindset", is_preset=True),
TradeTag(name="侥幸心理", category="mindset", is_preset=True),
# 纪律类
TradeTag(name="严格执行计划", category="discipline", is_preset=True),
TradeTag(name="违反止损", category="discipline", is_preset=True),
TradeTag(name="超仓交易", category="discipline", is_preset=True),
TradeTag(name="频繁交易", category="discipline", is_preset=True),
# 技术类
TradeTag(name="突破交易", category="technical", is_preset=True),
TradeTag(name="回调交易", category="technical", is_preset=True),
TradeTag(name="趋势跟踪", category="technical", is_preset=True),
TradeTag(name="震荡交易", category="technical", is_preset=True),
TradeTag(name="反转交易", category="technical", is_preset=True),
]
session.add_all(preset_tags)
session.commit()
except Exception:
session.rollback()
finally:
session.close()