From 380ce7cda9c41882890472c8ff2d334bf5ade2a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=96=87=E8=96=87=E5=AE=89?= Date: Sat, 31 Jan 2026 10:14:57 +0800 Subject: [PATCH] a --- backend/database/add_entry_context.sql | 21 +++++++ backend/sync_global_config_defaults.py | 87 ++++++++++++++++++++++++++ 2 files changed, 108 insertions(+) create mode 100644 backend/database/add_entry_context.sql create mode 100644 backend/sync_global_config_defaults.py diff --git a/backend/database/add_entry_context.sql b/backend/database/add_entry_context.sql new file mode 100644 index 0000000..ca659e2 --- /dev/null +++ b/backend/database/add_entry_context.sql @@ -0,0 +1,21 @@ +-- 为 trades 表添加「入场思路/过程」字段,便于事后分析策略执行效果 +-- 存储 JSON:signal_strength, market_regime, trend_4h, change_percent, rsi, reason, volume_confirmed 等 + +-- 使用动态 SQL 检查列是否存在(兼容已有库) +SET @column_exists = ( + SELECT COUNT(*) + FROM information_schema.columns + WHERE table_schema = DATABASE() + AND table_name = 'trades' + AND column_name = 'entry_context' +); + +SET @sql = IF(@column_exists = 0, + 'ALTER TABLE `trades` ADD COLUMN `entry_context` JSON NULL COMMENT ''入场时的思路与过程(信号强度、市场状态、趋势、过滤通过情况等),便于综合分析策略执行效果'' AFTER `entry_reason`', + 'SELECT "entry_context 列已存在,跳过添加" AS message' +); +PREPARE stmt FROM @sql; +EXECUTE stmt; +DEALLOCATE PREPARE stmt; + +SELECT 'Migration completed: entry_context added to trades (if not exists).' AS result; diff --git a/backend/sync_global_config_defaults.py b/backend/sync_global_config_defaults.py new file mode 100644 index 0000000..756c3aa --- /dev/null +++ b/backend/sync_global_config_defaults.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 +""" +将“缺省全局配置项”同步到数据库 global_strategy_config 表。 + +- 已在 UI 保存过的项不会覆盖(只插入缺失的 key)。 +- 用于新上线配置项(如 MAX_RSI_FOR_LONG、MIN_RSI_FOR_SHORT 等)一次性写入默认值, + 便于在数据库中可见、可备份,且不依赖“先在页面改一次再保存”。 + +使用方式(在项目根目录): + cd backend && python sync_global_config_defaults.py + 或 + python backend/sync_global_config_defaults.py +""" +import os +import sys +from pathlib import Path + +# 确保 backend 在路径中 +backend_dir = Path(__file__).resolve().parent +if str(backend_dir) not in sys.path: + sys.path.insert(0, str(backend_dir)) + +# 需要同步的缺省项(仅插入数据库中不存在的 key) +DEFAULTS_TO_SYNC = [ + {"config_key": "MAX_RSI_FOR_LONG", "config_value": "70", "config_type": "number", "category": "strategy", + "description": "做多时 RSI 超过此值则不开多(避免超买区追多)。2026-01-31新增。"}, + {"config_key": "MAX_CHANGE_PERCENT_FOR_LONG", "config_value": "25", "config_type": "number", "category": "strategy", + "description": "做多时 24h 涨跌幅超过此值则不开多(避免追大涨)。单位:百分比数值,如 25 表示 25%。2026-01-31新增。"}, + {"config_key": "MIN_RSI_FOR_SHORT", "config_value": "30", "config_type": "number", "category": "strategy", + "description": "做空时 RSI 低于此值则不做空(避免深超卖反弹)。2026-01-31新增。"}, + {"config_key": "MAX_CHANGE_PERCENT_FOR_SHORT", "config_value": "10", "config_type": "number", "category": "strategy", + "description": "做空时 24h 涨跌幅超过此值则不做空(24h 仍大涨时不做空)。单位:百分比数值。2026-01-31新增。"}, + {"config_key": "TAKE_PROFIT_1_PERCENT", "config_value": "0.15", "config_type": "number", "category": "strategy", + "description": "分步止盈第一目标(保证金百分比,如 0.15=15%)。第一目标触发后了结50%仓位,剩余追求第二目标。"}, + {"config_key": "SCAN_EXTRA_SYMBOLS_FOR_SUPPLEMENT", "config_value": "8", "config_type": "number", "category": "scan", + "description": "智能补单:多返回的候选数量。当前 TOP_N 中部分因冷却等被跳过时,仍会尝试这批额外候选,避免无单可下。"}, + {"config_key": "BETA_FILTER_ENABLED", "config_value": "true", "config_type": "boolean", "category": "strategy", + "description": "大盘共振过滤:BTC/ETH 下跌时屏蔽多单。"}, + {"config_key": "BETA_FILTER_THRESHOLD", "config_value": "-0.005", "config_type": "number", "category": "strategy", + "description": "大盘共振阈值(比例,如 -0.005 表示 -0.5%)。"}, +] + + +def main(): + try: + from database.models import GlobalStrategyConfig + from database.connection import db + except ImportError as e: + print(f"无法导入数据库模块,请确保在 backend 目录或设置 PYTHONPATH: {e}") + sys.exit(1) + + def _table_has_column(table: str, col: str) -> bool: + try: + db.execute_one(f"SELECT {col} FROM {table} LIMIT 1") + return True + except Exception: + return False + + if not _table_has_column("global_strategy_config", "config_key"): + print("表 global_strategy_config 不存在或结构异常,请先执行 backend/database/add_global_strategy_config.sql") + sys.exit(1) + + inserted = 0 + skipped = 0 + for row in DEFAULTS_TO_SYNC: + key = row["config_key"] + existing = GlobalStrategyConfig.get(key) + if existing: + skipped += 1 + print(f" 已有: {key}") + continue + GlobalStrategyConfig.set( + key, + row["config_value"], + row["config_type"], + row["category"], + row.get("description"), + updated_by="sync_global_config_defaults", + ) + inserted += 1 + print(f" 插入: {key} = {row['config_value']}") + + print(f"\n同步完成: 新增 {inserted} 项,已存在跳过 {skipped} 项。") + + +if __name__ == "__main__": + main()