feat: add OKX options module with dual API, USDT/USDC convert, and docs
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -100,6 +100,25 @@ OKX_POSITION_INST_TYPE=SWAP
|
||||
# 企业微信推送里展示的账户备注
|
||||
# OKX_ACCOUNT_LABEL=
|
||||
|
||||
# =============================================================================
|
||||
# 期权(主账户 API,与永续子账户 OKX_API_* 分离;修改后须重启 PM2)
|
||||
# 详见 docs/期权方案.md 与 docs/期权用法.md
|
||||
# =============================================================================
|
||||
OKX_OPTIONS_ENABLED=false
|
||||
OKX_OPTIONS_API_KEY=
|
||||
OKX_OPTIONS_API_SECRET=
|
||||
OKX_OPTIONS_API_PASSPHRASE=
|
||||
OKX_OPTIONS_ACCOUNT_LABEL=主账户·期权
|
||||
OKX_OPTIONS_TRADE_BUDGET_USDC=10
|
||||
OKX_OPTIONS_BUDGET_BUFFER=0.95
|
||||
OKX_OPTIONS_DEFAULT_UNDERLY=ETH
|
||||
OKX_OPTIONS_MAX_DTE_DAYS=2
|
||||
OKX_OPTIONS_ITM_MAX_DIST_USD=30
|
||||
OKX_OPTIONS_PROFIT_ALERT_RATIO=1.0
|
||||
OKX_OPTIONS_POLL_SECONDS=15
|
||||
OKX_OPTIONS_TD_MODE=cross
|
||||
OKX_OPTIONS_ALLOW_MARKET_CLOSE=false
|
||||
|
||||
# =============================================================================
|
||||
# 关键位程序自动下单(与 POSITION_SIZING_MODE 联动,修改后须重启 PM2)
|
||||
# =============================================================================
|
||||
|
||||
@@ -337,6 +337,12 @@ LIVE_TRADING_ENABLED = os.getenv("LIVE_TRADING_ENABLED", "false").lower() == "tr
|
||||
OKX_API_KEY = os.getenv("OKX_API_KEY", "")
|
||||
OKX_API_SECRET = os.getenv("OKX_API_SECRET", "")
|
||||
OKX_API_PASSPHRASE = os.getenv("OKX_API_PASSPHRASE", "")
|
||||
OKX_OPTIONS_ENABLED = os.getenv("OKX_OPTIONS_ENABLED", "false").lower() in ("1", "true", "yes", "on")
|
||||
OKX_OPTIONS_API_KEY = os.getenv("OKX_OPTIONS_API_KEY", "")
|
||||
OKX_OPTIONS_API_SECRET = os.getenv("OKX_OPTIONS_API_SECRET", "")
|
||||
OKX_OPTIONS_API_PASSPHRASE = os.getenv("OKX_OPTIONS_API_PASSPHRASE", "")
|
||||
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")
|
||||
OKX_POS_MODE = os.getenv("OKX_POS_MODE", "hedge")
|
||||
EXCHANGE_DISPLAY_NAME = (os.getenv("EXCHANGE_DISPLAY_NAME") or "OKX").strip() or "OKX"
|
||||
@@ -462,6 +468,20 @@ if OKX_API_KEY and OKX_API_SECRET and OKX_API_PASSPHRASE:
|
||||
exchange.apiKey = OKX_API_KEY
|
||||
exchange.secret = OKX_API_SECRET
|
||||
exchange.password = OKX_API_PASSPHRASE
|
||||
|
||||
exchange_options = ccxt.okx(
|
||||
{
|
||||
"enableRateLimit": True,
|
||||
"options": {"defaultType": "option"},
|
||||
}
|
||||
)
|
||||
if OKX_CCXT_PROXIES:
|
||||
exchange_options.proxies = OKX_CCXT_PROXIES
|
||||
if OKX_OPTIONS_API_KEY and OKX_OPTIONS_API_SECRET and OKX_OPTIONS_API_PASSPHRASE:
|
||||
exchange_options.apiKey = OKX_OPTIONS_API_KEY
|
||||
exchange_options.secret = OKX_OPTIONS_API_SECRET
|
||||
exchange_options.password = OKX_OPTIONS_API_PASSPHRASE
|
||||
|
||||
MARKETS_LOADED = False
|
||||
ACCOUNT_BALANCE_CACHE = {
|
||||
"updated_at": 0.0,
|
||||
@@ -1467,8 +1487,10 @@ def init_db():
|
||||
)
|
||||
|
||||
from lib.strategy.strategy_db import init_strategy_tables
|
||||
from lib.options.options_db import init_options_tables
|
||||
|
||||
init_strategy_tables(conn)
|
||||
init_options_tables(conn)
|
||||
from lib.trade.account_risk_lib import ensure_account_risk_schema
|
||||
|
||||
ensure_account_risk_schema(conn)
|
||||
@@ -6660,6 +6682,9 @@ def render_main_page(page="trade", embed_mode=None):
|
||||
key_rule_ctx=key_rule_ctx,
|
||||
funds_fmt=format_funds_u,
|
||||
exchange_display=EXCHANGE_DISPLAY_NAME,
|
||||
options_enabled=OKX_OPTIONS_ENABLED,
|
||||
options_trade_budget=OKX_OPTIONS_TRADE_BUDGET_USDC,
|
||||
options_default_underly=OKX_OPTIONS_DEFAULT_UNDERLY,
|
||||
risk_status=risk_status,
|
||||
max_active_positions=MAX_ACTIVE_POSITIONS,
|
||||
manual_min_planned_rr=MANUAL_MIN_PLANNED_RR,
|
||||
@@ -6739,6 +6764,15 @@ def settings_page():
|
||||
return render_main_page("settings")
|
||||
|
||||
|
||||
@app.route("/options")
|
||||
@login_required
|
||||
def options_main_page():
|
||||
if not OKX_OPTIONS_ENABLED:
|
||||
flash("期权模块未启用,请在 .env 设置 OKX_OPTIONS_ENABLED=true")
|
||||
return redirect(url_for("trade_page"))
|
||||
return render_main_page("options")
|
||||
|
||||
|
||||
@app.route("/api/account_snapshot")
|
||||
@login_required
|
||||
def api_account_snapshot():
|
||||
@@ -8802,6 +8836,7 @@ _REPO_STATIC_DIR = common_static_dir(os.path.dirname(BASE_DIR))
|
||||
_AI_REVIEW_RENDER_JS = os.path.join(_REPO_STATIC_DIR, "ai_review_render.js")
|
||||
_FORM_SUBMIT_GUARD_JS = os.path.join(_REPO_STATIC_DIR, "form_submit_guard.js")
|
||||
_MANUAL_ORDER_RR_PREVIEW_JS = os.path.join(_REPO_STATIC_DIR, "manual_order_rr_preview.js")
|
||||
_OPTIONS_PANEL_JS = os.path.join(_REPO_STATIC_DIR, "options_panel.js")
|
||||
|
||||
|
||||
@app.route("/static/ai_review_render.js")
|
||||
@@ -8825,6 +8860,13 @@ def static_manual_order_rr_preview_js():
|
||||
return send_file(_MANUAL_ORDER_RR_PREVIEW_JS, mimetype="application/javascript; charset=utf-8")
|
||||
|
||||
|
||||
@app.route("/static/options_panel.js")
|
||||
def static_options_panel_js():
|
||||
if not os.path.isfile(_OPTIONS_PANEL_JS):
|
||||
return Response("not found", status=404, mimetype="text/plain; charset=utf-8")
|
||||
return send_file(_OPTIONS_PANEL_JS, mimetype="application/javascript; charset=utf-8")
|
||||
|
||||
|
||||
@app.route("/export/review_md/<rid>")
|
||||
@login_required
|
||||
def export_review_md(rid):
|
||||
@@ -9276,6 +9318,10 @@ from lib.strategy.strategy_trend_register import install_strategy_trend
|
||||
install_strategy_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
|
||||
install_strategy_trend(app, _REPO_ROOT, app_module=sys.modules[__name__])
|
||||
|
||||
from lib.options.options_register import install_options_trading
|
||||
|
||||
install_options_trading(app, _REPO_ROOT, app_module=sys.modules[__name__])
|
||||
|
||||
_purge_key_monitors_if_full_margin()
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user