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

"""
期货智析数据库 - 独立存储
"""
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()