Initial standalone crypto_okx with one-click deploy.
Add deploy/manage.sh bootstrap for git.bz121.com/dekun/crypto_okx and point docs at this repo. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -0,0 +1,236 @@
|
||||
"""实例「系统设置」页:从 .env 汇总风控说明(三所共用)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
from typing import Any, Optional
|
||||
|
||||
from lib.trade.account_risk_lib import (
|
||||
cooling_hours_manual,
|
||||
cooling_hours_manual_journal,
|
||||
daily_loss_limit,
|
||||
manual_close_daily_limit,
|
||||
max_active_positions_from_env,
|
||||
mood_issues_daily_freeze_enabled,
|
||||
risk_control_enabled,
|
||||
)
|
||||
from lib.trade.position_sizing_lib import is_full_margin_mode, load_position_sizing_mode, mode_label_zh
|
||||
from lib.trade.trade_policy_lib import TradePolicy
|
||||
|
||||
|
||||
def _env_bool(key: str, default: bool = False) -> bool:
|
||||
raw = (os.getenv(key) or "").strip().lower()
|
||||
if not raw:
|
||||
return default
|
||||
return raw in ("1", "true", "yes", "on")
|
||||
|
||||
|
||||
def _env_float(key: str, default: float) -> float:
|
||||
try:
|
||||
return float(os.getenv(key, str(default)))
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
def _env_int(key: str, default: int) -> int:
|
||||
try:
|
||||
return int(os.getenv(key, str(default)))
|
||||
except (TypeError, ValueError):
|
||||
return default
|
||||
|
||||
|
||||
def _row(label: str, value: str, note: str = "") -> dict[str, str]:
|
||||
return {"label": label, "value": value, "note": note}
|
||||
|
||||
|
||||
def _on_off(enabled: bool) -> str:
|
||||
return "开启" if enabled else "关闭"
|
||||
|
||||
|
||||
def build_instance_settings_view(
|
||||
*,
|
||||
exchange_key: str,
|
||||
exchange_display: str,
|
||||
risk_status: Optional[dict[str, Any]] = None,
|
||||
trade_policy: Optional[TradePolicy] = None,
|
||||
data_export_version: int = 3,
|
||||
open_guard_enabled: Optional[bool] = None,
|
||||
) -> dict[str, Any]:
|
||||
rs = risk_status or {}
|
||||
sizing_mode = load_position_sizing_mode()
|
||||
reset_hour = _env_int("TRADING_DAY_RESET_HOUR", 8)
|
||||
hard_limit = _env_int("DAILY_OPEN_HARD_LIMIT", 0)
|
||||
alert_threshold = _env_int("DAILY_OPEN_ALERT_THRESHOLD", 5)
|
||||
force_close_on = _env_bool("FORCE_CLOSE_ENABLED", False)
|
||||
force_close_hour = _env_int("FORCE_CLOSE_BJ_HOUR", 0)
|
||||
auto_transfer_on = _env_bool("AUTO_TRANSFER_ENABLED", False)
|
||||
guard_on = (
|
||||
bool(open_guard_enabled)
|
||||
if open_guard_enabled is not None
|
||||
else _env_bool("TRADING_DAY_RESET_OPEN_GUARD_ENABLED", True)
|
||||
)
|
||||
|
||||
sections: list[dict[str, Any]] = []
|
||||
|
||||
sections.append(
|
||||
{
|
||||
"title": "交易执行",
|
||||
"rows": [
|
||||
_row("最大同时持仓", str(max_active_positions_from_env())),
|
||||
_row("计仓模式", mode_label_zh(sizing_mode)),
|
||||
_row("以损定仓风险%", f"{_env_float('RISK_PERCENT', 2):g}%"),
|
||||
_row("人工最低盈亏比", f">= {_env_float('MANUAL_MIN_PLANNED_RR', 1.4):g}:1"),
|
||||
_row(
|
||||
"交易日切点",
|
||||
f"北京时间 {reset_hour}:00",
|
||||
"新交易日统计与部分开仓限制以此为准",
|
||||
),
|
||||
_row(
|
||||
"允许北京时间切点前开仓",
|
||||
"已放开(允许开仓)" if not guard_on else "已限制(禁止开仓)",
|
||||
f"关闭限制后,{reset_hour}:00 前也可斐波成交登记与人工下单;"
|
||||
"环境配置「切点前禁止新开仓」(TRADING_DAY_RESET_OPEN_GUARD_ENABLED)",
|
||||
),
|
||||
_row(
|
||||
"单日开仓提醒",
|
||||
f"第 {alert_threshold} 次",
|
||||
"达次数推送企业微信,不拦单",
|
||||
),
|
||||
_row(
|
||||
"单日开仓硬上限",
|
||||
str(hard_limit) if hard_limit > 0 else "未启用",
|
||||
"达上限后禁止一切新开仓直至下一交易日" if hard_limit > 0 else "",
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
sections.append(
|
||||
{
|
||||
"title": "账户冷静期",
|
||||
"rows": [
|
||||
_row("风控总开关", _on_off(risk_control_enabled())),
|
||||
_row("手动平仓冷静", f"{cooling_hours_manual():g} 小时"),
|
||||
_row("复盘后冷静", f"{cooling_hours_manual_journal():g} 小时", "手动平仓且填写说明后可缩短"),
|
||||
_row("日手动平仓上限", f"{manual_close_daily_limit()} 次", "超限当日冻结"),
|
||||
_row(
|
||||
"日亏损次数上限",
|
||||
(
|
||||
f"{daily_loss_limit()} 次"
|
||||
if daily_loss_limit() > 0
|
||||
else "未启用"
|
||||
),
|
||||
"平仓亏损达限后当日冻结开仓;0=不启用" if daily_loss_limit() > 0 else "RISK_DAILY_LOSS_LIMIT=0",
|
||||
),
|
||||
_row(
|
||||
"复盘情绪日冻结",
|
||||
_on_off(mood_issues_daily_freeze_enabled()),
|
||||
"复盘勾选心态标签可触发当日冻结",
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
sections.append(
|
||||
{
|
||||
"title": "关键位监控",
|
||||
"rows": [
|
||||
_row("模式", "仅关键支撑阻力提醒", "箱体/斐波/触价等程序自动单已移除"),
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
if force_close_on or (exchange_key or "").strip().lower() == "gate":
|
||||
sections.append(
|
||||
{
|
||||
"title": "整点强制清仓",
|
||||
"rows": [
|
||||
_row("强制清仓", _on_off(force_close_on)),
|
||||
_row(
|
||||
"执行时刻",
|
||||
f"北京时间 {force_close_hour}:00 起 {_env_int('FORCE_CLOSE_GRACE_MINUTES', 5)} 分钟内",
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
if (exchange_key or "").strip().lower() == "okx" and _env_bool("OKX_OPTIONS_ENABLED", False):
|
||||
api_key = (os.getenv("OKX_API_KEY") or "").strip()
|
||||
sections.append(
|
||||
{
|
||||
"title": "期权设置",
|
||||
"rows": [
|
||||
_row("期权模块", "已启用"),
|
||||
_row(
|
||||
"账户 API",
|
||||
f"已配置(…{api_key[-4:]})" if len(api_key) >= 4 else "未配置 OKX_API_*",
|
||||
"永续与期权共用 OKX_API_*",
|
||||
),
|
||||
_row(
|
||||
"说明",
|
||||
"币种兑换与账户内划转到右侧「期权设置」卡片操作",
|
||||
),
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
policy_note = ""
|
||||
if trade_policy and getattr(trade_policy, "badge_text", ""):
|
||||
policy_note = str(trade_policy.badge_text)
|
||||
|
||||
return {
|
||||
"exchange_display": exchange_display,
|
||||
"risk_status_label": str(rs.get("status_label") or "正常"),
|
||||
"risk_status_reason": str(rs.get("reason") or "").strip(),
|
||||
"can_trade": bool(rs.get("can_trade", True)),
|
||||
"trade_policy_note": policy_note,
|
||||
"sections": sections,
|
||||
"data_export_version": int(data_export_version),
|
||||
"show_transfer": (exchange_key or "").strip().lower() in ("gate", "binance", "okx"),
|
||||
"options_settings_enabled": (exchange_key or "").strip().lower() == "okx"
|
||||
and _env_bool("OKX_OPTIONS_ENABLED", False),
|
||||
"auto_transfer_enabled": auto_transfer_on,
|
||||
"auto_transfer_bj_hour": _env_int("AUTO_TRANSFER_BJ_HOUR", 8),
|
||||
"auto_transfer_amount": _env_float("AUTO_TRANSFER_AMOUNT", 30),
|
||||
"auto_transfer_from": (os.getenv("AUTO_TRANSFER_FROM") or "funding").strip(),
|
||||
"auto_transfer_to": (os.getenv("AUTO_TRANSFER_TO") or "swap").strip(),
|
||||
}
|
||||
|
||||
|
||||
def build_settings_tabs(display: dict[str, Any] | None, instance_settings: dict[str, Any]) -> list[dict[str, str]]:
|
||||
disp = display or {}
|
||||
inst = instance_settings or {}
|
||||
tabs: list[dict[str, str]] = [{"key": "nav", "title": "导航显示"}]
|
||||
tabs.append({"key": "sim_funds", "title": "模拟资金"})
|
||||
if disp.get("show_settings_password", True):
|
||||
tabs.append({"key": "password", "title": "账户密码"})
|
||||
if inst.get("show_transfer") and disp.get("show_settings_transfer", True):
|
||||
tabs.append({"key": "transfer", "title": "永续划转"})
|
||||
if disp.get("show_settings_export", True):
|
||||
tabs.append({"key": "export", "title": "数据导出"})
|
||||
if inst.get("options_settings_enabled") and disp.get("show_settings_options_swap", True):
|
||||
tabs.append({"key": "options_swap", "title": "币种兑换"})
|
||||
if inst.get("options_settings_enabled") and disp.get("show_settings_options_transfer", True):
|
||||
tabs.append({"key": "options_transfer", "title": "期权划转"})
|
||||
return tabs
|
||||
|
||||
|
||||
def settings_page_context(page: str, *, instance_base_dir: str | None = None, **kwargs: Any) -> dict[str, Any]:
|
||||
p = (page or "").strip()
|
||||
if p == "system_guide":
|
||||
from lib.instance.instance_system_guide_lib import system_guide_template_context
|
||||
|
||||
return system_guide_template_context()
|
||||
if p not in ("settings", "risk_policy", "env_config"):
|
||||
return {}
|
||||
display = kwargs.pop("display", None)
|
||||
ctx: dict[str, Any] = {"instance_settings": build_instance_settings_view(**kwargs)}
|
||||
if p == "settings":
|
||||
ctx["settings_tabs"] = build_settings_tabs(display, ctx["instance_settings"])
|
||||
if p == "env_config" and instance_base_dir:
|
||||
from lib.env.env_ui_manifest import build_env_ui_payload
|
||||
|
||||
exchange_key = str(kwargs.get("exchange_key") or "")
|
||||
env_path = os.path.join(instance_base_dir, ".env")
|
||||
example_path = os.path.join(instance_base_dir, ".env.example")
|
||||
ctx["env_config_groups"] = build_env_ui_payload(exchange_key, example_path, env_path)
|
||||
return ctx
|
||||
Reference in New Issue
Block a user