bot修改开仓限制
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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(),
|
||||
)
|
||||
|
||||
@@ -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">
|
||||
|
||||
Reference in New Issue
Block a user