Merge OKX dual APIs into one OKX_API_* account for perp and options.
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -284,7 +284,7 @@
|
||||
setSettingsSubTabInUrl("transfer");
|
||||
return "transfer";
|
||||
}
|
||||
if (path.indexOf("/api/options/transfer") >= 0 || path.indexOf("/api/options/cross-transfer") >= 0) {
|
||||
if (path.indexOf("/api/options/transfer") >= 0) {
|
||||
setSettingsSubTabInUrl("options_transfer");
|
||||
return "options_transfer";
|
||||
}
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
|
||||
const SWAP_BTNS = ["opt-set-swap-btn", "opt-set-swap-all-btn"];
|
||||
const INT_BTNS = ["opt-set-int-btn", "opt-set-int-all-btn"];
|
||||
const CROSS_BTNS = ["opt-set-cross-btn", "opt-set-cross-all-btn"];
|
||||
|
||||
async function apiJson(url, opts) {
|
||||
const r = await fetch(url, Object.assign({ credentials: "same-origin" }, opts || {}));
|
||||
@@ -269,80 +268,6 @@
|
||||
});
|
||||
}
|
||||
|
||||
async function submitCrossTransfer(amount) {
|
||||
setButtonsBusy(CROSS_BTNS, true, "划转中…");
|
||||
setMsg("opt-set-cross-msg", "划转中…", false);
|
||||
try {
|
||||
const d = await apiJson("/api/options/cross-transfer", {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({
|
||||
ccy: document.getElementById("opt-set-cross-ccy").value,
|
||||
amount: amount,
|
||||
from_account: document.getElementById("opt-set-cross-from").value,
|
||||
to_account: document.getElementById("opt-set-cross-to").value,
|
||||
direction: document.getElementById("opt-set-cross-dir").value,
|
||||
}),
|
||||
});
|
||||
if (d.ok) {
|
||||
setMsg("opt-set-cross-msg", "划转成功", false);
|
||||
refreshFundsAfterMutation();
|
||||
} else {
|
||||
setMsg("opt-set-cross-msg", "划转失败:" + (d.msg || "未知错误"), true);
|
||||
}
|
||||
return d;
|
||||
} catch (e) {
|
||||
setMsg("opt-set-cross-msg", "划转失败:" + (e.message || "网络错误"), true);
|
||||
return { ok: false };
|
||||
} finally {
|
||||
setButtonsBusy(CROSS_BTNS, false);
|
||||
}
|
||||
}
|
||||
|
||||
const crossBtn = document.getElementById("opt-set-cross-btn");
|
||||
if (crossBtn) {
|
||||
crossBtn.addEventListener("click", async function () {
|
||||
const amount = parseFloat(document.getElementById("opt-set-cross-amount").value);
|
||||
if (!amount || amount <= 0) {
|
||||
setMsg("opt-set-cross-msg", "请输入有效数量", true);
|
||||
return;
|
||||
}
|
||||
await submitCrossTransfer(amount);
|
||||
});
|
||||
}
|
||||
|
||||
const crossAllBtn = document.getElementById("opt-set-cross-all-btn");
|
||||
if (crossAllBtn) {
|
||||
crossAllBtn.addEventListener("click", async function () {
|
||||
try {
|
||||
const ccy = document.getElementById("opt-set-cross-ccy").value;
|
||||
const from = document.getElementById("opt-set-cross-from").value;
|
||||
const to = document.getElementById("opt-set-cross-to").value;
|
||||
const direction = document.getElementById("opt-set-cross-dir").value;
|
||||
const scope = direction === "sub_to_main" ? "sub" : "main";
|
||||
const sideLabel = direction === "sub_to_main" ? "子账户" : "主账户";
|
||||
const amount = await resolveMaxAmount(from, ccy, scope);
|
||||
if (!amount) {
|
||||
setMsg("opt-set-cross-msg", sideLabel + "划出账户可用余额不足", true);
|
||||
return;
|
||||
}
|
||||
const msg =
|
||||
"确认全部划转?\n\n" +
|
||||
"方向:" + (direction === "main_to_sub" ? "主 → 子" : "子 → 主") + "\n" +
|
||||
"币种:" + ccy + "\n" +
|
||||
"划出:" + sideLabel + " · " + accountLabel(from) + "\n" +
|
||||
"划入:" + (direction === "main_to_sub" ? "子账户" : "主账户") + " · " + accountLabel(to) + "\n" +
|
||||
"金额:" + fmtAmt(amount, ccy) + "\n\n" +
|
||||
"将划转该账户全部可用余额。";
|
||||
if (!confirmOk(msg)) return;
|
||||
document.getElementById("opt-set-cross-amount").value = String(amount);
|
||||
await submitCrossTransfer(amount);
|
||||
} catch (e) {
|
||||
setMsg("opt-set-cross-msg", "划转失败:" + (e.message || "余额拉取失败"), true);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function hardenAmountAutofill(ids) {
|
||||
ids.forEach(function (id) {
|
||||
const el = document.getElementById(id);
|
||||
@@ -366,7 +291,7 @@
|
||||
}
|
||||
|
||||
// 全部划转/兑换前去掉 readonly,避免写不进数量
|
||||
["opt-set-swap-all-btn", "opt-set-int-all-btn", "opt-set-cross-all-btn"].forEach(function (btnId) {
|
||||
["opt-set-swap-all-btn", "opt-set-int-all-btn"].forEach(function (btnId) {
|
||||
const btn = document.getElementById(btnId);
|
||||
if (!btn) return;
|
||||
btn.addEventListener(
|
||||
@@ -375,7 +300,6 @@
|
||||
const map = {
|
||||
"opt-set-swap-all-btn": "opt-set-swap-amount",
|
||||
"opt-set-int-all-btn": "opt-set-int-amount",
|
||||
"opt-set-cross-all-btn": "opt-set-cross-amount",
|
||||
};
|
||||
const input = document.getElementById(map[btnId]);
|
||||
if (input) input.removeAttribute("readonly");
|
||||
@@ -384,5 +308,5 @@
|
||||
);
|
||||
});
|
||||
|
||||
hardenAmountAutofill(["opt-set-swap-amount", "opt-set-int-amount", "opt-set-cross-amount"]);
|
||||
hardenAmountAutofill(["opt-set-swap-amount", "opt-set-int-amount"]);
|
||||
})();
|
||||
|
||||
Vendored
+4
-7
@@ -20,8 +20,8 @@ from lib.env.env_schema import (
|
||||
_EXCHANGE_LIVE_FIELDS: dict[str, list[tuple[str, str, str]]] = {
|
||||
"okx": [
|
||||
("LIVE_TRADING_ENABLED", "开启实盘下单", "关闭时仅走本地流程,不向交易所发单"),
|
||||
("OKX_API_KEY", "API Key", "永续子账户"),
|
||||
("OKX_API_SECRET", "API Secret", "永续子账户"),
|
||||
("OKX_API_KEY", "API Key", "账户 API(永续+期权共用)"),
|
||||
("OKX_API_SECRET", "API Secret", "账户 API(永续+期权共用)"),
|
||||
("OKX_API_PASSPHRASE", "API Passphrase", "OKX 必填"),
|
||||
("OKX_TD_MODE", "保证金模式", ""),
|
||||
("OKX_POS_MODE", "持仓模式", ""),
|
||||
@@ -30,7 +30,7 @@ _EXCHANGE_LIVE_FIELDS: dict[str, list[tuple[str, str, str]]] = {
|
||||
(
|
||||
"OKX_SHOW_PERP_FUNDS",
|
||||
"显示永续资金",
|
||||
"默认开启;关闭后顶栏隐藏永续资金账户与交易账户,总资金仅计期权侧",
|
||||
"默认开启;关闭后顶栏隐藏 USDT 资金账户与交易账户,总资金仅计期权 USDC 侧",
|
||||
),
|
||||
],
|
||||
"binance": [
|
||||
@@ -140,10 +140,7 @@ _OPTIONS_SECTION: dict[str, Any] = {
|
||||
"title": "期权账户",
|
||||
"exchanges": frozenset({"okx"}),
|
||||
"fields": [
|
||||
("OKX_OPTIONS_ENABLED", "启用期权模块", ""),
|
||||
("OKX_OPTIONS_API_KEY", "期权 API Key", "主账户,与永续子账户分离"),
|
||||
("OKX_OPTIONS_API_SECRET", "期权 API Secret", ""),
|
||||
("OKX_OPTIONS_API_PASSPHRASE", "期权 API Passphrase", ""),
|
||||
("OKX_OPTIONS_ENABLED", "启用期权模块", "与永续共用上方 OKX_API_*;不再单独配置期权密钥"),
|
||||
("OKX_OPTIONS_ACCOUNT_LABEL", "期权账户备注", ""),
|
||||
("OKX_OPTIONS_TRADE_BUDGET_USDC", "单笔预算(USDC)", ""),
|
||||
("OKX_OPTIONS_BUDGET_BUFFER", "预算缓冲比例", "如 0.95"),
|
||||
|
||||
@@ -72,16 +72,22 @@ def td_mode_for_option_buy(configured: str | None = None) -> str:
|
||||
|
||||
|
||||
def create_options_exchange(
|
||||
api_key: str,
|
||||
api_secret: str,
|
||||
passphrase: str,
|
||||
api_key: str = "",
|
||||
api_secret: str = "",
|
||||
passphrase: str = "",
|
||||
proxies: dict[str, str] | None = None,
|
||||
) -> ccxt.okx:
|
||||
"""创建 option 客户端.未传密钥时读 OKX_API_*(与永续同源)."""
|
||||
import os
|
||||
|
||||
key = (api_key or os.getenv("OKX_API_KEY") or "").strip()
|
||||
secret = (api_secret or os.getenv("OKX_API_SECRET") or "").strip()
|
||||
password = (passphrase or os.getenv("OKX_API_PASSPHRASE") or "").strip()
|
||||
ex = ccxt.okx(
|
||||
{
|
||||
"apiKey": api_key,
|
||||
"secret": api_secret,
|
||||
"password": passphrase,
|
||||
"apiKey": key,
|
||||
"secret": secret,
|
||||
"password": password,
|
||||
"enableRateLimit": True,
|
||||
"options": {"defaultType": "option"},
|
||||
}
|
||||
@@ -600,7 +606,10 @@ def options_header_balances(
|
||||
*,
|
||||
force: bool = False,
|
||||
) -> tuple[float | None, float | None, float | None, float | None]:
|
||||
"""顶栏四格:交易 USDC/USDT,资金 USDC/USDT(单次拉取 + 缓存)."""
|
||||
"""顶栏期权两格用 USDC;顺带返回同账户 USDT(调用方勿再计入总资金,避免与永续栏重复).
|
||||
|
||||
返回:(trading_usdc, funding_usdc, funding_usdt, trading_usdt)
|
||||
"""
|
||||
bal = fetch_options_balances(ex, force=force)
|
||||
|
||||
def _round(v: Any) -> float | None:
|
||||
@@ -1562,43 +1571,6 @@ def spot_market_swap_usdt_usdc(
|
||||
return {"ok": False, "msg": _okx_trade_error_message(e)}
|
||||
|
||||
|
||||
def transfer_main_sub_account(
|
||||
ex: ccxt.okx,
|
||||
*,
|
||||
ccy: str,
|
||||
amount: float,
|
||||
sub_acct: str,
|
||||
main_to_sub: bool,
|
||||
from_account: str = "funding",
|
||||
to_account: str = "funding",
|
||||
) -> dict[str, Any]:
|
||||
"""主账户与子账户之间划转(须主账户 API)."""
|
||||
if amount <= 0:
|
||||
return {"ok": False, "msg": "划转金额须大于 0"}
|
||||
sub = (sub_acct or "").strip()
|
||||
if not sub:
|
||||
return {"ok": False, "msg": "未配置子账户名称 OKX_SUB_ACCOUNT_NAME"}
|
||||
from_code = _OKX_ACCT_CODE.get((from_account or "funding").lower(), "6")
|
||||
to_code = _OKX_ACCT_CODE.get((to_account or "funding").lower(), "6")
|
||||
try:
|
||||
resp = ex.private_post_asset_transfer(
|
||||
{
|
||||
"type": "1" if main_to_sub else "2",
|
||||
"ccy": str(ccy).upper(),
|
||||
"amt": str(amount),
|
||||
"from": from_code,
|
||||
"to": to_code,
|
||||
"subAcct": sub,
|
||||
}
|
||||
)
|
||||
data = (resp or {}).get("data") or []
|
||||
if data and str(data[0].get("sCode", "0")) == "0":
|
||||
return {"ok": True, "data": data[0], "raw": resp}
|
||||
return {"ok": False, "msg": _okx_trade_error_message(resp=resp), "raw": resp}
|
||||
except Exception as e:
|
||||
return {"ok": False, "msg": _okx_trade_error_message(e)}
|
||||
|
||||
|
||||
def format_position_row(
|
||||
pos: dict[str, Any],
|
||||
ct_mult: float = 0.01,
|
||||
|
||||
@@ -104,12 +104,14 @@ def options_funding_label(
|
||||
funding_usdc: float | None,
|
||||
funding_usdt: float | None = None,
|
||||
) -> str:
|
||||
parts: list[str] = []
|
||||
if funding_usdc is not None and float(funding_usdc) > 0:
|
||||
parts.append(f"{float(funding_usdc):.2f} USDC")
|
||||
if funding_usdt is not None and float(funding_usdt) > 0:
|
||||
parts.append(f"{float(funding_usdt):.2f} USDT")
|
||||
return " · ".join(parts) if parts else "—"
|
||||
"""期权侧顶栏仅展示 USDC(USDT 归永续资金/交易账户).funding_usdt 参数保留兼容,忽略."""
|
||||
_ = funding_usdt
|
||||
if funding_usdc is None:
|
||||
return "—"
|
||||
try:
|
||||
return f"{float(funding_usdc):.2f} USDC"
|
||||
except (TypeError, ValueError):
|
||||
return "—"
|
||||
|
||||
|
||||
def total_funds_usdt(
|
||||
|
||||
@@ -158,24 +158,20 @@ def build_instance_settings_view(
|
||||
)
|
||||
|
||||
if (exchange_key or "").strip().lower() == "okx" and _env_bool("OKX_OPTIONS_ENABLED", False):
|
||||
opt_key = (os.getenv("OKX_OPTIONS_API_KEY") or "").strip()
|
||||
api_key = (os.getenv("OKX_API_KEY") or "").strip()
|
||||
sections.append(
|
||||
{
|
||||
"title": "期权设置",
|
||||
"rows": [
|
||||
_row("期权模块", "已启用"),
|
||||
_row(
|
||||
"期权 API",
|
||||
f"已配置(…{opt_key[-4:]})" if len(opt_key) >= 4 else "未配置",
|
||||
),
|
||||
_row(
|
||||
"子账户",
|
||||
(os.getenv("OKX_SUB_ACCOUNT_NAME") or "").strip() or "未配置 OKX_SUB_ACCOUNT_NAME",
|
||||
"主/子账户划转用",
|
||||
"账户 API",
|
||||
f"已配置(…{api_key[-4:]})" if len(api_key) >= 4 else "未配置 OKX_API_*",
|
||||
"永续与期权共用 OKX_API_*",
|
||||
),
|
||||
_row(
|
||||
"说明",
|
||||
"币种兑换与账户划转到右侧「期权设置」卡片操作",
|
||||
"币种兑换与账户内划转到右侧「期权设置」卡片操作",
|
||||
),
|
||||
],
|
||||
}
|
||||
@@ -196,7 +192,6 @@ def build_instance_settings_view(
|
||||
"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),
|
||||
"options_sub_account": (os.getenv("OKX_SUB_ACCOUNT_NAME") or "").strip(),
|
||||
"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),
|
||||
|
||||
@@ -1137,10 +1137,11 @@ function paintRealtimePnlFromSnapshot(data){
|
||||
}
|
||||
|
||||
function formatOptionsFundingLabel(usdc, usdt) {
|
||||
const parts = [];
|
||||
if (usdc !== null && usdc !== undefined && Number(usdc) > 0) parts.push(`${Number(usdc).toFixed(2)} USDC`);
|
||||
if (usdt !== null && usdt !== undefined && Number(usdt) > 0) parts.push(`${Number(usdt).toFixed(2)} USDT`);
|
||||
return parts.length ? parts.join(" · ") : "—";
|
||||
// 期权侧顶栏仅 USDC;usdt 参数忽略(USDT 在永续资金/交易账户)
|
||||
if (usdc === null || usdc === undefined || usdc === "") return "—";
|
||||
const n = Number(usdc);
|
||||
if (Number.isNaN(n)) return "—";
|
||||
return `${n.toFixed(2)} USDC`;
|
||||
}
|
||||
|
||||
function setFundsFieldText(field, text){
|
||||
|
||||
@@ -1614,10 +1614,11 @@ function paintRealtimePnlFromSnapshot(data){
|
||||
}
|
||||
|
||||
function formatOptionsFundingLabel(usdc, usdt) {
|
||||
const parts = [];
|
||||
if (usdc !== null && usdc !== undefined && Number(usdc) > 0) parts.push(`${Number(usdc).toFixed(2)} USDC`);
|
||||
if (usdt !== null && usdt !== undefined && Number(usdt) > 0) parts.push(`${Number(usdt).toFixed(2)} USDT`);
|
||||
return parts.length ? parts.join(" · ") : "—";
|
||||
// 期权侧顶栏仅 USDC;usdt 参数忽略(USDT 在永续资金/交易账户)
|
||||
if(usdc == null || usdc === "") return "—";
|
||||
const n = Number(usdc);
|
||||
if(Number.isNaN(n)) return "—";
|
||||
return `${n.toFixed(2)} USDC`;
|
||||
}
|
||||
|
||||
function setFundsFieldText(field, text){
|
||||
|
||||
@@ -35,11 +35,11 @@
|
||||
{% if options_enabled %}
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">期权资金账户</div>
|
||||
<div class="value" id="options-funding-usdc" data-funds-field="options-funding-usdc">{{ options_funding_label(options_funding_usdc, options_funding_usdt) }}</div>
|
||||
<div class="value" id="options-funding-usdc" data-funds-field="options-funding-usdc">{{ options_funding_label(options_funding_usdc) }}</div>
|
||||
</div>
|
||||
<div class="stat-strip-item">
|
||||
<div class="label">期权交易账户</div>
|
||||
<div class="value" id="options-trading-usdc" data-funds-field="options-trading-usdc">{{ options_funding_label(options_trading_usdc, options_trading_usdt) }}</div>
|
||||
<div class="value" id="options-trading-usdc" data-funds-field="options-trading-usdc">{{ options_funding_label(options_trading_usdc) }}</div>
|
||||
</div>
|
||||
{% endif %}
|
||||
<div class="stat-strip-item stat-strip-item--pnl">
|
||||
|
||||
@@ -36,7 +36,7 @@
|
||||
{% include 'password_settings_panel.html' %}
|
||||
{% elif tab.key == 'transfer' %}
|
||||
<h2>永续资金划转</h2>
|
||||
<p class="settings-subcard-desc">子账户永续:资金账户与交易账户之间划转 USDT.</p>
|
||||
<p class="settings-subcard-desc">账户内:资金账户与交易账户之间划转 USDT.</p>
|
||||
{% include 'instance_transfer_panel.html' %}
|
||||
{% elif tab.key == 'export' %}
|
||||
<h2>数据导出</h2>
|
||||
|
||||
@@ -92,12 +92,10 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
|
||||
quote_option_contract,
|
||||
spot_market_swap_usdt_usdc,
|
||||
transfer_ccy,
|
||||
transfer_main_sub_account,
|
||||
)
|
||||
|
||||
return {
|
||||
"enabled": _env_bool("OKX_OPTIONS_ENABLED", False),
|
||||
"sub_account_name": (os.getenv("OKX_SUB_ACCOUNT_NAME") or "").strip(),
|
||||
"get_db": app_module.get_db,
|
||||
"login_required": app_module.login_required,
|
||||
"exchange_options": getattr(app_module, "exchange_options", None),
|
||||
@@ -132,7 +130,6 @@ def _build_cfg(app_module: Any) -> dict[str, Any]:
|
||||
"execute_convert": execute_convert,
|
||||
"transfer_ccy": transfer_ccy,
|
||||
"spot_market_swap_usdt_usdc": spot_market_swap_usdt_usdc,
|
||||
"transfer_main_sub_account": transfer_main_sub_account,
|
||||
"options_api_ready": options_api_ready,
|
||||
"app_module": app_module,
|
||||
}
|
||||
@@ -357,13 +354,7 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
if ex is None:
|
||||
return jsonify({"ok": False, "msg": err})
|
||||
force = (request.args.get("force") or "").strip().lower() in ("1", "true", "yes")
|
||||
scope = (request.args.get("scope") or "main").strip().lower()
|
||||
bal = cfg["fetch_options_balances"](
|
||||
ex,
|
||||
force=force,
|
||||
scope=scope,
|
||||
sub_acct=cfg.get("sub_account_name") or "",
|
||||
)
|
||||
bal = cfg["fetch_options_balances"](ex, force=force, scope="main")
|
||||
return jsonify({"ok": True, **bal, "trade_budget": cfg["trade_budget"]})
|
||||
|
||||
@app.route("/api/options/chain")
|
||||
@@ -1272,54 +1263,6 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
_mark_balances_stale(cfg)
|
||||
return jsonify(result)
|
||||
|
||||
@app.route("/api/options/cross-transfer", methods=["POST"])
|
||||
@lr
|
||||
def api_options_cross_transfer():
|
||||
ex, err = _require_options_ex(cfg)
|
||||
if ex is None:
|
||||
return jsonify({"ok": False, "msg": err})
|
||||
data = request.get_json(silent=True) or {}
|
||||
ccy = (data.get("ccy") or "USDT").upper()
|
||||
direction = (data.get("direction") or "sub_to_main").strip()
|
||||
from_account = (data.get("from_account") or data.get("account") or "funding").strip()
|
||||
to_account = (data.get("to_account") or data.get("account") or "funding").strip()
|
||||
try:
|
||||
amount = float(data.get("amount"))
|
||||
except (TypeError, ValueError):
|
||||
return jsonify({"ok": False, "msg": "数量无效"})
|
||||
main_to_sub = direction == "main_to_sub"
|
||||
result = cfg["transfer_main_sub_account"](
|
||||
ex,
|
||||
ccy=ccy,
|
||||
amount=amount,
|
||||
sub_acct=cfg.get("sub_account_name") or "",
|
||||
main_to_sub=main_to_sub,
|
||||
from_account=from_account,
|
||||
to_account=to_account,
|
||||
)
|
||||
if result.get("ok"):
|
||||
conn = cfg["get_db"]()
|
||||
try:
|
||||
init_options_tables(conn)
|
||||
conn.execute(
|
||||
"""
|
||||
INSERT INTO options_transfer_log (ccy, amount, from_account, to_account, status, message)
|
||||
VALUES (?, ?, ?, ?, 'ok', ?)
|
||||
""",
|
||||
(
|
||||
ccy,
|
||||
amount,
|
||||
("main" if main_to_sub else "sub") + ":" + from_account,
|
||||
("sub" if main_to_sub else "main") + ":" + to_account,
|
||||
"cross",
|
||||
),
|
||||
)
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
_mark_balances_stale(cfg)
|
||||
return jsonify(result)
|
||||
|
||||
@app.route("/api/options/history")
|
||||
@lr
|
||||
def api_options_history():
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
data-trade-budget="{{ options_trade_budget | default(10) }}"
|
||||
data-ask-liq-filter="{% if options_chain_ask_liq_filter is defined %}{{ '1' if options_chain_ask_liq_filter else '0' }}{% else %}1{% endif %}">
|
||||
{% if not options_enabled %}
|
||||
<div class="flash" style="margin-bottom:12px">期权 API 未启用:请在 <code>crypto_monitor_okx/.env</code> 设置 <code>OKX_OPTIONS_ENABLED=true</code> 及主账户 <code>OKX_OPTIONS_API_*</code>,然后 <code>pm2 restart crypto_okx --update-env</code>.</div>
|
||||
<div class="flash" style="margin-bottom:12px">期权未启用:请在 <code>crypto_monitor_okx/.env</code> 设置 <code>OKX_OPTIONS_ENABLED=true</code> 及 <code>OKX_API_*</code>(永续与期权共用),然后 <code>pm2 restart crypto_okx --update-env</code>.</div>
|
||||
{% endif %}
|
||||
{% if options_enabled and options_open_allowed is defined and not options_open_allowed %}
|
||||
<div class="flash" style="margin-bottom:12px">当前交易模式为对冲(永期/期期),不可单独开期权;持仓可在此查看/平仓.切换请到 env「交易模式」.</div>
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
{# 期权设置脚本挂载点(卡片在 settings_panel 中拆分) #}
|
||||
<div id="options-settings-root" hidden
|
||||
data-sub-account="{{ instance_settings.options_sub_account | default('', true) }}"></div>
|
||||
<script src="/static/options_settings.js?v=9"></script>
|
||||
<div id="options-settings-root" hidden></div>
|
||||
<script src="/static/options_settings.js?v=10"></script>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="options-settings-section">
|
||||
<p class="options-settings-hint">主账户资金账户:USDT ↔ USDC 现货市价单.</p>
|
||||
<p class="options-settings-hint">账户资金账户:USDT ↔ USDC 现货市价单.</p>
|
||||
<div class="form-row settings-transfer-form options-settings-row">
|
||||
<input type="text" name="username" autocomplete="username" tabindex="-1" aria-hidden="true"
|
||||
style="position:absolute;left:-9999px;width:1px;height:1px;opacity:0" value="">
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<div class="options-settings-section">
|
||||
<div class="options-settings-subtitle">主账户内</div>
|
||||
<div class="options-settings-subtitle">账户内划转</div>
|
||||
<div class="form-row settings-transfer-form options-settings-row">
|
||||
<input type="text" name="username" autocomplete="username" tabindex="-1" aria-hidden="true"
|
||||
style="position:absolute;left:-9999px;width:1px;height:1px;opacity:0" value="">
|
||||
@@ -22,35 +22,3 @@
|
||||
</div>
|
||||
<div id="opt-set-int-msg" class="options-settings-msg muted"></div>
|
||||
</div>
|
||||
|
||||
<div class="options-settings-section">
|
||||
<div class="options-settings-subtitle">
|
||||
主子账户
|
||||
<span class="muted">({{ instance_settings.options_sub_account or '未配置' }})</span>
|
||||
</div>
|
||||
<div class="form-row settings-transfer-form options-settings-row">
|
||||
<input type="text" name="username" autocomplete="username" tabindex="-1" aria-hidden="true"
|
||||
style="position:absolute;left:-9999px;width:1px;height:1px;opacity:0" value="">
|
||||
<select id="opt-set-cross-dir" aria-label="主子方向" autocomplete="off">
|
||||
<option value="main_to_sub" selected>主 → 子</option>
|
||||
<option value="sub_to_main">子 → 主</option>
|
||||
</select>
|
||||
<select id="opt-set-cross-ccy" aria-label="币种">
|
||||
<option value="USDT" selected>USDT</option>
|
||||
<option value="USDC">USDC</option>
|
||||
</select>
|
||||
<select id="opt-set-cross-from" aria-label="划出账户">
|
||||
<option value="funding" selected>from: 资金</option>
|
||||
<option value="trading">from: 交易</option>
|
||||
</select>
|
||||
<select id="opt-set-cross-to" aria-label="划入账户">
|
||||
<option value="trading" selected>to: 交易</option>
|
||||
<option value="funding">to: 资金</option>
|
||||
</select>
|
||||
<input type="number" id="opt-set-cross-amount" name="cm_opt_cross_amt" min="0.01" step="0.01" placeholder="数量"
|
||||
autocomplete="off" inputmode="decimal" data-lpignore="true" data-1p-ignore="true" data-bwignore="true" data-form-type="other" readonly>
|
||||
<button type="button" class="btn-secondary btn-sm" id="opt-set-cross-all-btn">全部划转</button>
|
||||
<button type="button" class="btn-primary btn-sm" id="opt-set-cross-btn">划转</button>
|
||||
</div>
|
||||
<div id="opt-set-cross-msg" class="options-settings-msg muted"></div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user