diff --git a/crypto_monitor_binance/.env.example b/crypto_monitor_binance/.env.example index ae546a6..d6294e7 100644 --- a/crypto_monitor_binance/.env.example +++ b/crypto_monitor_binance/.env.example @@ -52,6 +52,13 @@ UPLOAD_DIR=static/images # BINANCE_FUNDING_INCLUDE_SPOT=false # 计仓:risk=以损定仓(默认);full_margin=合约可用×FULL_MARGIN_BUFFER_RATIO 全仓杠杆(须无仓后重启) POSITION_SIZING_MODE=risk +# 方向限制(默认 false=双向均可;true 时按 TRADE_DIRECTION 限制,修改后须重启) +# TRADE_DIRECTION=long_only | short_only | both(或 多/空/双向) +TRADE_DIRECTION_RESTRICT_ENABLED=false +TRADE_DIRECTION=both +# 币种白名单(默认 false=全币种可手输;true 时关键位/下单/策略仅下拉选择) +TRADE_SYMBOL_RESTRICT_ENABLED=false +TRADE_SYMBOL_WHITELIST=BTC,ETH # 每天起始基数(U) DAILY_START_CAPITAL=30 # 日内回撤后基数(U) diff --git a/crypto_monitor_binance/app.py b/crypto_monitor_binance/app.py index 31b40ca..e12caa2 100644 --- a/crypto_monitor_binance/app.py +++ b/crypto_monitor_binance/app.py @@ -159,6 +159,14 @@ from lib.trade.position_sizing_lib import ( mode_label_zh, risk_percent_for_storage, ) +from lib.trade.trade_policy_lib import load_trade_policy +from lib.trade.trade_policy_app_lib import ( + check_direction_policy, + check_open_policy, + check_symbol_policy, + default_symbol_for_policy, + trade_policy_template_context, +) from lib.key_monitor.key_monitor_full_margin_lib import ( monitor_type_disallowed_in_full_margin, purge_disallowed_key_monitors, @@ -342,6 +350,7 @@ FORCE_CLOSE_BJ_HOUR = int(os.getenv("FORCE_CLOSE_BJ_HOUR", "0")) AUTO_TRANSFER_BJ_HOUR = int(os.getenv("AUTO_TRANSFER_BJ_HOUR", "8")) # 计仓模式:risk=以损定仓(默认);full_margin=合约可用保证金×比例全仓杠杆(仅 env 切换,须无仓) POSITION_SIZING_MODE = load_position_sizing_mode() +TRADE_POLICY = load_trade_policy() WECHAT_TIMEOUT_SECONDS = int(os.getenv("WECHAT_TIMEOUT_SECONDS", "10")) AI_TIMEOUT_SECONDS = int(os.getenv("AI_TIMEOUT_SECONDS", "120")) MONITOR_POLL_SECONDS = int(os.getenv("MONITOR_POLL_SECONDS", "3")) @@ -2035,6 +2044,12 @@ def normalize_symbol_input(symbol): return f"{sym}/USDT" +def validate_trade_policy_open(symbol, direction): + return check_open_policy( + TRADE_POLICY, symbol, direction, normalize_symbol_input + ) + + def normalize_kline_limit(limit_raw, default=200): try: n = int(limit_raw) @@ -7277,6 +7292,7 @@ def render_main_page(page="trade", embed_mode=None): risk_percent=RISK_PERCENT, position_sizing_mode=POSITION_SIZING_MODE, position_sizing_mode_label=mode_label_zh(POSITION_SIZING_MODE), + trade_policy=trade_policy_template_context(TRADE_POLICY), open_position_button_label=( "开仓(全仓杠杆)" if is_full_margin_mode(POSITION_SIZING_MODE) else "开仓(以损定仓)" ), @@ -7996,7 +8012,10 @@ def key_focus(): selected_key = next((k for k in key_list if (k.get("symbol") or "").upper() == symbol_query), None) if selected_key is None and key_list: selected_key = key_list[0] - default_symbol = symbol_query or ((selected_key or {}).get("symbol")) or "BTC/USDT" + default_symbol = default_symbol_for_policy( + TRADE_POLICY, + symbol_query or ((selected_key or {}).get("symbol")) or "BTC/USDT", + ) return render_template( "key_focus_v2.html", key_list=key_list, @@ -8006,6 +8025,7 @@ def key_focus(): default_kline_limit=200, price_refresh_seconds=PRICE_REFRESH_SECONDS, exchange_display=EXCHANGE_DISPLAY_NAME, + trade_policy=trade_policy_template_context(TRADE_POLICY), ) @@ -8116,6 +8136,12 @@ def add_key(): if not symbol: flash("symbol 不能为空") return redirect("/key_monitor") + ok_sym, sym_msg = check_symbol_policy( + TRADE_POLICY, symbol, normalize_symbol_input + ) + if not ok_sym: + flash(sym_msg) + return redirect("/key_monitor") mt = (d.get("type") or "").strip() direction_sel = (d.get("direction") or "").strip().lower() dup_msg = check_duplicate_submit( @@ -8130,6 +8156,10 @@ def add_key(): elif direction_sel not in ("long", "short"): flash("箱体/收敛突破请选择做多或做空") return redirect("/key_monitor") + ok_dir, dir_msg = check_direction_policy(TRADE_POLICY, direction_sel) + if not ok_dir: + flash(dir_msg) + return redirect("/key_monitor") allowed_types = ( tuple(KEY_MONITOR_AUTO_TYPES) + tuple(KEY_MONITOR_ALERT_ONLY_TYPES) @@ -8375,6 +8405,11 @@ def add_order(): conn.close() flash("symbol 不能为空") return redirect("/") + ok_pol, pol_msg = validate_trade_policy_open(symbol, direction) + if not ok_pol: + conn.close() + flash(f"账户限制:{pol_msg}") + return redirect("/trade") dup_msg = check_duplicate_submit(session, submit_scope_add_order(symbol, direction)) if dup_msg: conn.close() @@ -9703,6 +9738,7 @@ def _hub_meta_bundle(): "max_active_positions": MAX_ACTIVE_POSITIONS, "btc_leverage": BTC_LEVERAGE, "alt_leverage": ALT_LEVERAGE, + "trade_policy": trade_policy_template_context(TRADE_POLICY), } diff --git a/crypto_monitor_binance/templates/index.html b/crypto_monitor_binance/templates/index.html index b27d408..e6c487a 100644 --- a/crypto_monitor_binance/templates/index.html +++ b/crypto_monitor_binance/templates/index.html @@ -243,7 +243,7 @@ .stats-period-block h3{font-size:1rem;color:#dbe4ff;margin-bottom:4px} .stats-period-block .sub{font-size:.78rem;color:#8892b0;margin-bottom:10px;line-height:1.4} - +
加密货币|交易监控 + AI复盘一体化