diff --git a/app/__pycache__/__init__.cpython-311.pyc b/app/__pycache__/__init__.cpython-311.pyc index a2eb7f4..e36685f 100644 Binary files a/app/__pycache__/__init__.cpython-311.pyc and b/app/__pycache__/__init__.cpython-311.pyc differ diff --git a/app/__pycache__/config.cpython-311.pyc b/app/__pycache__/config.cpython-311.pyc index bb4ef7d..284079a 100644 Binary files a/app/__pycache__/config.cpython-311.pyc and b/app/__pycache__/config.cpython-311.pyc differ diff --git a/app/__pycache__/database.cpython-311.pyc b/app/__pycache__/database.cpython-311.pyc index caaca94..d036706 100644 Binary files a/app/__pycache__/database.cpython-311.pyc and b/app/__pycache__/database.cpython-311.pyc differ diff --git a/app/__pycache__/main.cpython-311.pyc b/app/__pycache__/main.cpython-311.pyc index 27e7430..d78f12f 100644 Binary files a/app/__pycache__/main.cpython-311.pyc and b/app/__pycache__/main.cpython-311.pyc differ diff --git a/app/__pycache__/models.cpython-311.pyc b/app/__pycache__/models.cpython-311.pyc index 84b3f06..696a88a 100644 Binary files a/app/__pycache__/models.cpython-311.pyc and b/app/__pycache__/models.cpython-311.pyc differ diff --git a/app/__pycache__/schemas.cpython-311.pyc b/app/__pycache__/schemas.cpython-311.pyc index 7cafdde..44e880f 100644 Binary files a/app/__pycache__/schemas.cpython-311.pyc and b/app/__pycache__/schemas.cpython-311.pyc differ diff --git a/app/analysis_db.py b/app/analysis_db.py index c115945..c8a04fa 100644 --- a/app/analysis_db.py +++ b/app/analysis_db.py @@ -7,9 +7,6 @@ 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__) # 数据目录 @@ -31,38 +28,14 @@ AnalysisBase = declarative_base() def get_analysis_db(): """获取期货智析数据库会话。 - 根据 MySQL 可用性动态选择后端: - - MySQL 可用时返回 MySQL Session - - MySQL 不可用时返回 SQLite Session(兜底) + 分析数据量较小(复盘计划、评分等),始终使用 SQLite 以保证稳定性。 + AnalysisBase 绑定的是 analysis_engine (SQLite),ORM 模型均基于此定义。 """ - 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() + db = AnalysisSessionLocal() + try: + yield db + finally: + db.close() def init_analysis_db(): diff --git a/app/api/__pycache__/__init__.cpython-311.pyc b/app/api/__pycache__/__init__.cpython-311.pyc index 281e2fe..62c7309 100644 Binary files a/app/api/__pycache__/__init__.cpython-311.pyc and b/app/api/__pycache__/__init__.cpython-311.pyc differ diff --git a/app/api/__pycache__/ai_config.cpython-311.pyc b/app/api/__pycache__/ai_config.cpython-311.pyc index f17a40c..5d63489 100644 Binary files a/app/api/__pycache__/ai_config.cpython-311.pyc and b/app/api/__pycache__/ai_config.cpython-311.pyc differ diff --git a/app/api/__pycache__/config.cpython-311.pyc b/app/api/__pycache__/config.cpython-311.pyc index 1e93bb5..befc1ee 100644 Binary files a/app/api/__pycache__/config.cpython-311.pyc and b/app/api/__pycache__/config.cpython-311.pyc differ diff --git a/app/api/__pycache__/data.cpython-311.pyc b/app/api/__pycache__/data.cpython-311.pyc index 5d3f8c9..af85d31 100644 Binary files a/app/api/__pycache__/data.cpython-311.pyc and b/app/api/__pycache__/data.cpython-311.pyc differ diff --git a/app/api/__pycache__/futures_analysis.cpython-311.pyc b/app/api/__pycache__/futures_analysis.cpython-311.pyc index 7dd0b8a..d52cabe 100644 Binary files a/app/api/__pycache__/futures_analysis.cpython-311.pyc and b/app/api/__pycache__/futures_analysis.cpython-311.pyc differ diff --git a/app/api/__pycache__/tasks.cpython-311.pyc b/app/api/__pycache__/tasks.cpython-311.pyc index 99a8554..3475082 100644 Binary files a/app/api/__pycache__/tasks.cpython-311.pyc and b/app/api/__pycache__/tasks.cpython-311.pyc differ diff --git a/app/services/__pycache__/__init__.cpython-311.pyc b/app/services/__pycache__/__init__.cpython-311.pyc index 030fe03..f4c03da 100644 Binary files a/app/services/__pycache__/__init__.cpython-311.pyc and b/app/services/__pycache__/__init__.cpython-311.pyc differ diff --git a/app/services/__pycache__/cache.cpython-311.pyc b/app/services/__pycache__/cache.cpython-311.pyc index aca66dc..6486e2b 100644 Binary files a/app/services/__pycache__/cache.cpython-311.pyc and b/app/services/__pycache__/cache.cpython-311.pyc differ diff --git a/app/services/__pycache__/collector.cpython-311.pyc b/app/services/__pycache__/collector.cpython-311.pyc index 23034c8..ddd278d 100644 Binary files a/app/services/__pycache__/collector.cpython-311.pyc and b/app/services/__pycache__/collector.cpython-311.pyc differ diff --git a/app/services/__pycache__/scheduler.cpython-311.pyc b/app/services/__pycache__/scheduler.cpython-311.pyc index 4d6baf5..e3265c7 100644 Binary files a/app/services/__pycache__/scheduler.cpython-311.pyc and b/app/services/__pycache__/scheduler.cpython-311.pyc differ diff --git a/app/services/plan_generator.py b/app/services/plan_generator.py index 1857194..20e24ff 100644 --- a/app/services/plan_generator.py +++ b/app/services/plan_generator.py @@ -626,121 +626,126 @@ def save_plan_to_db(db: Session, plan_data: dict) -> int: review_date_str = plan_data["review_date"] week_day = plan_data["week_day"] - # 清理旧数据 - existing = db.query(ReviewPlanV2).filter_by(review_date=review_date_str).first() - if existing: - review_plan_id = existing.id - # 清理关联数据 - db.query(SymbolScoreV2).filter_by(review_date_id=review_plan_id).delete() - db.query(TradingPlanV2).filter_by(review_date_id=review_plan_id).delete() - db.query(SectorHeat).filter_by(review_date_id=review_plan_id).delete() - # 更新元数据 - existing.week_day = week_day - existing.data_basis = plan_data.get("data_basis") - existing.core_conclusion = plan_data.get("core_conclusion") - existing.bull_count = plan_data.get("bull_count") - existing.bear_count = plan_data.get("bear_count") - existing.neutral_count = plan_data.get("neutral_count") - existing.opportunity_count = plan_data.get("opportunity_count") - existing.risk_warnings = plan_data.get("risk_warnings") - existing.actual_data_date = plan_data.get("actual_data_date") - existing.data_date_matches = 1 if plan_data.get("data_date_matches") else 0 - else: - review_plan = ReviewPlanV2( - review_date=review_date_str, - week_day=week_day, - data_basis=plan_data.get("data_basis"), - core_conclusion=plan_data.get("core_conclusion"), - bull_count=plan_data.get("bull_count"), - bear_count=plan_data.get("bear_count"), - neutral_count=plan_data.get("neutral_count"), - opportunity_count=plan_data.get("opportunity_count"), - risk_warnings=plan_data.get("risk_warnings"), - actual_data_date=plan_data.get("actual_data_date"), - data_date_matches=1 if plan_data.get("data_date_matches") else 0, - ) - db.add(review_plan) - db.flush() - review_plan_id = review_plan.id - - # 保存评分数据 - for s in plan_data.get("scores", []): - pivots = s.get("pivots", {}) - score_record = SymbolScoreV2( - review_date_id=review_plan_id, - symbol=s["symbol"], - name=s["name"], - close_price=s.get("close_price"), - prev_close=s.get("prev_close"), - high_price=s.get("high_price"), - low_price=s.get("low_price"), - volume=s.get("volume"), - avg_volume_5=s.get("avg_volume_5"), - amplitude_score=s.get("amplitude_score"), - volume_score=s.get("volume_score"), - change_score=s.get("change_score"), - trend_score=s.get("trend_score"), - activity_score=s.get("activity_score"), - composite_score=s.get("composite_score"), - amplitude_pct=s.get("amplitude_pct"), - change_pct=s.get("change_pct"), - volume_ratio=s.get("volume_ratio"), - trend_60m=s.get("trend_60m"), - trend_15m=s.get("trend_15m"), - trend_5m=s.get("trend_5m"), - direction=s.get("direction"), - direction_tag=s.get("direction_tag"), - category=s.get("category"), - pivot=pivots.get("pivot"), - r1=pivots.get("r1"), - r2=pivots.get("r2"), - s1=pivots.get("s1"), - s2=pivots.get("s2"), - rank=s.get("rank"), - data_date=s.get("data_date"), - ) - db.add(score_record) - - # 保存交易计划 - for p in plan_data.get("plans", []): - plan_record = TradingPlanV2( - review_date_id=review_plan_id, - symbol=p["symbol"], - name=p["name"], - direction=p["direction"], - composite_score=p.get("composite_score"), - entry_low=p.get("entry_low"), - entry_high=p.get("entry_high"), - stop_loss=p.get("stop_loss"), - target1=p.get("target1"), - target2=p.get("target2"), - trigger=p.get("trigger"), - amplitude_score=p.get("amplitude_score"), - volume_score=p.get("volume_score"), - trend_score=p.get("trend_score"), - activity_score=p.get("activity_score"), - category=p.get("category"), - ) - db.add(plan_record) - - # 保存板块热度 - for sec in plan_data.get("sectors", []): - sector_record = SectorHeat( - review_date_id=review_plan_id, - sector_name=sec["sector_name"], - avg_score=sec.get("avg_score"), - avg_trend=sec.get("avg_trend"), - direction=sec.get("direction"), - heat_level=sec.get("heat_level"), - leader_symbol=sec.get("leader_symbol"), - leader_score=sec.get("leader_score"), - members=sec.get("members"), - ) - db.add(sector_record) - - db.commit() - logger.info(f"交易计划已保存: {review_date_str}, ID={review_plan_id}") - return review_plan_id + try: + # 清理旧数据 + existing = db.query(ReviewPlanV2).filter_by(review_date=review_date_str).first() + if existing: + review_plan_id = existing.id + # 清理关联数据 + db.query(SymbolScoreV2).filter_by(review_date_id=review_plan_id).delete() + db.query(TradingPlanV2).filter_by(review_date_id=review_plan_id).delete() + db.query(SectorHeat).filter_by(review_date_id=review_plan_id).delete() + # 更新元数据 + existing.week_day = week_day + existing.data_basis = plan_data.get("data_basis") + existing.core_conclusion = plan_data.get("core_conclusion") + existing.bull_count = plan_data.get("bull_count") + existing.bear_count = plan_data.get("bear_count") + existing.neutral_count = plan_data.get("neutral_count") + existing.opportunity_count = plan_data.get("opportunity_count") + existing.risk_warnings = plan_data.get("risk_warnings") + existing.actual_data_date = plan_data.get("actual_data_date") + existing.data_date_matches = 1 if plan_data.get("data_date_matches") else 0 + else: + review_plan = ReviewPlanV2( + review_date=review_date_str, + week_day=week_day, + data_basis=plan_data.get("data_basis"), + core_conclusion=plan_data.get("core_conclusion"), + bull_count=plan_data.get("bull_count"), + bear_count=plan_data.get("bear_count"), + neutral_count=plan_data.get("neutral_count"), + opportunity_count=plan_data.get("opportunity_count"), + risk_warnings=plan_data.get("risk_warnings"), + actual_data_date=plan_data.get("actual_data_date"), + data_date_matches=1 if plan_data.get("data_date_matches") else 0, + ) + db.add(review_plan) + db.flush() + review_plan_id = review_plan.id + + # 保存评分数据 + for s in plan_data.get("scores", []): + pivots = s.get("pivots", {}) + score_record = SymbolScoreV2( + review_date_id=review_plan_id, + symbol=s["symbol"], + name=s["name"], + close_price=s.get("close_price"), + prev_close=s.get("prev_close"), + high_price=s.get("high_price"), + low_price=s.get("low_price"), + volume=s.get("volume"), + avg_volume_5=s.get("avg_volume_5"), + amplitude_score=s.get("amplitude_score"), + volume_score=s.get("volume_score"), + change_score=s.get("change_score"), + trend_score=s.get("trend_score"), + activity_score=s.get("activity_score"), + composite_score=s.get("composite_score"), + amplitude_pct=s.get("amplitude_pct"), + change_pct=s.get("change_pct"), + volume_ratio=s.get("volume_ratio"), + trend_60m=s.get("trend_60m"), + trend_15m=s.get("trend_15m"), + trend_5m=s.get("trend_5m"), + direction=s.get("direction"), + direction_tag=s.get("direction_tag"), + category=s.get("category"), + pivot=pivots.get("pivot"), + r1=pivots.get("r1"), + r2=pivots.get("r2"), + s1=pivots.get("s1"), + s2=pivots.get("s2"), + rank=s.get("rank"), + data_date=s.get("data_date"), + ) + db.add(score_record) + + # 保存交易计划 + for p in plan_data.get("plans", []): + plan_record = TradingPlanV2( + review_date_id=review_plan_id, + symbol=p["symbol"], + name=p["name"], + direction=p["direction"], + composite_score=p.get("composite_score"), + entry_low=p.get("entry_low"), + entry_high=p.get("entry_high"), + stop_loss=p.get("stop_loss"), + target1=p.get("target1"), + target2=p.get("target2"), + trigger=p.get("trigger"), + amplitude_score=p.get("amplitude_score"), + volume_score=p.get("volume_score"), + trend_score=p.get("trend_score"), + activity_score=p.get("activity_score"), + category=p.get("category"), + ) + db.add(plan_record) + + # 保存板块热度 + for sec in plan_data.get("sectors", []): + sector_record = SectorHeat( + review_date_id=review_plan_id, + sector_name=sec["sector_name"], + avg_score=sec.get("avg_score"), + avg_trend=sec.get("avg_trend"), + direction=sec.get("direction"), + heat_level=sec.get("heat_level"), + leader_symbol=sec.get("leader_symbol"), + leader_score=sec.get("leader_score"), + members=sec.get("members"), + ) + db.add(sector_record) + + db.commit() + logger.info(f"交易计划已保存: {review_date_str}, ID={review_plan_id}") + return review_plan_id + except Exception as e: + db.rollback() + logger.exception(f"保存交易计划失败: {e}") + raise def get_plan_data(db: Session, review_plan_id: int) -> Optional[dict]: diff --git a/data/futures_analysis.db b/data/futures_analysis.db index 011ffd1..d2b6dac 100644 Binary files a/data/futures_analysis.db and b/data/futures_analysis.db differ