单独期权增加翻倍出场:可开关、自选倍数(默认1倍=盈利等于权利金),达标后买一限价平。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -763,6 +763,12 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
return jsonify({"ok": False, "msg": "目标位无效"})
|
||||
if target_index <= 0:
|
||||
return jsonify({"ok": False, "msg": "目标位无效"})
|
||||
profit_exit_enabled = bool(data.get("profit_exit_enabled"))
|
||||
profit_exit_mult = 1.0
|
||||
if profit_exit_enabled:
|
||||
from lib.options.options_profit_exit_lib import normalize_profit_exit_mult
|
||||
|
||||
profit_exit_mult = normalize_profit_exit_mult(data.get("profit_exit_mult"), default=1.0)
|
||||
if not inst_id:
|
||||
return jsonify({"ok": False, "msg": "缺少 inst_id"})
|
||||
q = cfg["quote_option_contract"](ex, inst_id)
|
||||
@@ -926,6 +932,9 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
open_opt_type = None
|
||||
try:
|
||||
init_options_tables(conn)
|
||||
from lib.options.options_profit_exit_lib import ensure_profit_exit_columns
|
||||
|
||||
ensure_profit_exit_columns(conn)
|
||||
meta = q.get("meta") or {}
|
||||
u = str(meta.get("uly") or inst_id).split("-")[0]
|
||||
opt_type = meta.get("optType")
|
||||
@@ -935,8 +944,9 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
"""
|
||||
INSERT INTO options_trades
|
||||
(inst_id, underlying, opt_type, strike, exp_time, sheets, eth_amount,
|
||||
open_quote, premium_paid, status, signal_note, exchange_ord_id)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'open', ?, ?)
|
||||
open_quote, premium_paid, status, signal_note, exchange_ord_id,
|
||||
profit_exit_enabled, profit_exit_mult, profit_exit_state)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, 'open', ?, ?, ?, ?, ?)
|
||||
""",
|
||||
(
|
||||
inst_id,
|
||||
@@ -950,6 +960,9 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
sizing["total_premium"],
|
||||
signal_note,
|
||||
ord_id,
|
||||
1 if profit_exit_enabled else 0,
|
||||
profit_exit_mult if profit_exit_enabled else 1.0,
|
||||
"active" if profit_exit_enabled else "idle",
|
||||
),
|
||||
)
|
||||
trade_id = int(cur.lastrowid)
|
||||
@@ -965,6 +978,8 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
trade_id=trade_id,
|
||||
sheets=sheets,
|
||||
)
|
||||
if profit_exit_enabled:
|
||||
pass # 列已由 init_options_tables / ensure 迁移
|
||||
conn.commit()
|
||||
finally:
|
||||
conn.close()
|
||||
@@ -1083,9 +1098,11 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
conn = cfg["get_db"]()
|
||||
try:
|
||||
from lib.options.options_target_lib import targets_by_inst
|
||||
from lib.options.options_profit_exit_lib import profit_exit_by_inst
|
||||
from lib.hedge_plan.hedge_plan_db import active_options_targets_by_inst
|
||||
|
||||
tgt_map = targets_by_inst(conn)
|
||||
profit_exit_map = profit_exit_by_inst(conn)
|
||||
hedge_target_map = active_options_targets_by_inst(conn)
|
||||
rows = []
|
||||
for p in raw:
|
||||
@@ -1104,6 +1121,12 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
row["target_index"] = mon.get("target_index")
|
||||
row["target_monitor_id"] = mon.get("id")
|
||||
row["target_monitor"] = mon
|
||||
pe = profit_exit_map.get(inst)
|
||||
if pe:
|
||||
row["profit_exit_enabled"] = pe.get("profit_exit_enabled")
|
||||
row["profit_exit_mult"] = pe.get("profit_exit_mult")
|
||||
row["profit_exit_state"] = pe.get("profit_exit_state")
|
||||
row["profit_exit_required_recycle"] = pe.get("required_recycle")
|
||||
hedge_target = hedge_target_map.get(inst)
|
||||
if hedge_target:
|
||||
row["hedge_plan_target"] = hedge_target
|
||||
@@ -1230,6 +1253,62 @@ def register_options_routes(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@app.route("/api/options/profit-exit", methods=["POST"])
|
||||
@lr
|
||||
def api_options_profit_exit_set():
|
||||
ex, err = _require_options_ex(cfg)
|
||||
if ex is None:
|
||||
return jsonify({"ok": False, "msg": err})
|
||||
data = request.get_json(silent=True) or {}
|
||||
inst_id = (data.get("inst_id") or "").strip()
|
||||
if not inst_id:
|
||||
return jsonify({"ok": False, "msg": "缺少 inst_id"})
|
||||
try:
|
||||
from lib.hedge_plan.hedge_plan_db import (
|
||||
active_hedge_option_inst_ids,
|
||||
init_hedge_plan_tables,
|
||||
)
|
||||
|
||||
conn_h = cfg["get_db"]()
|
||||
try:
|
||||
init_hedge_plan_tables(conn_h)
|
||||
if inst_id in active_hedge_option_inst_ids(conn_h):
|
||||
return jsonify(
|
||||
{
|
||||
"ok": False,
|
||||
"msg": "该合约属于进行中的对冲计划,请在对冲计划中管理,禁止在期权页设置翻倍出场",
|
||||
}
|
||||
)
|
||||
finally:
|
||||
conn_h.close()
|
||||
except Exception as e:
|
||||
return jsonify({"ok": False, "msg": f"对冲托管校验失败: {e}"})
|
||||
enabled_raw = data.get("enabled")
|
||||
if enabled_raw is None:
|
||||
enabled_raw = data.get("profit_exit_enabled")
|
||||
enabled = bool(enabled_raw) and str(enabled_raw).strip().lower() not in (
|
||||
"0",
|
||||
"false",
|
||||
"off",
|
||||
"no",
|
||||
)
|
||||
from lib.options.options_profit_exit_lib import normalize_profit_exit_mult, set_profit_exit
|
||||
|
||||
mult = normalize_profit_exit_mult(data.get("mult", data.get("profit_exit_mult")), default=1.0)
|
||||
raw = cfg["fetch_option_positions"](ex)
|
||||
if raw is None:
|
||||
return jsonify({"ok": False, "msg": "获取期权持仓失败"})
|
||||
if not _find_position(raw, inst_id):
|
||||
return jsonify({"ok": False, "msg": "未找到持仓"})
|
||||
conn = cfg["get_db"]()
|
||||
try:
|
||||
out = set_profit_exit(conn, inst_id=inst_id, enabled=enabled, mult=mult)
|
||||
if out.get("ok"):
|
||||
conn.commit()
|
||||
return jsonify(out)
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
@app.route("/api/options/close", methods=["POST"])
|
||||
@lr
|
||||
def api_options_close():
|
||||
@@ -1583,6 +1662,24 @@ def _start_monitor_thread(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
pass
|
||||
return result
|
||||
|
||||
def _profit_exit_close(inst_id: str) -> dict[str, Any]:
|
||||
from lib.options.options_profit_exit_lib import close_option_by_bid_profit_exit
|
||||
|
||||
ex = cfg.get("exchange_options")
|
||||
if ex is None:
|
||||
return {"ok": False, "msg": "期权 exchange 未就绪"}
|
||||
result = close_option_by_bid_profit_exit(cfg, ex, inst_id)
|
||||
if result.get("ok"):
|
||||
try:
|
||||
_sync_options_trades(cfg, force=True)
|
||||
except Exception:
|
||||
pass
|
||||
try:
|
||||
_mark_balances_stale(cfg)
|
||||
except Exception:
|
||||
pass
|
||||
return result
|
||||
|
||||
def _stale_pending() -> dict[str, Any]:
|
||||
from lib.exchange.okx_options_lib import invalidate_option_positions_cache
|
||||
from lib.options.options_pending_lib import cancel_stale_close_pending_orders
|
||||
@@ -1633,6 +1730,8 @@ def _start_monitor_thread(app: Flask, cfg: dict[str, Any]) -> None:
|
||||
"profit_ratio": cfg["profit_ratio"],
|
||||
"sync_trades_fn": _sync,
|
||||
"target_close_fn": _target_close,
|
||||
"profit_exit_close_fn": _profit_exit_close,
|
||||
"profit_exit_cfg": cfg,
|
||||
"stale_pending_fn": _stale_pending,
|
||||
},
|
||||
daemon=True,
|
||||
|
||||
Reference in New Issue
Block a user