bot修改开仓限制

This commit is contained in:
dekun
2026-05-21 06:55:25 +08:00
parent 2cddce92a0
commit 86e8ed6888
3 changed files with 8715 additions and 8695 deletions
+8
View File
@@ -79,6 +79,14 @@ GATE_TPSL_PRICE_TYPE=0
# 页面与浏览器标签展示的交易所名称(多环境区分时可改成例如 Gate·模拟)
# EXCHANGE_DISPLAY_NAME=Gate.io
# =============================================================================
# 交易执行 / 开仓限制(与 crypto_monitor_gate 主站一致)
# =============================================================================
# 【最大同时持仓】active 下单监控数达到该值后禁止再开仓(默认 1=单仓)
MAX_ACTIVE_POSITIONS=1
# 整点前禁止新开仓:true=启用(默认),false=关闭(交易日划分仍用 TRADING_DAY_RESET_HOUR
# TRADING_DAY_RESET_OPEN_GUARD_ENABLED=true
# 关键位监控:5m收线突破过滤参数
KLINE_TIMEFRAME=5m
KEY_BREAKOUT_LIMIT_PCT=1.5
+25 -13
View File
@@ -110,6 +110,8 @@ DAILY_LOSS_CAPITAL = float(os.getenv("DAILY_LOSS_CAPITAL", "20"))
DAILY_PROFIT_CAPITAL = float(os.getenv("DAILY_PROFIT_CAPITAL", "50"))
BTC_LEVERAGE = int(os.getenv("BTC_LEVERAGE", "10"))
ALT_LEVERAGE = int(os.getenv("ALT_LEVERAGE", "5"))
# 与 Gate 主站一致:最大同时 active 下单监控数(默认 1=单仓)
MAX_ACTIVE_POSITIONS = max(1, int(os.getenv("MAX_ACTIVE_POSITIONS", "1")))
# 交易日滚动与「可开仓」整点:按应用本地时区 wall clock(默认北京时间 UTC+8
TRADING_DAY_RESET_HOUR = int(os.getenv("TRADING_DAY_RESET_HOUR", "8"))
# false 时关闭「整点前禁止新开仓」守卫(交易日划分仍用 TRADING_DAY_RESET_HOUR
@@ -2635,13 +2637,18 @@ def trading_day_reset_allows_new_open(now):
return now.hour >= TRADING_DAY_RESET_HOUR
def get_active_position_count(conn):
"""与 Gate 主站一致:仅统计 order_monitors.status=active。"""
return int(conn.execute("SELECT COUNT(*) FROM order_monitors WHERE status='active'").fetchone()[0])
def precheck_risk(conn, symbol, direction):
now = app_now()
if not trading_day_reset_allows_new_open(now):
return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓"
active_count = conn.execute("SELECT COUNT(*) FROM order_monitors WHERE status='active'").fetchone()[0]
if active_count > 0:
return False, "一次只能持有一个仓位"
active_count = get_active_position_count(conn)
if active_count >= MAX_ACTIVE_POSITIONS:
return False, f"已达最大持仓数({active_count}/{MAX_ACTIVE_POSITIONS}"
trend_n = conn.execute(
"SELECT COUNT(*) FROM trend_pullback_plans WHERE status='active'"
).fetchone()[0]
@@ -2659,13 +2666,16 @@ def precheck_risk(conn, symbol, direction):
def precheck_trend_pullback_start(conn):
"""趋势回调启动前:不与机器人单仓监控并存。"""
"""趋势回调启动前:不与机器人下单监控达持仓上限并存。"""
now = app_now()
if not trading_day_reset_allows_new_open(now):
return False, f"北京时间 {TRADING_DAY_RESET_HOUR}:00 前不允许持仓"
active_count = conn.execute("SELECT COUNT(*) FROM order_monitors WHERE status='active'").fetchone()[0]
if active_count > 0:
return False, "请先结束「机器人下单监控」中的持仓,再启动趋势回调"
active_count = get_active_position_count(conn)
if active_count >= MAX_ACTIVE_POSITIONS:
return False, (
f"已达最大持仓数({active_count}/{MAX_ACTIVE_POSITIONS}),"
"请先结束「机器人下单监控」中的持仓,再启动趋势回调"
)
trend_n = conn.execute(
"SELECT COUNT(*) FROM trend_pullback_plans WHERE status='active'"
).fetchone()[0]
@@ -5112,7 +5122,7 @@ def render_main_page(page="trade"):
and ("持仓占用" in str(r.get("effective_miss_reason") or ""))
)
rate = round(win/total*100,2) if total else 0
active_count = len(order_list)
active_count = get_active_position_count(conn)
trend_active = conn.execute(
"SELECT COUNT(*) FROM trend_pullback_plans WHERE status='active'"
).fetchone()[0]
@@ -5143,7 +5153,7 @@ def render_main_page(page="trade"):
preview_snapshots.append(sd)
can_trade = (
trading_day_reset_allows_new_open(now)
and active_count == 0
and active_count < MAX_ACTIVE_POSITIONS
and int(trend_active or 0) == 0
)
trend_preview = None
@@ -5201,6 +5211,7 @@ def render_main_page(page="trade"):
full_margin_buffer_ratio=FULL_MARGIN_BUFFER_RATIO,
price_refresh_seconds=PRICE_REFRESH_SECONDS,
active_count=active_count,
max_active_positions=MAX_ACTIVE_POSITIONS,
can_trade=can_trade,
trend_plans=trend_plans,
plan_history=plan_history,
@@ -5294,9 +5305,9 @@ def api_account_snapshot():
funding_usdt = round(funding_capital, 2) if funding_capital is not None else None
current_capital = round(trading_capital, 2) if trading_capital is not None else round(local_current_capital, 2)
recommended_capital = round(get_recommended_capital(current_capital), 2)
active_count = conn.execute("SELECT COUNT(*) FROM order_monitors WHERE status='active'").fetchone()[0]
active_count = get_active_position_count(conn)
conn.close()
can_trade = trading_day_reset_allows_new_open(now) and active_count == 0
can_trade = trading_day_reset_allows_new_open(now) and active_count < MAX_ACTIVE_POSITIONS
available_trading_usdt = get_available_trading_usdt()
return jsonify({
"funding_usdt": funding_usdt,
@@ -5304,6 +5315,7 @@ def api_account_snapshot():
"available_trading_usdt": round(available_trading_usdt, 2) if available_trading_usdt is not None else None,
"recommended_capital": recommended_capital,
"active_count": active_count,
"max_active_positions": MAX_ACTIVE_POSITIONS,
"can_trade": can_trade,
"trading_day": trading_day
})
@@ -5743,7 +5755,7 @@ def add_order():
return redirect("/")
ok, reason = precheck_risk(conn, symbol, direction)
if not ok:
if "一次只能持有一个仓位" in reason:
if "已达最大持仓数" in reason:
try:
tp_raw = parse_positive_float(d.get("tp"))
sl_raw = parse_positive_float(d.get("sl"))
@@ -5759,7 +5771,7 @@ def add_order():
stop_loss=sl_raw or 0,
take_profit=tgt_raw or 0,
result="错过",
miss_reason="持仓占用:一次只能持有一个仓位",
miss_reason=f"持仓占用:{reason}",
opened_at=app_now_str(),
closed_at=app_now_str(),
)
+2 -2
View File
@@ -250,8 +250,8 @@
{% endif %}
</div>
<div class="rule-tip" id="order-rule-tip">
规则:单仓;与「趋势回调」计划互斥;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x
{% if can_trade %}可开仓{% else %}不可开仓(持仓、有趋势回调计划,或未到北京时间 {{ reset_hour }}:00{% endif %}
规则:最大同时持仓 {{ max_active_positions }}(与 Gate 主站 MAX_ACTIVE_POSITIONS 一致,当前 active {{ active_count }};与「趋势回调」计划互斥;BTC {{ btc_leverage }}x / 山寨 {{ alt_leverage }}x
{% if can_trade %}可开仓{% else %}不可开仓(持仓达上限、有趋势回调计划,或未到北京时间 {{ reset_hour }}:00{% endif %}
按风险比例自动计算仓位
</div>
<div class="rule-tip">