Fix options review empty lists when search box has symbol text.

Treat the filter as fuzzy q over underlying/inst/strategy (BTCUSDT->BTC) instead of exact strategy_tag, which always wiped pending rows.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
dekun
2026-07-19 16:17:45 +08:00
parent 426afb8dbe
commit 12574ae88a
5 changed files with 87 additions and 7 deletions
+3 -3
View File
@@ -98,12 +98,12 @@
p.set("source_type", activeSource);
var uly = ($("or-filter-uly") || {}).value || "";
var opt = ($("or-filter-opt") || {}).value || "";
var strategy = (($("or-filter-strategy") || {}).value || "").trim();
var q = (($("or-filter-q") || $("or-filter-strategy") || {}).value || "").trim();
var from = ($("or-filter-from") || {}).value || "";
var to = ($("or-filter-to") || {}).value || "";
if (uly) p.set("underlying", uly);
if (opt) p.set("opt_type", opt);
if (strategy) p.set("strategy_tag", strategy);
if (q) p.set("q", q);
if (from) p.set("closed_from", from.replace("T", " ") + ":00");
if (to) p.set("closed_to", to.replace("T", " ") + ":00");
if (($("or-include-hedge-legs") || {}).checked) p.set("include_hedge_legs", "1");
@@ -1152,7 +1152,7 @@
});
}
});
["or-filter-strategy", "or-filter-from", "or-filter-to"].forEach(function (id) {
["or-filter-q", "or-filter-strategy", "or-filter-from", "or-filter-to"].forEach(function (id) {
var el = $(id);
if (el) {
el.addEventListener("change", function () {
+40 -2
View File
@@ -591,12 +591,29 @@ def enrich_trade_row(row: dict[str, Any], entry: dict[str, Any] | None = None) -
return out
def _review_search_tokens(q: str) -> list[str]:
"""自由搜索词:BTCUSDT 同时匹配 BTC / BTCUSDT."""
raw = str(q or "").strip()
if not raw:
return []
tokens = [raw]
u = raw.upper()
for suf in ("-USDT", "-USD", "-USDC", "USDT", "USD", "USDC"):
if u.endswith(suf) and len(u) > len(suf):
base = u[: -len(suf)].rstrip("-_")
if base and base not in {t.upper() for t in tokens}:
tokens.append(base)
break
return tokens
def _review_trades_filters(
*,
source_type: str | None = None,
underlying: str | None = None,
opt_type: str | None = None,
strategy_tag: str | None = None,
q: str | None = None,
reviewed: str | None = None,
include_hedge_legs: bool = False,
closed_from: str | None = None,
@@ -635,9 +652,26 @@ def _review_trades_filters(
if closed_to:
wheres.append("COALESCE(t.closed_at,'')<=?")
args.append(closed_to)
if strategy_tag:
wheres.append("e.strategy_tag=?")
# 兼容旧参数:精确策略标签;前端已改用 q 模糊搜索
if strategy_tag and not q:
wheres.append("UPPER(COALESCE(e.strategy_tag,''))=UPPER(?)")
args.append(strategy_tag)
search_tokens = _review_search_tokens(q or "")
if search_tokens:
token_ors: list[str] = []
for tok in search_tokens:
like = f"%{tok}%"
token_ors.append(
"""(
UPPER(COALESCE(t.underlying,'')) LIKE UPPER(?)
OR UPPER(COALESCE(t.inst_id,'')) LIKE UPPER(?)
OR UPPER(COALESCE(t.legs_json,'')) LIKE UPPER(?)
OR UPPER(COALESCE(e.strategy_tag,'')) LIKE UPPER(?)
OR UPPER(COALESCE(e.result_tag,'')) LIKE UPPER(?)
)"""
)
args.extend([like, like, like, like, like])
wheres.append("(" + " OR ".join(token_ors) + ")")
if reviewed == "1" or reviewed == "yes":
wheres.append("e.id IS NOT NULL")
elif reviewed == "0" or reviewed == "no":
@@ -653,6 +687,7 @@ def count_review_trades(
underlying: str | None = None,
opt_type: str | None = None,
strategy_tag: str | None = None,
q: str | None = None,
reviewed: str | None = None,
include_hedge_legs: bool = False,
closed_from: str | None = None,
@@ -664,6 +699,7 @@ def count_review_trades(
underlying=underlying,
opt_type=opt_type,
strategy_tag=strategy_tag,
q=q,
reviewed=reviewed,
include_hedge_legs=include_hedge_legs,
closed_from=closed_from,
@@ -688,6 +724,7 @@ def list_review_trades(
underlying: str | None = None,
opt_type: str | None = None,
strategy_tag: str | None = None,
q: str | None = None,
reviewed: str | None = None,
include_hedge_legs: bool = False,
closed_from: str | None = None,
@@ -701,6 +738,7 @@ def list_review_trades(
underlying=underlying,
opt_type=opt_type,
strategy_tag=strategy_tag,
q=q,
reviewed=reviewed,
include_hedge_legs=include_hedge_legs,
closed_from=closed_from,
+1
View File
@@ -135,6 +135,7 @@ def register_options_review_routes(app: Flask, cfg: dict[str, Any], repo_root: s
underlying=(request.args.get("underlying") or "").strip() or None,
opt_type=(request.args.get("opt_type") or "").strip() or None,
strategy_tag=(request.args.get("strategy_tag") or "").strip() or None,
q=(request.args.get("q") or "").strip() or None,
reviewed=(request.args.get("reviewed") or "").strip() or None,
include_hedge_legs=(request.args.get("include_hedge_legs") or "")
.strip()
@@ -94,7 +94,7 @@
<option value="C">Call</option>
<option value="P">Put</option>
</select>
<input type="text" id="or-filter-strategy" placeholder="策略标签" style="max-width:110px;font-size:.76rem">
<input type="text" id="or-filter-q" placeholder="搜索标的/合约/策略" autocomplete="off" style="max-width:150px;font-size:.76rem">
<input type="datetime-local" id="or-filter-from" title="平仓起" style="font-size:.76rem">
<input type="datetime-local" id="or-filter-to" title="平仓止" style="font-size:.76rem">
<label class="muted" style="display:flex;align-items:center;gap:4px;font-size:.72rem">
@@ -261,4 +261,4 @@
</div>
</div>
<script src="/static/options_review.js?v=12"></script>
<script src="/static/options_review.js?v=13"></script>
+41
View File
@@ -337,6 +337,47 @@ class OptionsReviewTests(unittest.TestCase):
self.assertEqual(stats["by_strategy"][0]["key"], "假破")
self.assertEqual(stats["kpi"]["total"], 2)
def test_q_search_btcusdt_matches_btc_pending(self):
from lib.options.options_review_lib import count_review_trades
conn = _conn()
upsert_option_history_row(
conn,
{
"history_key": "ex:btc1",
"inst_id": "BTC-USD-260328-90000-C",
"underlying": "BTC",
"opt_type": "C",
"realized_pnl": 1.2,
"created_at": "2026-01-01 00:00:00",
"closed_at": "2026-01-01 02:00:00",
},
)
upsert_option_history_row(
conn,
{
"history_key": "ex:eth1",
"inst_id": "ETH-USD-260328-2000-C",
"underlying": "ETH",
"opt_type": "C",
"realized_pnl": 2.0,
"created_at": "2026-01-01 00:00:00",
"closed_at": "2026-01-01 03:00:00",
},
)
# 旧精确 strategy_tag 会把待复盘滤成空
self.assertEqual(
count_review_trades(conn, strategy_tag="BTCUSDT", reviewed="0"),
0,
)
self.assertEqual(
count_review_trades(conn, q="BTCUSDT", reviewed="0"),
1,
)
rows = list_review_trades(conn, q="BTCUSDT", reviewed="0")
self.assertEqual(len(rows), 1)
self.assertEqual(rows[0]["underlying"], "BTC")
if __name__ == "__main__":
unittest.main()