From b4d62ea0a6a30528e7bad68523ee942ef919d012 Mon Sep 17 00:00:00 2001 From: dekun Date: Wed, 14 Oct 2026 09:26:38 +0800 Subject: [PATCH] Enable options by default and allow full sim trading without OKX API keys. Relax hedge start gates in sim, route perp open/close through sim broker, and mount strategy templates for records pages. Co-authored-by: Cursor --- .env.example | 678 +++++++++++++------------- app.py | 25 +- lib/hedge_plan/hedge_plan_calc_lib.py | 9 +- lib/hedge_plan/hedge_plan_register.py | 12 +- lib/instance/instance_settings_lib.py | 4 +- lib/options/options_register.py | 4 +- lib/sim/hooks.py | 63 +++ scripts/bootstrap_deploy_secrets.py | 2 + 8 files changed, 445 insertions(+), 352 deletions(-) diff --git a/.env.example b/.env.example index c846076..7294072 100644 --- a/.env.example +++ b/.env.example @@ -1,339 +1,339 @@ -# ============================================================================= -# 环境配置模板(可提交 Git).程序运行时只读取同目录下的 .env. -# -# 首次部署 / 新机: -# cp .env.example .env -# nano .env # 填入真实密钥,端口,代理等 -# -# 升级代码(git pull)前建议备份(.env 不在 Git 中,pull 不会覆盖): -# cp .env .env.backup.$(date +%Y%m%d) -# -# 从备份恢复: -# cp .env.backup.YYYYMMDD .env -# ============================================================================= - -APP_ENV=production -# 服务监听地址(云服务器通常用 0.0.0.0) -APP_HOST=0.0.0.0 -# 服务端口 -APP_PORT=5010 -# 是否开启调试模式(生产建议 false) -APP_DEBUG=false - -# 登录账号 -APP_USERNAME=admin -# 登录密码(请改成你自己的强密码) -APP_PASSWORD=admin123 -# 是否关闭登录校验(局域网可设 true;公网务必 false) -APP_AUTH_DISABLED=true -# --- 多账户交易中控 manual_trading_hub --- -# 中控请求本实例 /api/hub/* 时携带请求头 X-Hub-Token,须与中控启动环境变量 HUB_BRIDGE_TOKEN 一致 -# 未设置且 APP_AUTH_DISABLED=false 时,仅网页登录后可访问;本机联调可保持 APP_AUTH_DISABLED=true -# HUB_BRIDGE_TOKEN=your-long-random-token -# 允许复盘中控 iframe 内嵌本实例(与 hub 域名一致;默认已开启) -# APP_ALLOW_HUB_EMBED=true -# HUB_EMBED_PARENT_ORIGINS=https://hub.example.com -# HTTPS 且经 iframe 打开时建议 true;不设则 hub-sso 在 HTTPS 下也会自动尝试 SameSite=None -# APP_COOKIE_SECURE=true -# Flask 会话密钥(必须替换为长随机字符串) -FLASK_SECRET_KEY=CHANGE_TO_LONG_RANDOM_SECRET - -# 企业微信机器人 Webhook(用于行情/风控推送) -WECHAT_WEBHOOK=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=REPLACE_WITH_REAL_KEY - -# 数据库文件路径(相对路径会自动按项目目录解析) -DB_PATH=crypto.db -# 交易截图上传目录 -UPLOAD_DIR=static/images - -# 自动备份(scripts/backup_data.sh + cron,可选;默认即可) -# BACKUP_ROOT=/root/backups -# BACKUP_RETENTION_DAYS=30 -# BACKUP_INSTANCE=coinox - -# 训练总资金(U) -# TOTAL_CAPITAL=100 # 已弃用,资金展示读交易所 -# 计仓:risk=以损定仓(默认);full_margin=合约可用×FULL_MARGIN_BUFFER_RATIO 全仓杠杆(须无仓后重启) -POSITION_SIZING_MODE=risk -# 方向限制(默认 false=双向均可;true 时按 TRADE_DIRECTION 限制,修改后须重启) -# TRADE_DIRECTION=long_only | short_only | both(或 多/空/双向) -TRADE_DIRECTION_RESTRICT_ENABLED=false -TRADE_DIRECTION=both -# 币种白名单(默认 false=全币种可手输;true 时关键位/下单/策略仅下拉选择) -TRADE_SYMBOL_RESTRICT_ENABLED=false -TRADE_SYMBOL_WHITELIST=BTC,ETH -# 每天起始基数(U) -DAILY_START_CAPITAL=30 -# 日内回撤后基数(U) -DAILY_LOSS_CAPITAL=20 -# 日内盈利后基数(U) -DAILY_PROFIT_CAPITAL=50 -# BTC 默认杠杆倍数 -BTC_LEVERAGE=10 -# 山寨币默认杠杆倍数 -ALT_LEVERAGE=5 -# 交易日重置小时(北京时间) -TRADING_DAY_RESET_HOUR=8 -# 整点前禁止新开仓:true=启用(默认),false=关闭(仍可保留 8 点作为交易日划分) -TRADING_DAY_RESET_OPEN_GUARD_ENABLED=true - -# 是否开启 OKX 实盘下单(false=只做本地流程,true=真实下单) -LIVE_TRADING_ENABLED=false -# ============================================================================= -# OKX 账户 API(永续+期权共用;仅服务器 .env 手改,前端不展示) -# 新机保持为空;填真钥后 pm2 restart --update-env(含子代理) -# 旧键 OKX_OPTIONS_API_* 已废弃:若 OKX_API_* 为空,启动时会从 OPTIONS 键回填 -# ============================================================================= -OKX_API_KEY= -OKX_API_SECRET= -OKX_API_PASSPHRASE= -# 保证金模式:cross=全仓,isolated=逐仓 -OKX_TD_MODE=cross -# 持仓模式:hedge=双向持仓,net=单向净持仓 -OKX_POS_MODE=hedge -# 仓位查询 instType(OKX) -OKX_POSITION_INST_TYPE=SWAP -# 从 OKX 历史仓位同步已实现盈亏(北京时间起点,空=近 90 天 0 点起) -# EXCHANGE_POSITION_SYNC_FROM_BJ=2026-01-01 -# 单次拉取历史仓位条数上限(OKX 每页最多 100,程序会分页) -# EXCHANGE_POSITION_HISTORY_LIMIT=200 -# 页面与浏览器标签展示的交易所名称(多环境区分时可改成例如 OKX·测试网) -EXCHANGE_DISPLAY_NAME=CoinOX -# 企业微信推送里展示的账户备注 -# OKX_ACCOUNT_LABEL= -# 顶栏是否显示 USDT 资金/交易账户(热更);false 时总资金仅计期权 USDC 侧 -OKX_SHOW_PERP_FUNDS=true - -# ============================================================================= -# 期权模块(与上方 OKX_API_* 同源;修改启用开关后须重启 PM2) -# 详见 docs/期权方案.md 与 docs/期权用法.md -# ============================================================================= -OKX_OPTIONS_ENABLED=false -# 以下 OKX_OPTIONS_API_* 已废弃,请勿再配置(仅兼容旧部署回填) -# OKX_OPTIONS_API_KEY= -# OKX_OPTIONS_API_SECRET= -# OKX_OPTIONS_API_PASSPHRASE= -OKX_OPTIONS_ACCOUNT_LABEL=账户·期权 -# 单笔期权本位: coin(默认,币本位+USDT买币桥) | usdc(权利金USDC;对冲仍仅USDC) -OKX_OPTIONS_MARGIN_MODE=coin -OKX_OPTIONS_TRADE_BUDGET_USDC=10 -OKX_OPTIONS_BUDGET_BUFFER=0.95 -# 币本位:按交易户USDT×缓冲复利;上限开关默认关(靠人工转走) -OKX_OPTIONS_COIN_COMPOUND=true -OKX_OPTIONS_COIN_BUDGET_USDT=10 -OKX_OPTIONS_COIN_MAX_USDT_ENABLED=false -OKX_OPTIONS_COIN_MAX_USDT=50 -# 现货买入相对权利金缓冲:1.10=多买10%;也可写 0.10。按最大可开张数×权利金×缓冲买币,不全额兑换 -OKX_OPTIONS_COIN_SPOT_BUY_BUFFER=1.10 -# 全仓复利:开启时隐藏单笔预算且不可用打满;关闭后恢复单笔预算 -OKX_OPTIONS_COMPOUND_FULL_ENABLED=true -OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED=false -OKX_OPTIONS_COMPOUND_FULL_CAP_USDC=300 -# 交易模式三选一(热更):options=单独期权 / perp_options=永期对冲 / options_options=期期对冲 -# 选单独期权时隐藏对冲导航与对冲配置;选对冲时不可单独开期权,仓位按「对冲组数上限」 -OKX_TRADE_MODE=perp_options -# 仅单独期权模式:期权同时持仓上限(笔);0=不限制;同合约加仓不占新笔数;热更 -OKX_OPTIONS_MAX_ACTIVE_POSITIONS=0 -OKX_OPTIONS_DEFAULT_UNDERLY=ETH -# 期权链仅显示卖一深度≥1张的合约(估算卖一/无深度不显示);false 则显示全部 -OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED=true -OKX_OPTIONS_MAX_DTE_DAYS=2 -OKX_OPTIONS_CHAIN_MAX_DTE_DAYS=14 -OKX_OPTIONS_ITM_MAX_DIST_USD=30 -OKX_OPTIONS_PROFIT_ALERT_RATIO=1.0 -OKX_OPTIONS_POLL_SECONDS=15 -OKX_OPTIONS_TD_MODE=isolated -OKX_OPTIONS_ALLOW_MARKET_CLOSE=false -# 目标平仓门控(目标触达后自动平才校验;比较口径均为 USDT 估值) -OKX_OPTIONS_CLOSE_GATE_MODE=premium -# 全局倍数可选;留空则按本位用下方 COIN/USDC -OKX_OPTIONS_CLOSE_RECYCLE_MULT= -OKX_OPTIONS_CLOSE_RECYCLE_MULT_COIN=1.05 -OKX_OPTIONS_CLOSE_RECYCLE_MULT_USDC=2 -OKX_OPTIONS_CLOSE_NET_PNL_MIN_U=0 -OKX_OPTIONS_CLOSE_HOLD_SECONDS=120 -# 对冲买期权等成交超时(秒);超时撤未成交部分,未完全成交则开仓失败 -OKX_OPTIONS_OPEN_FILL_TIMEOUT_SEC=12 - -# ============================================================================= -# 对冲计划(仅 OKX;由 OKX_TRADE_MODE 控制是否启用;详见 docs/对冲计划开发方案.md) -# ============================================================================= -# 以下三项已由 OKX_TRADE_MODE 取代,保留兼容旧部署(未配置 TRADE_MODE 时仍可读) -HEDGE_PLAN_ENABLED=false -HEDGE_PLAN_SHOW_PERP_OPTIONS=true -HEDGE_PLAN_SHOW_OPTIONS_OPTIONS=true -HEDGE_PLAN_LIVE_ORDER=false -# 永期子模式:true=以期权为主;false=保险模式(页面标题前标识,不可页内切换) -HEDGE_PLAN_OPTION_PRIMARY=true -HEDGE_PLAN_OPEN_ORDER=options_first -HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS=true -HEDGE_PLAN_ON_PERP_TP_CLOSE_OPTIONS=false -HEDGE_PLAN_OO_CLOSE_WINNER_ONLY=true -# 方案C:期期页面显示「平仓模式」(到期平/全平);关则固定到期平.默认开启,页面默认选全平 -HEDGE_PLAN_OO_CLOSE_MODE_ENABLED=true -# 期期「做多/做空」拆分口径:budget=按权利金预算(默认);sheets=先算同张数总张数(2n)再按比例拆 -HEDGE_PLAN_OO_BIAS_SPLIT_BY=budget -# 期期「做多/做空」主腿占比(0~1,默认 0.7=7:3);做多主腿=Call,做空主腿=Put -HEDGE_PLAN_OO_BIAS_RATIO=0.7 -# 对冲与单独期权互斥(默认 true):有对冲计划不可单独开期权;有单独期权不可启动对冲;false=可同时开 -HEDGE_PLAN_OPTIONS_MUTUAL_EXCLUSIVE=true -# 半腿失败改手动补开(默认 true):不自动平已成腿,计划挂 partial,页面补开;开启时下方自动平强制无效 -HEDGE_PLAN_MANUAL_COMPLETE_ON_PARTIAL=true -# 对冲组数上限(默认 1;opening/active/partial 计入);仅永期/期期模式生效;热更 -MAX_ACTIVE_HEDGE_PLANS=1 -HEDGE_PLAN_MONITOR_POLL_SECONDS=15 -# 半腿失败自动平期权;若 MANUAL_COMPLETE_ON_PARTIAL=true 则运行时强制无效(建议一并写成 false) -HEDGE_PLAN_PARTIAL_AUTO_CLOSE_OPTION=false - -# ============================================================================= -# 关键位程序自动下单(与 POSITION_SIZING_MODE 联动,修改后须重启 PM2) -# ============================================================================= -# 默认 false = 关闭所有关键位程序自动单(箱体/收敛/斐波/假突破/触价) -# -# POSITION_SIZING_MODE=risk(以损定仓) -# false → 不执行任何关键位自动单;支撑/阻力提醒,人工下单,顺势加仓不受影响 -# true → 允许关键位全套自动(含触价) -# -# POSITION_SIZING_MODE=full_margin(全仓杠杆,须无仓切换) -# false → 不执行触价自动单 -# true → 仅回调/突破触价可程序自动开仓;箱体/斐波等仍禁止 -# -# 顺势加仓,趋势回调不受本开关控制;全仓模式下策略自动仍禁止. -KEY_AUTO_ORDER_ENABLED=false - -# ============================================================================= -# 关键位门控(页面「关键位监控」规则条与 _key_hard_checks 共用) -# ============================================================================= -# 【周期】门控 K 线周期,如 5m,15m;仅影响关键位硬条件,不改变顶栏分区 -KLINE_TIMEFRAME=5m -# OKX 遗留:突破过滤百分比(与 KEY_BREAKOUT_AMP_* 并存,程序仍读取) -KEY_BREAKOUT_LIMIT_PCT=1.5 -# 【确认K】闭合 K 序列中的棒偏移:突破棒默认 -2(倒数第2根),确认棒默认 -1(倒数第1根) -KEY_CONFIRM_BREAKOUT_BAR=-2 -KEY_CONFIRM_BAR=-1 -# 【量能】突破棒成交量 > 前 N 根均量 × 倍数(默认 N=20,倍数=1.3 即放大 30%) -KEY_VOLUME_MA_BARS=20 -KEY_VOLUME_RATIO_MIN=1.3 -# 【箱体/收敛】突破K收盘越过关键位(占该侧价格%)的下限;无上限(过猛由计划RR过滤) -KEY_BREAKOUT_AMP_MIN_PCT=0.03 -# 已不参与门控,可保留配置项兼容旧环境 -KEY_BREAKOUT_AMP_MAX_PCT=0.5 -# 【阻力/支撑】突破后微信提醒次数与间隔(分钟) -KEY_ALERT_MAX_TIMES=3 -KEY_ALERT_INTERVAL_MINUTES=5 -# 【日成交量排名】品种须在该排名前 N 名(添加关键位与运行时门控均校验) -KEY_DAILY_VOLUME_RANK_MAX=30 -# 【关键位自动开仓盈亏比】按确认K收盘 E 计算,严格大于该值才市价开仓(如 1.5 表示须 >1.5:1) -KEY_AUTO_MIN_PLANNED_RR=1.5 -# 止损:突破 K 极值向外缓冲的百分比(默认 0.5 即 0.5%) -KEY_STOP_OUTSIDE_BREAKOUT_PCT=0.5 -# 趋势单方案:止损在突破 K 极值外侧的百分比(默认 1 即 1%) -KEY_TREND_STOP_OUTSIDE_PCT=1 - -# ============================================================================= -# 交易执行 / 人工风控(页面「实盘下单」) -# ============================================================================= -# 【最大同时持仓】active 订单数达到该值后禁止人工与关键位自动再加仓(默认 1=单仓) -MAX_ACTIVE_POSITIONS=1 -# 【人工下单最低盈亏比】按当前价与 SL/TP 计算,低于该值前后端均拒绝(默认 1.4,即须 >=1.4:1) -MANUAL_MIN_PLANNED_RR=1.4 -# 【关键位连开计仓】true=已有持仓时关键位自动单仍按「无仓时」资金快照算保证金基数 -KEY_SIZING_USE_ZERO_POSITION_SNAPSHOT=true -# 【单日开仓 AI 提醒】本交易日开仓达到该次数时推送企业微信 AI 克制提醒(不拦单) -DAILY_OPEN_ALERT_THRESHOLD=5 -# 【单日开仓硬上限】本交易日开仓次数>=该值后禁止一切新开仓直至下一交易日(北京时间 TRADING_DAY_RESET_HOUR 切日);0=不启用 -DAILY_OPEN_HARD_LIMIT=0 - -# ============================================================================= -# 账户冷静期 / 日冻结风控(手动平仓,外部平仓,复盘情绪标签) -# 详见 docs/account-risk-cooldown.md -# ============================================================================= -RISK_CONTROL_ENABLED=true -RISK_COOLING_HOURS_MANUAL=4 -RISK_COOLING_HOURS_MANUAL_JOURNAL=1 -RISK_MANUAL_CLOSE_DAILY_LIMIT=2 -# 日亏损次数上限:平仓盈亏<0 计1次;达限当日冻结开仓;0=不启用 -RISK_DAILY_LOSS_LIMIT=2 -RISK_MOOD_ISSUES_DAILY_FREEZE=true - -# 资金与仓位刷新周期(秒) -BALANCE_REFRESH_SECONDS=60 -# 前端价格快照轮询(秒) -PRICE_REFRESH_SECONDS=5 -# 后台监控轮询周期(秒) -MONITOR_POLL_SECONDS=3 -# 移动保本同步交易所止盈止损的最小间隔(秒),避免频繁撤挂叠单 -BREAKEVEN_EXCHANGE_MIN_INTERVAL_SEC=60 -# 重启后多少秒内不做「外部平仓」同步(避免 API 未就绪误判) -RECONCILE_STARTUP_GRACE_SEC=90 -# 连续多少次轮询确认交易所空仓后,才记为外部平仓(默认 3 次 ≈ 9 秒) -RECONCILE_FLAT_CONFIRM_POLLS=3 -# 使用可用资金时的缓冲比例(如0.98代表用98%) -FULL_MARGIN_BUFFER_RATIO=0.98 - -# ============================================================================= -# 自动划转(页顶「将 swap 补足到 XU」;与 DAILY_START_CAPITAL 独立,需一致时请设为相同值) -# ============================================================================= -AUTO_TRANSFER_ENABLED=false -# 交易账户(swap)目标余额 U:每日 8 点(北京)自动划入或划出至 funding;持仓中不划转 -AUTO_TRANSFER_AMOUNT=30 -AUTO_TRANSFER_FROM=funding -AUTO_TRANSFER_TO=swap -TRANSFER_CCY=USDT -# 北京时间该整点小时内尝试;账簿按 UTC 自然日去重 -AUTO_TRANSFER_BJ_HOUR=8 -# 强制清仓整点(北京时间,默认 0=凌晨00点) -FORCE_CLOSE_BJ_HOUR=0 -# 是否启用强制清仓(默认关闭,true 才会在整点执行) -FORCE_CLOSE_ENABLED=false -# 强制清仓执行窗口(分钟,默认 5;该窗口内禁止开仓) -FORCE_CLOSE_GRACE_MINUTES=5 - -# 推送与AI超时(秒) -WECHAT_TIMEOUT_SECONDS=10 -AI_TIMEOUT_SECONDS=120 - -# AI 复盘服务地址(本机 Ollama 默认地址) -AI_PROVIDER=openai -OPENAI_API_BASE=https://op.bz121.com/v1 -OPENAI_API_KEY=你的密钥 -OPENAI_MODEL=gemma4:e4b -OLLAMA_API=http://127.0.0.1:11434/api/generate -AI_MODEL=huihui_ai/deepseek-r1-abliterated:latest - -# OKX 代理(可选,仅本地开发网络受限时用;云服务器部署请留空,直连 OKX 即可) -# 1) 先在本机建立隧道(示例): -# ssh -N -D 127.0.0.1:1080 root@你的VPS_IP -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes -# 2) 再启用下面这一行(推荐 socks5h,让远端解析域名): -# OKX_SOCKS_PROXY=socks5h://127.0.0.1:1080 -# -# 如你更偏向 HTTP 代理(VPS 上跑 tinyproxy 之类),可用: -# OKX_HTTP_PROXY=http://127.0.0.1:3128 -# OKX_HTTPS_PROXY=http://127.0.0.1:3128 - -# 开仓多周期K线图(可选) -# ORDER_CHART_ENABLED=true -# ORDER_CHART_TFS=4h,1h,15m,5m -# ORDER_CHART_LIMIT=100 -# ORDER_CHART_DIR=static/images/order_charts -# 详见上文 DAILY_OPEN_ALERT_THRESHOLD / DAILY_OPEN_HARD_LIMIT;说明文档 docs/daily-open-limit.md -# 以损定仓(按交易账户资金的百分比) -# RISK_PERCENT=2 -# 移动保本触发(达到多少R触发)与偏移(百分比) -# BREAKEVEN_RR_TRIGGER=1.0 -# 移动保本阶梯(每多少R继续上移一次,默认1R) -# BREAKEVEN_STEP_R=1.0 -# BREAKEVEN_OFFSET_PCT=0.02 -# 开单风格默认值:trend / swing -# DEFAULT_TRADE_STYLE=trend - -APP_TIMEZONE=Asia/Shanghai -# TRADING_DAY_RESET_HOUR 现在表示「北京时间」整点,默认 8 点起算新交易日;开仓整点限制见 TRADING_DAY_RESET_OPEN_GUARD_ENABLED - -# --- CoinOX 模拟资金 --- -# sim=本地模拟资金撮合; live=实盘账户 -# 默认模拟盘(本地钱包); 切实盘请改为 live 并配置 API -SIM_DEFAULT_MODE=sim -SIM_INITIAL_EQUITY_USDT=10000 -SIM_INITIAL_USDC=0 -SIM_FEE_RATE=0.0005 +# ============================================================================= +# 环境配置模板(可提交 Git).程序运行时只读取同目录下的 .env. +# +# 首次部署 / 新机: +# cp .env.example .env +# nano .env # 填入真实密钥,端口,代理等 +# +# 升级代码(git pull)前建议备份(.env 不在 Git 中,pull 不会覆盖): +# cp .env .env.backup.$(date +%Y%m%d) +# +# 从备份恢复: +# cp .env.backup.YYYYMMDD .env +# ============================================================================= + +APP_ENV=production +# 服务监听地址(云服务器通常用 0.0.0.0) +APP_HOST=0.0.0.0 +# 服务端口 +APP_PORT=5010 +# 是否开启调试模式(生产建议 false) +APP_DEBUG=false + +# 登录账号 +APP_USERNAME=admin +# 登录密码(请改成你自己的强密码) +APP_PASSWORD=admin123 +# 是否关闭登录校验(局域网可设 true;公网务必 false) +APP_AUTH_DISABLED=true +# --- 多账户交易中控 manual_trading_hub --- +# 中控请求本实例 /api/hub/* 时携带请求头 X-Hub-Token,须与中控启动环境变量 HUB_BRIDGE_TOKEN 一致 +# 未设置且 APP_AUTH_DISABLED=false 时,仅网页登录后可访问;本机联调可保持 APP_AUTH_DISABLED=true +# HUB_BRIDGE_TOKEN=your-long-random-token +# 允许复盘中控 iframe 内嵌本实例(与 hub 域名一致;默认已开启) +# APP_ALLOW_HUB_EMBED=true +# HUB_EMBED_PARENT_ORIGINS=https://hub.example.com +# HTTPS 且经 iframe 打开时建议 true;不设则 hub-sso 在 HTTPS 下也会自动尝试 SameSite=None +# APP_COOKIE_SECURE=true +# Flask 会话密钥(必须替换为长随机字符串) +FLASK_SECRET_KEY=CHANGE_TO_LONG_RANDOM_SECRET + +# 企业微信机器人 Webhook(用于行情/风控推送) +WECHAT_WEBHOOK=https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=REPLACE_WITH_REAL_KEY + +# 数据库文件路径(相对路径会自动按项目目录解析) +DB_PATH=crypto.db +# 交易截图上传目录 +UPLOAD_DIR=static/images + +# 自动备份(scripts/backup_data.sh + cron,可选;默认即可) +# BACKUP_ROOT=/root/backups +# BACKUP_RETENTION_DAYS=30 +# BACKUP_INSTANCE=coinox + +# 训练总资金(U) +# TOTAL_CAPITAL=100 # 已弃用,资金展示读交易所 +# 计仓:risk=以损定仓(默认);full_margin=合约可用×FULL_MARGIN_BUFFER_RATIO 全仓杠杆(须无仓后重启) +POSITION_SIZING_MODE=risk +# 方向限制(默认 false=双向均可;true 时按 TRADE_DIRECTION 限制,修改后须重启) +# TRADE_DIRECTION=long_only | short_only | both(或 多/空/双向) +TRADE_DIRECTION_RESTRICT_ENABLED=false +TRADE_DIRECTION=both +# 币种白名单(默认 false=全币种可手输;true 时关键位/下单/策略仅下拉选择) +TRADE_SYMBOL_RESTRICT_ENABLED=false +TRADE_SYMBOL_WHITELIST=BTC,ETH +# 每天起始基数(U) +DAILY_START_CAPITAL=30 +# 日内回撤后基数(U) +DAILY_LOSS_CAPITAL=20 +# 日内盈利后基数(U) +DAILY_PROFIT_CAPITAL=50 +# BTC 默认杠杆倍数 +BTC_LEVERAGE=10 +# 山寨币默认杠杆倍数 +ALT_LEVERAGE=5 +# 交易日重置小时(北京时间) +TRADING_DAY_RESET_HOUR=8 +# 整点前禁止新开仓:true=启用(默认),false=关闭(仍可保留 8 点作为交易日划分) +TRADING_DAY_RESET_OPEN_GUARD_ENABLED=true + +# 是否开启 OKX 实盘下单(false=只做本地流程,true=真实下单) +LIVE_TRADING_ENABLED=false +# ============================================================================= +# OKX 账户 API(永续+期权共用;仅服务器 .env 手改,前端不展示) +# 新机保持为空;填真钥后 pm2 restart --update-env(含子代理) +# 旧键 OKX_OPTIONS_API_* 已废弃:若 OKX_API_* 为空,启动时会从 OPTIONS 键回填 +# ============================================================================= +OKX_API_KEY= +OKX_API_SECRET= +OKX_API_PASSPHRASE= +# 保证金模式:cross=全仓,isolated=逐仓 +OKX_TD_MODE=cross +# 持仓模式:hedge=双向持仓,net=单向净持仓 +OKX_POS_MODE=hedge +# 仓位查询 instType(OKX) +OKX_POSITION_INST_TYPE=SWAP +# 从 OKX 历史仓位同步已实现盈亏(北京时间起点,空=近 90 天 0 点起) +# EXCHANGE_POSITION_SYNC_FROM_BJ=2026-01-01 +# 单次拉取历史仓位条数上限(OKX 每页最多 100,程序会分页) +# EXCHANGE_POSITION_HISTORY_LIMIT=200 +# 页面与浏览器标签展示的交易所名称(多环境区分时可改成例如 OKX·测试网) +EXCHANGE_DISPLAY_NAME=CoinOX +# 企业微信推送里展示的账户备注 +# OKX_ACCOUNT_LABEL= +# 顶栏是否显示 USDT 资金/交易账户(热更);false 时总资金仅计期权 USDC 侧 +OKX_SHOW_PERP_FUNDS=true + +# ============================================================================= +# 期权模块(与上方 OKX_API_* 同源;修改启用开关后须重启 PM2) +# 详见 docs/期权方案.md 与 docs/期权用法.md +# ============================================================================= +OKX_OPTIONS_ENABLED=true +# 以下 OKX_OPTIONS_API_* 已废弃,请勿再配置(仅兼容旧部署回填) +# OKX_OPTIONS_API_KEY= +# OKX_OPTIONS_API_SECRET= +# OKX_OPTIONS_API_PASSPHRASE= +OKX_OPTIONS_ACCOUNT_LABEL=账户·期权 +# 单笔期权本位: coin(默认,币本位+USDT买币桥) | usdc(权利金USDC;对冲仍仅USDC) +OKX_OPTIONS_MARGIN_MODE=coin +OKX_OPTIONS_TRADE_BUDGET_USDC=10 +OKX_OPTIONS_BUDGET_BUFFER=0.95 +# 币本位:按交易户USDT×缓冲复利;上限开关默认关(靠人工转走) +OKX_OPTIONS_COIN_COMPOUND=true +OKX_OPTIONS_COIN_BUDGET_USDT=10 +OKX_OPTIONS_COIN_MAX_USDT_ENABLED=false +OKX_OPTIONS_COIN_MAX_USDT=50 +# 现货买入相对权利金缓冲:1.10=多买10%;也可写 0.10。按最大可开张数×权利金×缓冲买币,不全额兑换 +OKX_OPTIONS_COIN_SPOT_BUY_BUFFER=1.10 +# 全仓复利:开启时隐藏单笔预算且不可用打满;关闭后恢复单笔预算 +OKX_OPTIONS_COMPOUND_FULL_ENABLED=true +OKX_OPTIONS_COMPOUND_FULL_CAP_ENABLED=false +OKX_OPTIONS_COMPOUND_FULL_CAP_USDC=300 +# 交易模式三选一(热更):options=单独期权 / perp_options=永期对冲 / options_options=期期对冲 +# 选单独期权时隐藏对冲导航与对冲配置;选对冲时不可单独开期权,仓位按「对冲组数上限」 +OKX_TRADE_MODE=perp_options +# 仅单独期权模式:期权同时持仓上限(笔);0=不限制;同合约加仓不占新笔数;热更 +OKX_OPTIONS_MAX_ACTIVE_POSITIONS=0 +OKX_OPTIONS_DEFAULT_UNDERLY=ETH +# 期权链仅显示卖一深度≥1张的合约(估算卖一/无深度不显示);false 则显示全部 +OKX_OPTIONS_CHAIN_ASK_LIQ_FILTER_ENABLED=true +OKX_OPTIONS_MAX_DTE_DAYS=2 +OKX_OPTIONS_CHAIN_MAX_DTE_DAYS=14 +OKX_OPTIONS_ITM_MAX_DIST_USD=30 +OKX_OPTIONS_PROFIT_ALERT_RATIO=1.0 +OKX_OPTIONS_POLL_SECONDS=15 +OKX_OPTIONS_TD_MODE=isolated +OKX_OPTIONS_ALLOW_MARKET_CLOSE=false +# 目标平仓门控(目标触达后自动平才校验;比较口径均为 USDT 估值) +OKX_OPTIONS_CLOSE_GATE_MODE=premium +# 全局倍数可选;留空则按本位用下方 COIN/USDC +OKX_OPTIONS_CLOSE_RECYCLE_MULT= +OKX_OPTIONS_CLOSE_RECYCLE_MULT_COIN=1.05 +OKX_OPTIONS_CLOSE_RECYCLE_MULT_USDC=2 +OKX_OPTIONS_CLOSE_NET_PNL_MIN_U=0 +OKX_OPTIONS_CLOSE_HOLD_SECONDS=120 +# 对冲买期权等成交超时(秒);超时撤未成交部分,未完全成交则开仓失败 +OKX_OPTIONS_OPEN_FILL_TIMEOUT_SEC=12 + +# ============================================================================= +# 对冲计划(仅 OKX;由 OKX_TRADE_MODE 控制是否启用;详见 docs/对冲计划开发方案.md) +# ============================================================================= +# 以下三项已由 OKX_TRADE_MODE 取代,保留兼容旧部署(未配置 TRADE_MODE 时仍可读) +HEDGE_PLAN_ENABLED=false +HEDGE_PLAN_SHOW_PERP_OPTIONS=true +HEDGE_PLAN_SHOW_OPTIONS_OPTIONS=true +HEDGE_PLAN_LIVE_ORDER=false +# 永期子模式:true=以期权为主;false=保险模式(页面标题前标识,不可页内切换) +HEDGE_PLAN_OPTION_PRIMARY=true +HEDGE_PLAN_OPEN_ORDER=options_first +HEDGE_PLAN_ON_PERP_SL_CLOSE_OPTIONS=true +HEDGE_PLAN_ON_PERP_TP_CLOSE_OPTIONS=false +HEDGE_PLAN_OO_CLOSE_WINNER_ONLY=true +# 方案C:期期页面显示「平仓模式」(到期平/全平);关则固定到期平.默认开启,页面默认选全平 +HEDGE_PLAN_OO_CLOSE_MODE_ENABLED=true +# 期期「做多/做空」拆分口径:budget=按权利金预算(默认);sheets=先算同张数总张数(2n)再按比例拆 +HEDGE_PLAN_OO_BIAS_SPLIT_BY=budget +# 期期「做多/做空」主腿占比(0~1,默认 0.7=7:3);做多主腿=Call,做空主腿=Put +HEDGE_PLAN_OO_BIAS_RATIO=0.7 +# 对冲与单独期权互斥(默认 true):有对冲计划不可单独开期权;有单独期权不可启动对冲;false=可同时开 +HEDGE_PLAN_OPTIONS_MUTUAL_EXCLUSIVE=true +# 半腿失败改手动补开(默认 true):不自动平已成腿,计划挂 partial,页面补开;开启时下方自动平强制无效 +HEDGE_PLAN_MANUAL_COMPLETE_ON_PARTIAL=true +# 对冲组数上限(默认 1;opening/active/partial 计入);仅永期/期期模式生效;热更 +MAX_ACTIVE_HEDGE_PLANS=1 +HEDGE_PLAN_MONITOR_POLL_SECONDS=15 +# 半腿失败自动平期权;若 MANUAL_COMPLETE_ON_PARTIAL=true 则运行时强制无效(建议一并写成 false) +HEDGE_PLAN_PARTIAL_AUTO_CLOSE_OPTION=false + +# ============================================================================= +# 关键位程序自动下单(与 POSITION_SIZING_MODE 联动,修改后须重启 PM2) +# ============================================================================= +# 默认 false = 关闭所有关键位程序自动单(箱体/收敛/斐波/假突破/触价) +# +# POSITION_SIZING_MODE=risk(以损定仓) +# false → 不执行任何关键位自动单;支撑/阻力提醒,人工下单,顺势加仓不受影响 +# true → 允许关键位全套自动(含触价) +# +# POSITION_SIZING_MODE=full_margin(全仓杠杆,须无仓切换) +# false → 不执行触价自动单 +# true → 仅回调/突破触价可程序自动开仓;箱体/斐波等仍禁止 +# +# 顺势加仓,趋势回调不受本开关控制;全仓模式下策略自动仍禁止. +KEY_AUTO_ORDER_ENABLED=false + +# ============================================================================= +# 关键位门控(页面「关键位监控」规则条与 _key_hard_checks 共用) +# ============================================================================= +# 【周期】门控 K 线周期,如 5m,15m;仅影响关键位硬条件,不改变顶栏分区 +KLINE_TIMEFRAME=5m +# OKX 遗留:突破过滤百分比(与 KEY_BREAKOUT_AMP_* 并存,程序仍读取) +KEY_BREAKOUT_LIMIT_PCT=1.5 +# 【确认K】闭合 K 序列中的棒偏移:突破棒默认 -2(倒数第2根),确认棒默认 -1(倒数第1根) +KEY_CONFIRM_BREAKOUT_BAR=-2 +KEY_CONFIRM_BAR=-1 +# 【量能】突破棒成交量 > 前 N 根均量 × 倍数(默认 N=20,倍数=1.3 即放大 30%) +KEY_VOLUME_MA_BARS=20 +KEY_VOLUME_RATIO_MIN=1.3 +# 【箱体/收敛】突破K收盘越过关键位(占该侧价格%)的下限;无上限(过猛由计划RR过滤) +KEY_BREAKOUT_AMP_MIN_PCT=0.03 +# 已不参与门控,可保留配置项兼容旧环境 +KEY_BREAKOUT_AMP_MAX_PCT=0.5 +# 【阻力/支撑】突破后微信提醒次数与间隔(分钟) +KEY_ALERT_MAX_TIMES=3 +KEY_ALERT_INTERVAL_MINUTES=5 +# 【日成交量排名】品种须在该排名前 N 名(添加关键位与运行时门控均校验) +KEY_DAILY_VOLUME_RANK_MAX=30 +# 【关键位自动开仓盈亏比】按确认K收盘 E 计算,严格大于该值才市价开仓(如 1.5 表示须 >1.5:1) +KEY_AUTO_MIN_PLANNED_RR=1.5 +# 止损:突破 K 极值向外缓冲的百分比(默认 0.5 即 0.5%) +KEY_STOP_OUTSIDE_BREAKOUT_PCT=0.5 +# 趋势单方案:止损在突破 K 极值外侧的百分比(默认 1 即 1%) +KEY_TREND_STOP_OUTSIDE_PCT=1 + +# ============================================================================= +# 交易执行 / 人工风控(页面「实盘下单」) +# ============================================================================= +# 【最大同时持仓】active 订单数达到该值后禁止人工与关键位自动再加仓(默认 1=单仓) +MAX_ACTIVE_POSITIONS=1 +# 【人工下单最低盈亏比】按当前价与 SL/TP 计算,低于该值前后端均拒绝(默认 1.4,即须 >=1.4:1) +MANUAL_MIN_PLANNED_RR=1.4 +# 【关键位连开计仓】true=已有持仓时关键位自动单仍按「无仓时」资金快照算保证金基数 +KEY_SIZING_USE_ZERO_POSITION_SNAPSHOT=true +# 【单日开仓 AI 提醒】本交易日开仓达到该次数时推送企业微信 AI 克制提醒(不拦单) +DAILY_OPEN_ALERT_THRESHOLD=5 +# 【单日开仓硬上限】本交易日开仓次数>=该值后禁止一切新开仓直至下一交易日(北京时间 TRADING_DAY_RESET_HOUR 切日);0=不启用 +DAILY_OPEN_HARD_LIMIT=0 + +# ============================================================================= +# 账户冷静期 / 日冻结风控(手动平仓,外部平仓,复盘情绪标签) +# 详见 docs/account-risk-cooldown.md +# ============================================================================= +RISK_CONTROL_ENABLED=true +RISK_COOLING_HOURS_MANUAL=4 +RISK_COOLING_HOURS_MANUAL_JOURNAL=1 +RISK_MANUAL_CLOSE_DAILY_LIMIT=2 +# 日亏损次数上限:平仓盈亏<0 计1次;达限当日冻结开仓;0=不启用 +RISK_DAILY_LOSS_LIMIT=2 +RISK_MOOD_ISSUES_DAILY_FREEZE=true + +# 资金与仓位刷新周期(秒) +BALANCE_REFRESH_SECONDS=60 +# 前端价格快照轮询(秒) +PRICE_REFRESH_SECONDS=5 +# 后台监控轮询周期(秒) +MONITOR_POLL_SECONDS=3 +# 移动保本同步交易所止盈止损的最小间隔(秒),避免频繁撤挂叠单 +BREAKEVEN_EXCHANGE_MIN_INTERVAL_SEC=60 +# 重启后多少秒内不做「外部平仓」同步(避免 API 未就绪误判) +RECONCILE_STARTUP_GRACE_SEC=90 +# 连续多少次轮询确认交易所空仓后,才记为外部平仓(默认 3 次 ≈ 9 秒) +RECONCILE_FLAT_CONFIRM_POLLS=3 +# 使用可用资金时的缓冲比例(如0.98代表用98%) +FULL_MARGIN_BUFFER_RATIO=0.98 + +# ============================================================================= +# 自动划转(页顶「将 swap 补足到 XU」;与 DAILY_START_CAPITAL 独立,需一致时请设为相同值) +# ============================================================================= +AUTO_TRANSFER_ENABLED=false +# 交易账户(swap)目标余额 U:每日 8 点(北京)自动划入或划出至 funding;持仓中不划转 +AUTO_TRANSFER_AMOUNT=30 +AUTO_TRANSFER_FROM=funding +AUTO_TRANSFER_TO=swap +TRANSFER_CCY=USDT +# 北京时间该整点小时内尝试;账簿按 UTC 自然日去重 +AUTO_TRANSFER_BJ_HOUR=8 +# 强制清仓整点(北京时间,默认 0=凌晨00点) +FORCE_CLOSE_BJ_HOUR=0 +# 是否启用强制清仓(默认关闭,true 才会在整点执行) +FORCE_CLOSE_ENABLED=false +# 强制清仓执行窗口(分钟,默认 5;该窗口内禁止开仓) +FORCE_CLOSE_GRACE_MINUTES=5 + +# 推送与AI超时(秒) +WECHAT_TIMEOUT_SECONDS=10 +AI_TIMEOUT_SECONDS=120 + +# AI 复盘服务地址(本机 Ollama 默认地址) +AI_PROVIDER=openai +OPENAI_API_BASE=https://op.bz121.com/v1 +OPENAI_API_KEY=你的密钥 +OPENAI_MODEL=gemma4:e4b +OLLAMA_API=http://127.0.0.1:11434/api/generate +AI_MODEL=huihui_ai/deepseek-r1-abliterated:latest + +# OKX 代理(可选,仅本地开发网络受限时用;云服务器部署请留空,直连 OKX 即可) +# 1) 先在本机建立隧道(示例): +# ssh -N -D 127.0.0.1:1080 root@你的VPS_IP -o ServerAliveInterval=30 -o ExitOnForwardFailure=yes +# 2) 再启用下面这一行(推荐 socks5h,让远端解析域名): +# OKX_SOCKS_PROXY=socks5h://127.0.0.1:1080 +# +# 如你更偏向 HTTP 代理(VPS 上跑 tinyproxy 之类),可用: +# OKX_HTTP_PROXY=http://127.0.0.1:3128 +# OKX_HTTPS_PROXY=http://127.0.0.1:3128 + +# 开仓多周期K线图(可选) +# ORDER_CHART_ENABLED=true +# ORDER_CHART_TFS=4h,1h,15m,5m +# ORDER_CHART_LIMIT=100 +# ORDER_CHART_DIR=static/images/order_charts +# 详见上文 DAILY_OPEN_ALERT_THRESHOLD / DAILY_OPEN_HARD_LIMIT;说明文档 docs/daily-open-limit.md +# 以损定仓(按交易账户资金的百分比) +# RISK_PERCENT=2 +# 移动保本触发(达到多少R触发)与偏移(百分比) +# BREAKEVEN_RR_TRIGGER=1.0 +# 移动保本阶梯(每多少R继续上移一次,默认1R) +# BREAKEVEN_STEP_R=1.0 +# BREAKEVEN_OFFSET_PCT=0.02 +# 开单风格默认值:trend / swing +# DEFAULT_TRADE_STYLE=trend + +APP_TIMEZONE=Asia/Shanghai +# TRADING_DAY_RESET_HOUR 现在表示「北京时间」整点,默认 8 点起算新交易日;开仓整点限制见 TRADING_DAY_RESET_OPEN_GUARD_ENABLED + +# --- CoinOX 模拟资金 --- +# sim=本地模拟资金撮合; live=实盘账户 +# 默认模拟盘(本地钱包); 切实盘请改为 live 并配置 API +SIM_DEFAULT_MODE=sim +SIM_INITIAL_EQUITY_USDT=10000 +SIM_INITIAL_USDC=0 +SIM_FEE_RATE=0.0005 diff --git a/app.py b/app.py index e3cec91..cd2592d 100644 --- a/app.py +++ b/app.py @@ -305,8 +305,21 @@ def resolve_path(path_value): app = Flask(__name__) app.secret_key = os.getenv("FLASK_SECRET_KEY", "coinox_2026_secret_key") from lib.instance.instance_embed_lib import attach_embed_templates, redirect_to_embed_shell_if_enabled +from jinja2 import ChoiceLoader, FileSystemLoader +from lib.paths import strategy_templates_dir attach_embed_templates(app, _REPO_ROOT) +# 复盘页引用 strategy 模板字段;CoinOX 不装策略交易,仍挂载模板目录 +_strategy_tpl = strategy_templates_dir(_REPO_ROOT) +if os.path.isdir(_strategy_tpl): + _existing = app.jinja_loader + _loaders = [FileSystemLoader(_strategy_tpl)] + if _existing is not None: + if isinstance(_existing, ChoiceLoader): + _loaders = list(_existing.loaders) + _loaders + else: + _loaders.insert(0, _existing) + app.jinja_loader = ChoiceLoader(_loaders) # ====================== 登录配置 ====================== USERNAME = os.getenv("APP_USERNAME", "dekun") @@ -368,7 +381,8 @@ _promote_legacy_options_api_keys() OKX_API_KEY = normalize_api_credential(os.getenv("OKX_API_KEY")) OKX_API_SECRET = normalize_api_credential(os.getenv("OKX_API_SECRET")) OKX_API_PASSPHRASE = normalize_api_credential(os.getenv("OKX_API_PASSPHRASE")) -OKX_OPTIONS_ENABLED = os.getenv("OKX_OPTIONS_ENABLED", "false").lower() in ("1", "true", "yes", "on") +# CoinOX 默认开启期权;模拟盘可不配 OKX API +OKX_OPTIONS_ENABLED = os.getenv("OKX_OPTIONS_ENABLED", "true").lower() in ("1", "true", "yes", "on") OKX_OPTIONS_TRADE_BUDGET_USDC = float(os.getenv("OKX_OPTIONS_TRADE_BUDGET_USDC", "10")) OKX_OPTIONS_DEFAULT_UNDERLY = (os.getenv("OKX_OPTIONS_DEFAULT_UNDERLY") or "ETH").strip().upper() OKX_TD_MODE = os.getenv("OKX_TD_MODE", "cross") @@ -9911,11 +9925,12 @@ install_hedge_plan(app, _REPO_ROOT, app_module=sys.modules[__name__]) try: from lib.sim.hooks import patch_options_cfg - _opt_cfg = app.extensions.get("options_cfg") - if isinstance(_opt_cfg, dict): - patch_options_cfg(_opt_cfg) + for _cfg_key in ("options_cfg", "hedge_plan_cfg"): + _cfg = app.extensions.get(_cfg_key) + if isinstance(_cfg, dict): + patch_options_cfg(_cfg) except Exception as _sim_patch_err: - print(f"[sim] patch options_cfg: {_sim_patch_err}", flush=True) + print(f"[sim] patch options/hedge cfg: {_sim_patch_err}", flush=True) _purge_key_monitors_if_full_margin() diff --git a/lib/hedge_plan/hedge_plan_calc_lib.py b/lib/hedge_plan/hedge_plan_calc_lib.py index b44cc53..53b4540 100644 --- a/lib/hedge_plan/hedge_plan_calc_lib.py +++ b/lib/hedge_plan/hedge_plan_calc_lib.py @@ -716,6 +716,7 @@ def gate_status( show_options_options: bool = True, mutual_exclusive: bool = True, has_standalone_option: bool = False, + sim_mode: bool = False, ) -> dict[str, Any]: from lib.trade.position_sizing_lib import is_full_margin_mode @@ -739,7 +740,8 @@ def gate_status( can_preview = False can_start = False reasons.append("期期对冲已隐藏(HEDGE_PLAN_SHOW_OPTIONS_OPTIONS)") - if not live_order: + # 模拟盘用本地钱包撮合,不要求实盘开关 / API + if not sim_mode and not live_order: can_start = False reasons.append("未允许对冲真实下单(HEDGE_PLAN_LIVE_ORDER)") if active_count >= max(1, int(max_active or 1)): @@ -749,10 +751,10 @@ def gate_status( can_start = False reasons.append("存在单独期权持仓,禁止启动对冲计划(互斥门控)") if pt == "perp_options": - if not full: + if not full and not sim_mode: can_start = False reasons.append("永期开仓仅全仓模式可用(当前可测算)") - if not live_trading: + if not sim_mode and not live_trading: can_start = False reasons.append("未开启实盘(LIVE_TRADING_ENABLED)") elif pt == "options_options": @@ -770,6 +772,7 @@ def gate_status( "plan_type": pt, "live_order": live_order, "live_trading": live_trading, + "sim_mode": bool(sim_mode), "active_count": active_count, "max_active": max_active, "show_perp_options": bool(show_perp_options), diff --git a/lib/hedge_plan/hedge_plan_register.py b/lib/hedge_plan/hedge_plan_register.py index f770383..8d1f8a2 100644 --- a/lib/hedge_plan/hedge_plan_register.py +++ b/lib/hedge_plan/hedge_plan_register.py @@ -93,7 +93,7 @@ def _build_cfg(app_module: Any) -> dict[str, Any]: "alt_leverage": int(getattr(app_module, "ALT_LEVERAGE", 5) or 5), "full_margin_buffer": float(getattr(app_module, "FULL_MARGIN_BUFFER_RATIO", 0.98) or 0.98), "funds_decimals": int(getattr(app_module, "FUNDS_DECIMALS", 2) or 2), - "options_enabled": _env_bool("OKX_OPTIONS_ENABLED", False), + "options_enabled": _env_bool("OKX_OPTIONS_ENABLED", True), "default_underly": (os.getenv("OKX_OPTIONS_DEFAULT_UNDERLY") or "ETH").strip().upper(), "chain_max_dte": float(os.getenv("OKX_OPTIONS_CHAIN_MAX_DTE_DAYS") or os.getenv("OKX_OPTIONS_MAX_DTE_DAYS") or "14"), "perp_account_label": (os.getenv("OKX_ACCOUNT_LABEL") or "合约账户").strip(), @@ -195,6 +195,15 @@ def _gates_dict(cfg: dict[str, Any], plan_type: str) -> dict[str, Any]: # fail-closed:探测失败视为不可开仓 active = 10**9 has_standalone = True + sim_mode = False + try: + from lib.sim.mode_lib import is_sim_mode + + get_db = cfg.get("get_db") + if callable(get_db): + sim_mode = bool(is_sim_mode(get_db)) + except Exception: + sim_mode = False return gate_status( hedge_enabled=_hedge_enabled(), sizing_mode=load_position_sizing_mode(), @@ -208,6 +217,7 @@ def _gates_dict(cfg: dict[str, Any], plan_type: str) -> dict[str, Any]: show_options_options=_show_options_options(), mutual_exclusive=mutual, has_standalone_option=has_standalone, + sim_mode=sim_mode, ) diff --git a/lib/instance/instance_settings_lib.py b/lib/instance/instance_settings_lib.py index 5a3e12b..3fa493a 100644 --- a/lib/instance/instance_settings_lib.py +++ b/lib/instance/instance_settings_lib.py @@ -160,7 +160,7 @@ def build_instance_settings_view( } ) - if (exchange_key or "").strip().lower() == "okx" and _env_bool("OKX_OPTIONS_ENABLED", False): + if (exchange_key or "").strip().lower() == "okx" and _env_bool("OKX_OPTIONS_ENABLED", True): api_key = (os.getenv("OKX_API_KEY") or "").strip() sections.append( { @@ -194,7 +194,7 @@ def build_instance_settings_view( "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), + and _env_bool("OKX_OPTIONS_ENABLED", True), "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), diff --git a/lib/options/options_register.py b/lib/options/options_register.py index 2823143..3302362 100644 --- a/lib/options/options_register.py +++ b/lib/options/options_register.py @@ -54,7 +54,7 @@ def attach_options_templates(app: Flask, repo_root: str) -> None: def install_options_trading(app: Flask, repo_root: str, app_module: Any) -> None: - enabled = _env_bool("OKX_OPTIONS_ENABLED", False) + enabled = _env_bool("OKX_OPTIONS_ENABLED", True) attach_options_templates(app, repo_root) cfg = _build_cfg(app_module) app.extensions["options_cfg"] = cfg @@ -95,7 +95,7 @@ def _build_cfg(app_module: Any) -> dict[str, Any]: ) return { - "enabled": _env_bool("OKX_OPTIONS_ENABLED", False), + "enabled": _env_bool("OKX_OPTIONS_ENABLED", True), "get_db": app_module.get_db, "login_required": app_module.login_required, "exchange_options": getattr(app_module, "exchange_options", None), diff --git a/lib/sim/hooks.py b/lib/sim/hooks.py index 6e019fa..37399b1 100644 --- a/lib/sim/hooks.py +++ b/lib/sim/hooks.py @@ -74,6 +74,69 @@ def apply_sim_hooks(app_module: Any) -> None: app_module.get_available_trading_usdt = get_available_trading_usdt app_module.get_live_position_contracts = get_live_position_contracts + _orig_place = getattr(app_module, "place_exchange_order", None) + _orig_close = getattr(app_module, "close_exchange_order", None) + if callable(_orig_place): + + def place_exchange_order( + exchange_symbol, + direction, + amount, + leverage, + stop_loss=None, + take_profit=None, + ): + if is_sim_mode(app_module.get_db): + ex = getattr(app_module, "exchange", None) + if ex is None: + raise RuntimeError("sim: 无公开行情 exchange") + ensure = getattr(app_module, "ensure_markets_loaded", None) + if callable(ensure): + try: + ensure() + except Exception: + pass + return broker().place_perp_market( + ex, + symbol=exchange_symbol, + direction=direction, + contracts=float(amount), + leverage=int(leverage or 1), + stop_loss=stop_loss, + take_profit=take_profit, + ) + return _orig_place( + exchange_symbol, + direction, + amount, + leverage, + stop_loss=stop_loss, + take_profit=take_profit, + ) + + app_module.place_exchange_order = place_exchange_order + + if callable(_orig_close): + + def close_exchange_order(order_row): + if is_sim_mode(app_module.get_db): + ex = getattr(app_module, "exchange", None) + if ex is None: + raise RuntimeError("sim: 无公开行情 exchange") + row = order_row or {} + symbol = row.get("exchange_symbol") or row.get("symbol") + direction = row.get("direction") + contracts = float(row.get("order_amount") or 0) + return broker().close_perp_market( + ex, + symbol=str(symbol or ""), + direction=str(direction or ""), + contracts=contracts if contracts > 0 else None, + ) + return _orig_close(order_row) + + app_module.close_exchange_order = close_exchange_order + _orig_xfer = getattr(app_module, "execute_transfer_usdt", None) if callable(_orig_xfer): diff --git a/scripts/bootstrap_deploy_secrets.py b/scripts/bootstrap_deploy_secrets.py index 7a02c74..c5914a8 100644 --- a/scripts/bootstrap_deploy_secrets.py +++ b/scripts/bootstrap_deploy_secrets.py @@ -64,6 +64,8 @@ def main() -> int: updates["OKX_OPTIONS_MARGIN_MODE"] = "coin" if not (env_get(lines, "EXCHANGE_DISPLAY_NAME") or "").strip(): updates["EXCHANGE_DISPLAY_NAME"] = "CoinOX" + if not (env_get(lines, "OKX_OPTIONS_ENABLED") or "").strip(): + updates["OKX_OPTIONS_ENABLED"] = "true" live = (env_get(lines, "LIVE_TRADING_ENABLED") or "").strip().lower() if live in ("",): updates["LIVE_TRADING_ENABLED"] = "false"