refactor: 移除 gate_bot,统一为三所架构并更新文档
删除 crypto_monitor_gate_bot 目录,中控与子代理改为 binance/okx/gate 三账户; 文档与 UI 文案「四所」改为「三所」;新增清库前一次性配置备份脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+230
-230
@@ -1,230 +1,230 @@
|
||||
"""各交易所 app 模块 → strategy_register 配置(统一工厂)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
|
||||
def resolve_trading_app_module(app_module: Any = None) -> Any:
|
||||
"""
|
||||
须在 login_required 定义之后调用。
|
||||
PM2 / python app.py 时 __name__ 为 __main__,请传入 sys.modules[__name__]。
|
||||
"""
|
||||
if app_module is None:
|
||||
main = sys.modules.get("__main__")
|
||||
if main is not None and hasattr(main, "login_required"):
|
||||
m = main
|
||||
else:
|
||||
import inspect
|
||||
|
||||
m = None
|
||||
for fr in inspect.stack():
|
||||
g = fr.frame.f_globals
|
||||
if callable(g.get("login_required")) and callable(g.get("get_db")):
|
||||
m = g
|
||||
break
|
||||
if m is None:
|
||||
raise RuntimeError(
|
||||
"策略交易注册失败:请使用 install_strategy_trading(app, repo_root, app_module=sys.modules[__name__])"
|
||||
)
|
||||
else:
|
||||
m = app_module
|
||||
if not hasattr(m, "login_required"):
|
||||
raise RuntimeError(
|
||||
"策略交易注册须在 login_required 定义之后执行(将 install_strategy_trading 放在 app.py 末尾)"
|
||||
)
|
||||
return m
|
||||
|
||||
|
||||
def build_strategy_config(
|
||||
app_module: Any = None, *, trend_enabled: bool = False, trend_disabled_note: str = ""
|
||||
) -> dict:
|
||||
m = resolve_trading_app_module(app_module)
|
||||
|
||||
def get_trading_capital_usdt(conn):
|
||||
if hasattr(m, "get_exchange_capitals"):
|
||||
_, tc = m.get_exchange_capitals(force=True)
|
||||
if tc is not None:
|
||||
return float(tc)
|
||||
if hasattr(m, "get_available_trading_usdt"):
|
||||
snap = m.get_available_trading_usdt()
|
||||
if snap is not None:
|
||||
return float(snap)
|
||||
day = m.get_trading_day(m.app_now())
|
||||
row = m.ensure_session(conn, day)
|
||||
return float(row["current_capital"])
|
||||
|
||||
def get_position(ex_sym, direction):
|
||||
qty = m.get_live_position_contracts(ex_sym, direction)
|
||||
entry = None
|
||||
try:
|
||||
rows = m.exchange.fetch_positions([ex_sym])
|
||||
for p in rows or []:
|
||||
matcher = getattr(m, "_row_matches_monitor_direction", None)
|
||||
if matcher and not matcher(direction, p):
|
||||
continue
|
||||
contracts = getattr(m, "_position_row_effective_contracts", lambda x: abs(float(x.get("contracts") or 0)))(p)
|
||||
if contracts <= 0:
|
||||
continue
|
||||
coerce = getattr(m, "_coerce_float", None)
|
||||
if coerce:
|
||||
entry = coerce(
|
||||
p.get("entryPrice"),
|
||||
p.get("average"),
|
||||
(p.get("info") or {}).get("entryPrice"),
|
||||
)
|
||||
if entry:
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
return {"contracts": float(qty or 0), "entry_price": entry}
|
||||
|
||||
def amount_to_precision(ex_sym, amount):
|
||||
try:
|
||||
return float(m.exchange.amount_to_precision(ex_sym, float(amount)))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def price_to_precision(ex_sym, price):
|
||||
try:
|
||||
return float(m.exchange.price_to_precision(ex_sym, float(price)))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def market_add(ex_sym, direction, amount, leverage):
|
||||
return m.place_exchange_order(ex_sym, direction, amount, leverage, stop_loss=None, take_profit=None)
|
||||
|
||||
def limit_add(ex_sym, direction, amount, price, leverage):
|
||||
m.exchange.set_leverage(int(leverage), ex_sym)
|
||||
side = "buy" if direction == "long" else "sell"
|
||||
if hasattr(m, "build_okx_order_params"):
|
||||
params = m.build_okx_order_params(direction, reduce_only=False)
|
||||
elif hasattr(m, "build_binance_order_params"):
|
||||
params = m.build_binance_order_params(direction, reduce_only=False)
|
||||
elif hasattr(m, "build_gate_order_params"):
|
||||
params = m.build_gate_order_params(direction, reduce_only=False)
|
||||
else:
|
||||
params = {}
|
||||
return m.exchange.create_order(
|
||||
ex_sym, "limit", side, float(amount), float(price), params if params is not None else {}
|
||||
)
|
||||
|
||||
def replace_tpsl(ex_sym, direction, sl, tp, order_row):
|
||||
row = order_row or {"symbol": ex_sym, "exchange_symbol": ex_sym, "direction": direction}
|
||||
m.replace_active_monitor_tpsl_on_exchange(row, sl, tp)
|
||||
|
||||
def count_trends(conn):
|
||||
try:
|
||||
return int(
|
||||
conn.execute(
|
||||
"SELECT COUNT(*) FROM trend_pullback_plans WHERE status='active'"
|
||||
).fetchone()[0]
|
||||
)
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def friendly_error(err):
|
||||
fn = getattr(m, "friendly_exchange_error", None) or getattr(
|
||||
m, "friendly_okx_error", None
|
||||
)
|
||||
if not callable(fn):
|
||||
return str(err)
|
||||
try:
|
||||
snap = m.get_available_trading_usdt()
|
||||
except Exception:
|
||||
snap = None
|
||||
try:
|
||||
return fn(err, available_usdt=snap)
|
||||
except TypeError:
|
||||
return fn(err)
|
||||
|
||||
def limit_order_status(ex_sym, order_id):
|
||||
fn = getattr(m, "fib_limit_order_status", None)
|
||||
if callable(fn):
|
||||
return fn(ex_sym, order_id)
|
||||
return "unknown"
|
||||
|
||||
def cancel_limit_order(ex_sym, order_id):
|
||||
fn = getattr(m, "cancel_fib_limit_order", None)
|
||||
if callable(fn):
|
||||
try:
|
||||
return fn(ex_sym, order_id)
|
||||
except Exception:
|
||||
pass
|
||||
if not order_id:
|
||||
return False
|
||||
try:
|
||||
m.exchange.cancel_order(str(order_id), ex_sym)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def get_mark_price(symbol):
|
||||
fn = getattr(m, "get_symbol_mark_price", None) or getattr(m, "get_price", None)
|
||||
if not callable(fn):
|
||||
return None
|
||||
try:
|
||||
return fn(symbol)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def wechat_account_label():
|
||||
fn = getattr(m, "_wechat_account_label", None)
|
||||
if callable(fn):
|
||||
try:
|
||||
return fn()
|
||||
except Exception:
|
||||
pass
|
||||
return getattr(m, "EXCHANGE_DISPLAY_NAME", "") or ""
|
||||
|
||||
def wechat_direction_text(direction):
|
||||
fn = getattr(m, "_wechat_direction_text", None)
|
||||
if callable(fn):
|
||||
try:
|
||||
return fn(direction)
|
||||
except Exception:
|
||||
pass
|
||||
d = (direction or "long").strip().lower()
|
||||
return "做多" if d == "long" else "做空"
|
||||
|
||||
def send_wechat(content):
|
||||
fn = getattr(m, "send_wechat_msg", None)
|
||||
if callable(fn):
|
||||
fn(content)
|
||||
|
||||
note = trend_disabled_note or (
|
||||
"趋势回调(自动补仓)请在 Gate 趋势机器人实例使用:/strategy/trend"
|
||||
)
|
||||
return {
|
||||
"app_module": m,
|
||||
"exchange_display": getattr(m, "EXCHANGE_DISPLAY_NAME", ""),
|
||||
"trend_enabled": trend_enabled,
|
||||
"trend_disabled_note": note,
|
||||
"login_required": m.login_required,
|
||||
"get_db": m.get_db,
|
||||
"normalize_symbol_input": m.normalize_symbol_input,
|
||||
"normalize_exchange_symbol": m.normalize_exchange_symbol,
|
||||
"get_price": m.get_price,
|
||||
"get_trading_capital_usdt": get_trading_capital_usdt,
|
||||
"get_position": get_position,
|
||||
"amount_to_precision": amount_to_precision,
|
||||
"price_to_precision": price_to_precision,
|
||||
"market_add": market_add,
|
||||
"limit_add": limit_add,
|
||||
"replace_tpsl": replace_tpsl,
|
||||
"ensure_live_ready": m.ensure_exchange_live_ready,
|
||||
"default_risk_percent": float(getattr(m, "RISK_PERCENT", 2)),
|
||||
"default_leverage": m.infer_leverage,
|
||||
"friendly_error": friendly_error,
|
||||
"app_now_str": m.app_now_str,
|
||||
"resolve_fill_price": m.resolve_order_entry_price,
|
||||
"price_fmt": m.format_price_for_symbol,
|
||||
"count_active_trend_plans": count_trends if trend_enabled else count_trends,
|
||||
"limit_order_status": limit_order_status,
|
||||
"cancel_limit_order": cancel_limit_order,
|
||||
"get_mark_price": get_mark_price,
|
||||
"send_wechat": send_wechat,
|
||||
"format_price": getattr(m, "format_price_for_symbol", None),
|
||||
"wechat_account_label": wechat_account_label,
|
||||
"wechat_direction_text": wechat_direction_text,
|
||||
}
|
||||
"""各交易所 app 模块 → strategy_register 配置(统一工厂)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import sys
|
||||
from typing import Any
|
||||
|
||||
|
||||
def resolve_trading_app_module(app_module: Any = None) -> Any:
|
||||
"""
|
||||
须在 login_required 定义之后调用。
|
||||
PM2 / python app.py 时 __name__ 为 __main__,请传入 sys.modules[__name__]。
|
||||
"""
|
||||
if app_module is None:
|
||||
main = sys.modules.get("__main__")
|
||||
if main is not None and hasattr(main, "login_required"):
|
||||
m = main
|
||||
else:
|
||||
import inspect
|
||||
|
||||
m = None
|
||||
for fr in inspect.stack():
|
||||
g = fr.frame.f_globals
|
||||
if callable(g.get("login_required")) and callable(g.get("get_db")):
|
||||
m = g
|
||||
break
|
||||
if m is None:
|
||||
raise RuntimeError(
|
||||
"策略交易注册失败:请使用 install_strategy_trading(app, repo_root, app_module=sys.modules[__name__])"
|
||||
)
|
||||
else:
|
||||
m = app_module
|
||||
if not hasattr(m, "login_required"):
|
||||
raise RuntimeError(
|
||||
"策略交易注册须在 login_required 定义之后执行(将 install_strategy_trading 放在 app.py 末尾)"
|
||||
)
|
||||
return m
|
||||
|
||||
|
||||
def build_strategy_config(
|
||||
app_module: Any = None, *, trend_enabled: bool = False, trend_disabled_note: str = ""
|
||||
) -> dict:
|
||||
m = resolve_trading_app_module(app_module)
|
||||
|
||||
def get_trading_capital_usdt(conn):
|
||||
if hasattr(m, "get_exchange_capitals"):
|
||||
_, tc = m.get_exchange_capitals(force=True)
|
||||
if tc is not None:
|
||||
return float(tc)
|
||||
if hasattr(m, "get_available_trading_usdt"):
|
||||
snap = m.get_available_trading_usdt()
|
||||
if snap is not None:
|
||||
return float(snap)
|
||||
day = m.get_trading_day(m.app_now())
|
||||
row = m.ensure_session(conn, day)
|
||||
return float(row["current_capital"])
|
||||
|
||||
def get_position(ex_sym, direction):
|
||||
qty = m.get_live_position_contracts(ex_sym, direction)
|
||||
entry = None
|
||||
try:
|
||||
rows = m.exchange.fetch_positions([ex_sym])
|
||||
for p in rows or []:
|
||||
matcher = getattr(m, "_row_matches_monitor_direction", None)
|
||||
if matcher and not matcher(direction, p):
|
||||
continue
|
||||
contracts = getattr(m, "_position_row_effective_contracts", lambda x: abs(float(x.get("contracts") or 0)))(p)
|
||||
if contracts <= 0:
|
||||
continue
|
||||
coerce = getattr(m, "_coerce_float", None)
|
||||
if coerce:
|
||||
entry = coerce(
|
||||
p.get("entryPrice"),
|
||||
p.get("average"),
|
||||
(p.get("info") or {}).get("entryPrice"),
|
||||
)
|
||||
if entry:
|
||||
break
|
||||
except Exception:
|
||||
pass
|
||||
return {"contracts": float(qty or 0), "entry_price": entry}
|
||||
|
||||
def amount_to_precision(ex_sym, amount):
|
||||
try:
|
||||
return float(m.exchange.amount_to_precision(ex_sym, float(amount)))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def price_to_precision(ex_sym, price):
|
||||
try:
|
||||
return float(m.exchange.price_to_precision(ex_sym, float(price)))
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def market_add(ex_sym, direction, amount, leverage):
|
||||
return m.place_exchange_order(ex_sym, direction, amount, leverage, stop_loss=None, take_profit=None)
|
||||
|
||||
def limit_add(ex_sym, direction, amount, price, leverage):
|
||||
m.exchange.set_leverage(int(leverage), ex_sym)
|
||||
side = "buy" if direction == "long" else "sell"
|
||||
if hasattr(m, "build_okx_order_params"):
|
||||
params = m.build_okx_order_params(direction, reduce_only=False)
|
||||
elif hasattr(m, "build_binance_order_params"):
|
||||
params = m.build_binance_order_params(direction, reduce_only=False)
|
||||
elif hasattr(m, "build_gate_order_params"):
|
||||
params = m.build_gate_order_params(direction, reduce_only=False)
|
||||
else:
|
||||
params = {}
|
||||
return m.exchange.create_order(
|
||||
ex_sym, "limit", side, float(amount), float(price), params if params is not None else {}
|
||||
)
|
||||
|
||||
def replace_tpsl(ex_sym, direction, sl, tp, order_row):
|
||||
row = order_row or {"symbol": ex_sym, "exchange_symbol": ex_sym, "direction": direction}
|
||||
m.replace_active_monitor_tpsl_on_exchange(row, sl, tp)
|
||||
|
||||
def count_trends(conn):
|
||||
try:
|
||||
return int(
|
||||
conn.execute(
|
||||
"SELECT COUNT(*) FROM trend_pullback_plans WHERE status='active'"
|
||||
).fetchone()[0]
|
||||
)
|
||||
except Exception:
|
||||
return 0
|
||||
|
||||
def friendly_error(err):
|
||||
fn = getattr(m, "friendly_exchange_error", None) or getattr(
|
||||
m, "friendly_okx_error", None
|
||||
)
|
||||
if not callable(fn):
|
||||
return str(err)
|
||||
try:
|
||||
snap = m.get_available_trading_usdt()
|
||||
except Exception:
|
||||
snap = None
|
||||
try:
|
||||
return fn(err, available_usdt=snap)
|
||||
except TypeError:
|
||||
return fn(err)
|
||||
|
||||
def limit_order_status(ex_sym, order_id):
|
||||
fn = getattr(m, "fib_limit_order_status", None)
|
||||
if callable(fn):
|
||||
return fn(ex_sym, order_id)
|
||||
return "unknown"
|
||||
|
||||
def cancel_limit_order(ex_sym, order_id):
|
||||
fn = getattr(m, "cancel_fib_limit_order", None)
|
||||
if callable(fn):
|
||||
try:
|
||||
return fn(ex_sym, order_id)
|
||||
except Exception:
|
||||
pass
|
||||
if not order_id:
|
||||
return False
|
||||
try:
|
||||
m.exchange.cancel_order(str(order_id), ex_sym)
|
||||
return True
|
||||
except Exception:
|
||||
return False
|
||||
|
||||
def get_mark_price(symbol):
|
||||
fn = getattr(m, "get_symbol_mark_price", None) or getattr(m, "get_price", None)
|
||||
if not callable(fn):
|
||||
return None
|
||||
try:
|
||||
return fn(symbol)
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
def wechat_account_label():
|
||||
fn = getattr(m, "_wechat_account_label", None)
|
||||
if callable(fn):
|
||||
try:
|
||||
return fn()
|
||||
except Exception:
|
||||
pass
|
||||
return getattr(m, "EXCHANGE_DISPLAY_NAME", "") or ""
|
||||
|
||||
def wechat_direction_text(direction):
|
||||
fn = getattr(m, "_wechat_direction_text", None)
|
||||
if callable(fn):
|
||||
try:
|
||||
return fn(direction)
|
||||
except Exception:
|
||||
pass
|
||||
d = (direction or "long").strip().lower()
|
||||
return "做多" if d == "long" else "做空"
|
||||
|
||||
def send_wechat(content):
|
||||
fn = getattr(m, "send_wechat_msg", None)
|
||||
if callable(fn):
|
||||
fn(content)
|
||||
|
||||
note = trend_disabled_note or (
|
||||
"趋势回调(自动补仓)请在 Gate机器人实例使用:/strategy/trend"
|
||||
)
|
||||
return {
|
||||
"app_module": m,
|
||||
"exchange_display": getattr(m, "EXCHANGE_DISPLAY_NAME", ""),
|
||||
"trend_enabled": trend_enabled,
|
||||
"trend_disabled_note": note,
|
||||
"login_required": m.login_required,
|
||||
"get_db": m.get_db,
|
||||
"normalize_symbol_input": m.normalize_symbol_input,
|
||||
"normalize_exchange_symbol": m.normalize_exchange_symbol,
|
||||
"get_price": m.get_price,
|
||||
"get_trading_capital_usdt": get_trading_capital_usdt,
|
||||
"get_position": get_position,
|
||||
"amount_to_precision": amount_to_precision,
|
||||
"price_to_precision": price_to_precision,
|
||||
"market_add": market_add,
|
||||
"limit_add": limit_add,
|
||||
"replace_tpsl": replace_tpsl,
|
||||
"ensure_live_ready": m.ensure_exchange_live_ready,
|
||||
"default_risk_percent": float(getattr(m, "RISK_PERCENT", 2)),
|
||||
"default_leverage": m.infer_leverage,
|
||||
"friendly_error": friendly_error,
|
||||
"app_now_str": m.app_now_str,
|
||||
"resolve_fill_price": m.resolve_order_entry_price,
|
||||
"price_fmt": m.format_price_for_symbol,
|
||||
"count_active_trend_plans": count_trends if trend_enabled else count_trends,
|
||||
"limit_order_status": limit_order_status,
|
||||
"cancel_limit_order": cancel_limit_order,
|
||||
"get_mark_price": get_mark_price,
|
||||
"send_wechat": send_wechat,
|
||||
"format_price": getattr(m, "format_price_for_symbol", None),
|
||||
"wechat_account_label": wechat_account_label,
|
||||
"wechat_direction_text": wechat_direction_text,
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
Gate.io USDT 永续 — 策略交易交易所侧能力。
|
||||
|
||||
实现方式:各 Gate 实例 app 通过 strategy_config.build_strategy_config(app_module) 注入
|
||||
ccxt 下单、精度、换 TP/SL;本文件为文档与类型锚点,避免在四个 app 重复实现滚仓公式。
|
||||
ccxt 下单、精度、换 TP/SL;本文件为文档与类型锚点,避免在各 app 重复实现滚仓公式。
|
||||
"""
|
||||
from lib.strategy.strategy_exchange_base import StrategyExchangeAdapter
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""策略交易记录页:已结束趋势 / 顺势加仓快照(四所统一)。"""
|
||||
"""策略交易记录页:已结束趋势 / 顺势加仓快照(三所统一)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""策略结束快照:趋势回调 / 顺势加仓(四所共用)。"""
|
||||
"""策略结束快照:趋势回调 / 顺势加仓(三所共用)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
|
||||
@@ -230,7 +230,7 @@ def compute_trend_plan_core(
|
||||
def calc_planned_reward_risk_ratio(
|
||||
direction: str, entry_price: float, stop_loss: float, take_profit: float
|
||||
) -> Optional[float]:
|
||||
"""盈亏比(reward/risk),与四所 calc_rr_ratio 口径一致。"""
|
||||
"""盈亏比(reward/risk),与三所 calc_rr_ratio 口径一致。"""
|
||||
try:
|
||||
entry = float(entry_price)
|
||||
sl = float(stop_loss)
|
||||
@@ -375,7 +375,7 @@ def trend_leg_grid_price(plan: dict, leg_idx: int) -> Optional[float]:
|
||||
|
||||
def trend_leg_display_price(plan: dict, leg_idx: int) -> Optional[float]:
|
||||
"""
|
||||
四所统一:单档展示价 = leg_fill_prices_json 实际记录,否则计划网格(首仓用均价/参考价)。
|
||||
三所统一:单档展示价 = leg_fill_prices_json 实际记录,否则计划网格(首仓用均价/参考价)。
|
||||
禁止为凑均价反推虚构成交价。
|
||||
"""
|
||||
p = plan or {}
|
||||
@@ -398,7 +398,7 @@ def trend_leg_display_price(plan: dict, leg_idx: int) -> Optional[float]:
|
||||
|
||||
|
||||
def reconcile_trend_leg_fill_prices(plan: dict) -> list[float]:
|
||||
"""首仓(0)+已补仓(1..legs_done) 展示价列表(四所共用 trend_leg_display_price)。"""
|
||||
"""首仓(0)+已补仓(1..legs_done) 展示价列表(三所共用 trend_leg_display_price)。"""
|
||||
p = plan or {}
|
||||
if int(p.get("first_order_done") or 0) == 0:
|
||||
return []
|
||||
@@ -563,7 +563,7 @@ def build_trend_preview_level_rows(preview: dict) -> tuple[dict, list[dict]]:
|
||||
|
||||
def enrich_trend_dca_levels_with_tp(plan: dict, levels: list[dict]) -> list[dict]:
|
||||
"""
|
||||
四所统一补仓表 enrich(实例策略页 + 中控 monitor 共用)。
|
||||
三所统一补仓表 enrich(实例策略页 + 中控 monitor 共用)。
|
||||
触发价:实际成交价或计划网格;末档加仓后均价用持仓均价;禁止反推虚构成交价。
|
||||
"""
|
||||
if not levels:
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""趋势回调:路由、轮询、页面数据(四所共用,依赖各 app 模块交易所能力)。"""
|
||||
"""趋势回调:路由、轮询、页面数据(三所共用,依赖各 app 模块交易所能力)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
import inspect
|
||||
@@ -138,8 +138,8 @@ def summarize_trend_dca_probe(cfg: dict, row) -> dict:
|
||||
out["block_reason"] = "交易所无持仓"
|
||||
else:
|
||||
out["block_reason"] = (
|
||||
"标记价已触达,轮询应自动下单;若仍未补请确认 PM2 进程 crypto_gate_bot "
|
||||
"(非 manual-agent-gate-bot)在运行,并查看 pm2 logs crypto_gate_bot"
|
||||
"标记价已触达,轮询应自动下单;若仍未补请确认 PM2 进程 crypto_gate "
|
||||
"(或对应所 Flask 进程)在运行,并查看 pm2 logs"
|
||||
)
|
||||
elif not reached:
|
||||
out["block_reason"] = f"标记价 {pf} 未触达下一档 {level}"
|
||||
@@ -520,7 +520,7 @@ def _patch_hub_trend_views(app: Flask) -> None:
|
||||
|
||||
|
||||
def patch_trend_hub_enrich(app: Flask, cfg: dict) -> None:
|
||||
"""hub_bridge install 之后调用:四所 /api/hub/monitor 趋势字段与策略页一致。"""
|
||||
"""hub_bridge install 之后调用:三所 /api/hub/monitor 趋势字段与策略页一致。"""
|
||||
_patch_hub_monitor_enrich(app, cfg)
|
||||
|
||||
|
||||
|
||||
@@ -77,9 +77,8 @@ def fetch_roll_page_data(
|
||||
|
||||
|
||||
DEFAULT_TREND_DISABLED_NOTE = (
|
||||
"趋势回调(预览、自动补仓、程序止盈)仅在 Gate 趋势机器人实例 "
|
||||
"(crypto_monitor_gate_bot,常见端口 5002)中启用。"
|
||||
"币安 / Gate 主站 / OKX 可使用本页「顺势加仓」;完整趋势回调请打开该实例。"
|
||||
"趋势回调(预览、自动补仓、程序止盈)须在本实例 .env 设置 "
|
||||
"`LIVE_TRADING_ENABLED=true` 并重启对应 PM2 进程(如 crypto_gate / crypto_okx / crypto_binance)。"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""策略计划(趋势回调 / 滚仓)开始与结束 — 企业微信推送(四所共用)。"""
|
||||
"""策略计划(趋势回调 / 滚仓)开始与结束 — 企业微信推送(三所共用)。"""
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Optional
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
<div class="box">
|
||||
<h1>趋势回调</h1>
|
||||
<p>{{ trend_note }}</p>
|
||||
<p style="color:#8892b0;font-size:.9rem">趋势回调含自动补仓档位,仅在 Gate 趋势机器人(crypto_monitor_gate_bot)实例中运行。</p>
|
||||
<p style="color:#8892b0;font-size:.9rem">趋势回调含自动补仓档位,在三所实例(Binance / Gate / OKX)中均可启用,须配置 LIVE_TRADING_ENABLED=true。</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<summary class="tip-collapse-summary">趋势回调说明(本实例未启用)</summary>
|
||||
<div class="tip-collapse-body rule-tip">
|
||||
{{ trend_disabled_note }}<br><br>
|
||||
趋势回调含自动补仓档位与预览执行,仅在 <strong>Gate 趋势机器人</strong>(<code>crypto_monitor_gate_bot</code>)实例中运行。
|
||||
请访问该实例同一菜单「策略交易 → 趋势回调」,或常用地址 <code>:5002/strategy/trend</code>。
|
||||
趋势回调含自动补仓档位与预览执行,在 <strong>Binance / Gate / OKX</strong> 各实例的「策略交易 → 趋势回调」中运行。
|
||||
请访问对应实例同一菜单,或常用地址如 Gate <code>:5000/strategy/trend</code>。
|
||||
</div>
|
||||
</details>
|
||||
<p style="margin-top:12px;font-size:.85rem">
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<strong>计划 #{{ p.plan_id }}</strong> 标记价 {{ p.mark_price }} 已触达补仓触发价 {{ p.next_trigger }},但未自动补仓:
|
||||
{{ p.block_reason }}。
|
||||
{% if not live_trading_enabled %}
|
||||
请在 <code>crypto_monitor_gate_bot/.env</code> 设置 <code>LIVE_TRADING_ENABLED=true</code> 后重启 PM2 进程 <strong>crypto_gate_bot</strong>(不是 manual-agent-gate-bot)。
|
||||
请在当前实例 <code>.env</code> 设置 <code>LIVE_TRADING_ENABLED=true</code> 后重启对应 PM2 进程(如 <strong>crypto_gate</strong>、<strong>crypto_okx</strong>、<strong>crypto_binance</strong>)。
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endif %}
|
||||
|
||||
Reference in New Issue
Block a user