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:
@@ -98,12 +98,12 @@
|
|||||||
p.set("source_type", activeSource);
|
p.set("source_type", activeSource);
|
||||||
var uly = ($("or-filter-uly") || {}).value || "";
|
var uly = ($("or-filter-uly") || {}).value || "";
|
||||||
var opt = ($("or-filter-opt") || {}).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 from = ($("or-filter-from") || {}).value || "";
|
||||||
var to = ($("or-filter-to") || {}).value || "";
|
var to = ($("or-filter-to") || {}).value || "";
|
||||||
if (uly) p.set("underlying", uly);
|
if (uly) p.set("underlying", uly);
|
||||||
if (opt) p.set("opt_type", opt);
|
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 (from) p.set("closed_from", from.replace("T", " ") + ":00");
|
||||||
if (to) p.set("closed_to", to.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");
|
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);
|
var el = $(id);
|
||||||
if (el) {
|
if (el) {
|
||||||
el.addEventListener("change", function () {
|
el.addEventListener("change", function () {
|
||||||
|
|||||||
@@ -591,12 +591,29 @@ def enrich_trade_row(row: dict[str, Any], entry: dict[str, Any] | None = None) -
|
|||||||
return out
|
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(
|
def _review_trades_filters(
|
||||||
*,
|
*,
|
||||||
source_type: str | None = None,
|
source_type: str | None = None,
|
||||||
underlying: str | None = None,
|
underlying: str | None = None,
|
||||||
opt_type: str | None = None,
|
opt_type: str | None = None,
|
||||||
strategy_tag: str | None = None,
|
strategy_tag: str | None = None,
|
||||||
|
q: str | None = None,
|
||||||
reviewed: str | None = None,
|
reviewed: str | None = None,
|
||||||
include_hedge_legs: bool = False,
|
include_hedge_legs: bool = False,
|
||||||
closed_from: str | None = None,
|
closed_from: str | None = None,
|
||||||
@@ -635,9 +652,26 @@ def _review_trades_filters(
|
|||||||
if closed_to:
|
if closed_to:
|
||||||
wheres.append("COALESCE(t.closed_at,'')<=?")
|
wheres.append("COALESCE(t.closed_at,'')<=?")
|
||||||
args.append(closed_to)
|
args.append(closed_to)
|
||||||
if strategy_tag:
|
# 兼容旧参数:精确策略标签;前端已改用 q 模糊搜索
|
||||||
wheres.append("e.strategy_tag=?")
|
if strategy_tag and not q:
|
||||||
|
wheres.append("UPPER(COALESCE(e.strategy_tag,''))=UPPER(?)")
|
||||||
args.append(strategy_tag)
|
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":
|
if reviewed == "1" or reviewed == "yes":
|
||||||
wheres.append("e.id IS NOT NULL")
|
wheres.append("e.id IS NOT NULL")
|
||||||
elif reviewed == "0" or reviewed == "no":
|
elif reviewed == "0" or reviewed == "no":
|
||||||
@@ -653,6 +687,7 @@ def count_review_trades(
|
|||||||
underlying: str | None = None,
|
underlying: str | None = None,
|
||||||
opt_type: str | None = None,
|
opt_type: str | None = None,
|
||||||
strategy_tag: str | None = None,
|
strategy_tag: str | None = None,
|
||||||
|
q: str | None = None,
|
||||||
reviewed: str | None = None,
|
reviewed: str | None = None,
|
||||||
include_hedge_legs: bool = False,
|
include_hedge_legs: bool = False,
|
||||||
closed_from: str | None = None,
|
closed_from: str | None = None,
|
||||||
@@ -664,6 +699,7 @@ def count_review_trades(
|
|||||||
underlying=underlying,
|
underlying=underlying,
|
||||||
opt_type=opt_type,
|
opt_type=opt_type,
|
||||||
strategy_tag=strategy_tag,
|
strategy_tag=strategy_tag,
|
||||||
|
q=q,
|
||||||
reviewed=reviewed,
|
reviewed=reviewed,
|
||||||
include_hedge_legs=include_hedge_legs,
|
include_hedge_legs=include_hedge_legs,
|
||||||
closed_from=closed_from,
|
closed_from=closed_from,
|
||||||
@@ -688,6 +724,7 @@ def list_review_trades(
|
|||||||
underlying: str | None = None,
|
underlying: str | None = None,
|
||||||
opt_type: str | None = None,
|
opt_type: str | None = None,
|
||||||
strategy_tag: str | None = None,
|
strategy_tag: str | None = None,
|
||||||
|
q: str | None = None,
|
||||||
reviewed: str | None = None,
|
reviewed: str | None = None,
|
||||||
include_hedge_legs: bool = False,
|
include_hedge_legs: bool = False,
|
||||||
closed_from: str | None = None,
|
closed_from: str | None = None,
|
||||||
@@ -701,6 +738,7 @@ def list_review_trades(
|
|||||||
underlying=underlying,
|
underlying=underlying,
|
||||||
opt_type=opt_type,
|
opt_type=opt_type,
|
||||||
strategy_tag=strategy_tag,
|
strategy_tag=strategy_tag,
|
||||||
|
q=q,
|
||||||
reviewed=reviewed,
|
reviewed=reviewed,
|
||||||
include_hedge_legs=include_hedge_legs,
|
include_hedge_legs=include_hedge_legs,
|
||||||
closed_from=closed_from,
|
closed_from=closed_from,
|
||||||
|
|||||||
@@ -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,
|
underlying=(request.args.get("underlying") or "").strip() or None,
|
||||||
opt_type=(request.args.get("opt_type") 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,
|
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,
|
reviewed=(request.args.get("reviewed") or "").strip() or None,
|
||||||
include_hedge_legs=(request.args.get("include_hedge_legs") or "")
|
include_hedge_legs=(request.args.get("include_hedge_legs") or "")
|
||||||
.strip()
|
.strip()
|
||||||
|
|||||||
@@ -94,7 +94,7 @@
|
|||||||
<option value="C">Call</option>
|
<option value="C">Call</option>
|
||||||
<option value="P">Put</option>
|
<option value="P">Put</option>
|
||||||
</select>
|
</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-from" title="平仓起" style="font-size:.76rem">
|
||||||
<input type="datetime-local" id="or-filter-to" 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">
|
<label class="muted" style="display:flex;align-items:center;gap:4px;font-size:.72rem">
|
||||||
@@ -261,4 +261,4 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<script src="/static/options_review.js?v=12"></script>
|
<script src="/static/options_review.js?v=13"></script>
|
||||||
|
|||||||
@@ -337,6 +337,47 @@ class OptionsReviewTests(unittest.TestCase):
|
|||||||
self.assertEqual(stats["by_strategy"][0]["key"], "假破")
|
self.assertEqual(stats["by_strategy"][0]["key"], "假破")
|
||||||
self.assertEqual(stats["kpi"]["total"], 2)
|
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__":
|
if __name__ == "__main__":
|
||||||
unittest.main()
|
unittest.main()
|
||||||
|
|||||||
Reference in New Issue
Block a user