diff --git a/crypto_monitor_binance/app.py b/crypto_monitor_binance/app.py index e7b7f44..31a54bc 100644 --- a/crypto_monitor_binance/app.py +++ b/crypto_monitor_binance/app.py @@ -99,6 +99,17 @@ from key_sl_tp_lib import ( sl_tp_mode_label, sl_tp_plan_summary_text, ) +from time_close_lib import ( + TIME_CLOSE_RESULT, + apply_time_close_to_payload, + ensure_time_close_schema, + parse_time_close_enabled_form, + parse_time_close_hours_form, + should_trigger_time_close, + time_close_insert_values, + time_close_label, + time_close_settings_from_row, +) from manual_sltp_lib import ( normalize_open_sltp_mode, resolve_entrust_sltp_prices, @@ -1431,6 +1442,7 @@ def init_db(): c.execute(ddl) except Exception: pass + ensure_time_close_schema(c) try: c.execute("ALTER TABLE trading_sessions ADD COLUMN key_sizing_capital_snapshot REAL") @@ -4534,6 +4546,8 @@ def _market_open_for_key_monitor( take_profit, key_signal_type=None, breakeven_enabled=0, + time_close_enabled=0, + time_close_hours=None, ): """ 与手动「实盘下单」对齐的市价开仓与 order_monitors 写入(Binance U 本位)。 @@ -5073,7 +5087,10 @@ def check_fib_key_monitors(): conn.close() -def _add_fib_key_monitor(conn, symbol, direction_sel, mt, upper_px, lower_px, breakeven_enabled=0): +def _add_fib_key_monitor( + conn, symbol, direction_sel, mt, upper_px, lower_px, breakeven_enabled=0, + time_close_enabled=0, time_close_hours=None, +): if _fib_key_exists_for_symbol(conn, symbol): return False, f"{symbol} 已有斐波监控(同币仅允许一条 0.618/0.786)" ratio = fib_ratio_from_type(mt) @@ -5134,15 +5151,16 @@ def _add_fib_key_monitor(conn, symbol, direction_sel, mt, upper_px, lower_px, br except Exception as e: return False, friendly_exchange_error(e, available_usdt=available_usdt) be_flag = 1 if int(breakeven_enabled or 0) != 0 else 0 + tc_en, tc_h, _ = time_close_insert_values(time_close_enabled, time_close_hours, None) conn.execute( "INSERT INTO key_monitors " "(symbol, monitor_type, direction, upper, lower, " "fib_limit_order_id, fib_entry_price, fib_stop_loss, fib_take_profit, " - "fib_order_amount, fib_margin_capital, fib_leverage, breakeven_enabled) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", + "fib_order_amount, fib_margin_capital, fib_leverage, breakeven_enabled, time_close_enabled, time_close_hours) " + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", ( symbol, mt, direction_sel, upper_px, lower_px, - oid, entry, sl, tp, float(amount), margin_capital, leverage, be_flag, + oid, entry, sl, tp, float(amount), margin_capital, leverage, be_flag, tc_en, tc_h, ), ) return True, None @@ -5158,6 +5176,7 @@ def _false_breakout_exists_for_symbol(conn, symbol): def _add_false_breakout_key_monitor( conn, symbol, direction_sel, upper_px, lower_px, key_px, breakeven_enabled=0, + time_close_enabled=0, time_close_hours=None, ): if _false_breakout_exists_for_symbol(conn, symbol): return False, f"{symbol} 已有假突破监控(同币仅允许一条)" @@ -5214,15 +5233,16 @@ def _add_false_breakout_key_monitor( except Exception as e: return False, friendly_exchange_error(e, available_usdt=available_usdt) be_flag = 1 if int(breakeven_enabled or 0) != 0 else 0 + tc_en, tc_h, _ = time_close_insert_values(time_close_enabled, time_close_hours, None) conn.execute( "INSERT INTO key_monitors " "(symbol, monitor_type, direction, upper, lower, " "fib_limit_order_id, fib_entry_price, fib_stop_loss, fib_take_profit, " - "fib_order_amount, fib_margin_capital, fib_leverage, breakeven_enabled) " - "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)", + "fib_order_amount, fib_margin_capital, fib_leverage, breakeven_enabled, time_close_enabled, time_close_hours) " + "VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", ( symbol, FALSE_BREAKOUT_MONITOR_TYPE, direction_sel, upper_px, lower_px, - oid, entry, sl, tp, float(amount), margin_capital, leverage, be_flag, + oid, entry, sl, tp, float(amount), margin_capital, leverage, be_flag, tc_en, tc_h, ), ) return True, None @@ -5333,6 +5353,7 @@ def check_key_monitors(): key_sig = typ if typ in KEY_MONITOR_AUTO_TYPES else None be_on = breakeven_enabled_from_row(r, 0) + tc_en, tc_h, _ = time_close_settings_from_row(r) ok_trade, trade_err, det = _market_open_for_key_monitor( conn, sym, @@ -5342,6 +5363,8 @@ def check_key_monitors(): tp_raw, key_signal_type=key_sig, breakeven_enabled=1 if be_on else 0, + time_close_enabled=tc_en, + time_close_hours=tc_h, ) planned_rr_txt = ( format_wechat_scalar_2dp(planned_rr) if planned_rr is not None else "-" @@ -5515,12 +5538,14 @@ def check_order_monitors(): send_wechat_msg(be_msg) res = None + if should_trigger_time_close(r): + res = TIME_CLOSE_RESULT # 做多 - if direction == "long": + if not res and direction == "long": if p >= take_profit: res = "止盈" elif p <= stop_loss: res = "止损" # 做空 - elif direction == "short": + elif not res and direction == "short": if p <= take_profit: res = "止盈" elif p >= stop_loss: res = "止损" @@ -6304,7 +6329,8 @@ def api_price_snapshot(): "SELECT id,symbol,monitor_type,direction,upper,lower,fib_entry_price,fib_limit_order_id,created_at FROM key_monitors" ).fetchall() order_rows = conn.execute( - "SELECT id,symbol,exchange_symbol,direction,trigger_price,stop_loss,initial_stop_loss,take_profit,margin_capital,leverage FROM order_monitors WHERE status='active'" + "SELECT id,symbol,exchange_symbol,direction,trigger_price,stop_loss,initial_stop_loss,take_profit,margin_capital,leverage," + "time_close_enabled,time_close_hours,time_close_at_ms,opened_at_ms FROM order_monitors WHERE status='active'" ).fetchall() symbol_set = set() @@ -6488,6 +6514,7 @@ def api_price_snapshot(): format_price_fn=format_price_for_symbol, symbol=r["symbol"], ) + apply_time_close_to_payload(payload, r) new_sl, new_tp, changed = order_monitor_tpsl_needs_sync( r["stop_loss"], r["take_profit"], exchange_tpsl ) @@ -7284,14 +7311,20 @@ def add_order(): else: breakeven_price = round(float(trigger_price) * (1 + breakeven_offset_pct / 100.0), 8) breakeven_enabled = 1 if (d.get("breakeven_enabled") or "").strip() in ("1", "true", "on", "yes") else 0 + tc_en = parse_time_close_enabled_form(d.get("time_close_enabled")) + tc_h = parse_time_close_hours_form(d.get("time_close_hours")) if tc_en else None + if tc_en and not tc_h: + tc_en = 0 + tc_en, tc_h, tc_at = time_close_insert_values(tc_en, tc_h, opened_at_ms) conn.execute( - "INSERT INTO order_monitors (symbol, exchange_symbol, direction, trigger_price, stop_loss, initial_stop_loss, take_profit, margin_capital, leverage, trade_style, risk_percent, risk_amount, breakeven_rr_trigger, breakeven_offset_pct, breakeven_step_r, breakeven_armed, breakeven_price, breakeven_enabled, notional_value, position_ratio, base_amount, order_amount, exchange_order_id, opened_at, opened_at_ms, session_date, monitor_type) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", + "INSERT INTO order_monitors (symbol, exchange_symbol, direction, trigger_price, stop_loss, initial_stop_loss, take_profit, margin_capital, leverage, trade_style, risk_percent, risk_amount, breakeven_rr_trigger, breakeven_offset_pct, breakeven_step_r, breakeven_armed, breakeven_price, breakeven_enabled, notional_value, position_ratio, base_amount, order_amount, exchange_order_id, opened_at, opened_at_ms, session_date, monitor_type, time_close_enabled, time_close_hours, time_close_at_ms) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", ( symbol, exchange_symbol, direction, trigger_price, stop_loss, stop_loss, take_profit, margin_capital, leverage, trade_style, risk_percent_db, risk_amount_final, breakeven_rr_trigger, breakeven_offset_pct, breakeven_step_r, 0, breakeven_price, breakeven_enabled, notional_value, position_ratio, base_amount, amount, open_order_id, opened_at_bj, opened_at_ms, trading_day, ORDER_MONITOR_TYPE_MANUAL, + tc_en, tc_h, tc_at, ) ) conn.commit() diff --git a/crypto_monitor_binance/templates/index.html b/crypto_monitor_binance/templates/index.html index 6f016f0..7056ac5 100644 --- a/crypto_monitor_binance/templates/index.html +++ b/crypto_monitor_binance/templates/index.html @@ -376,6 +376,14 @@ + @@ -417,6 +425,12 @@ +