fix(hub): archive labels, symbol column, and time-close dropdown
Force exchange_key on archive sync; add contract column and tag select styles on inner-light-mind; serve time_close_ui.js on instances so order/key time-close duration can be selected. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -376,14 +376,16 @@
|
||||
<label style="display:flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="breakeven_enabled" value="1" checked> 启用移动保本(关闭则仅保留初始止损与交易所挂单)
|
||||
</label>
|
||||
<label id="order-time-close-wrap" class="order-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<span id="order-time-close-wrap" class="order-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<label style="display:inline-flex;align-items:center;gap:4px;margin:0;cursor:pointer">
|
||||
<input type="checkbox" name="time_close_enabled" value="1" id="order-time-close-cb"> 时间平仓
|
||||
<select name="time_close_hours" id="order-time-close-hours" disabled>
|
||||
</label>
|
||||
<select name="time_close_hours" id="order-time-close-hours" title="持仓满该时长后自动平仓">
|
||||
<option value="1">1h</option>
|
||||
<option value="2">2h</option>
|
||||
<option value="4" selected>4h</option>
|
||||
</select>
|
||||
</label>
|
||||
</span>
|
||||
<label style="display:flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="order_chart" value="true"> 开仓后生成多周期K线图(各周期100根,含开平仓标记)
|
||||
</label>
|
||||
@@ -799,7 +801,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/static/instance_ui.js?v=1"></script>
|
||||
<script src="/static/time_close_ui.js?v=1"></script>
|
||||
<script src="/static/time_close_ui.js?v=2"></script>
|
||||
<script src="/static/ai_review_render.js?v=2"></script>
|
||||
<script src="/static/form_submit_guard.js?v=2"></script>
|
||||
<script>
|
||||
|
||||
@@ -356,14 +356,16 @@
|
||||
<label style="display:flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="breakeven_enabled" value="1" checked> 启用移动保本(关闭则仅保留初始止损与交易所挂单)
|
||||
</label>
|
||||
<label id="order-time-close-wrap" class="order-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<span id="order-time-close-wrap" class="order-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<label style="display:inline-flex;align-items:center;gap:4px;margin:0;cursor:pointer">
|
||||
<input type="checkbox" name="time_close_enabled" value="1" id="order-time-close-cb"> 时间平仓
|
||||
<select name="time_close_hours" id="order-time-close-hours" disabled>
|
||||
</label>
|
||||
<select name="time_close_hours" id="order-time-close-hours" title="持仓满该时长后自动平仓">
|
||||
<option value="1">1h</option>
|
||||
<option value="2">2h</option>
|
||||
<option value="4" selected>4h</option>
|
||||
</select>
|
||||
</label>
|
||||
</span>
|
||||
<label style="display:flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="order_chart" value="true"> 开仓后生成多周期K线图(各周期100根,含开平仓标记)
|
||||
</label>
|
||||
@@ -779,7 +781,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/static/instance_ui.js?v=1"></script>
|
||||
<script src="/static/time_close_ui.js?v=1"></script>
|
||||
<script src="/static/time_close_ui.js?v=2"></script>
|
||||
<script src="/static/ai_review_render.js?v=2"></script>
|
||||
<script src="/static/form_submit_guard.js?v=2"></script>
|
||||
<script>
|
||||
|
||||
@@ -356,14 +356,16 @@
|
||||
<label style="display:flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="breakeven_enabled" value="1" checked> 启用移动保本(关闭则仅保留初始止损与交易所挂单)
|
||||
</label>
|
||||
<label id="order-time-close-wrap" class="order-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<span id="order-time-close-wrap" class="order-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<label style="display:inline-flex;align-items:center;gap:4px;margin:0;cursor:pointer">
|
||||
<input type="checkbox" name="time_close_enabled" value="1" id="order-time-close-cb"> 时间平仓
|
||||
<select name="time_close_hours" id="order-time-close-hours" disabled>
|
||||
</label>
|
||||
<select name="time_close_hours" id="order-time-close-hours" title="持仓满该时长后自动平仓">
|
||||
<option value="1">1h</option>
|
||||
<option value="2">2h</option>
|
||||
<option value="4" selected>4h</option>
|
||||
</select>
|
||||
</label>
|
||||
</span>
|
||||
<label style="display:flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="order_chart" value="true"> 开仓后生成多周期K线图(各周期100根,含开平仓标记)
|
||||
</label>
|
||||
@@ -779,7 +781,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/static/instance_ui.js?v=1"></script>
|
||||
<script src="/static/time_close_ui.js?v=1"></script>
|
||||
<script src="/static/time_close_ui.js?v=2"></script>
|
||||
<script src="/static/ai_review_render.js?v=2"></script>
|
||||
<script src="/static/form_submit_guard.js?v=2"></script>
|
||||
<script>
|
||||
|
||||
@@ -385,14 +385,16 @@
|
||||
<label style="display:flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="breakeven_enabled" value="1" checked> 启用移动保本(关闭则仅保留初始止损与交易所挂单)
|
||||
</label>
|
||||
<label id="order-time-close-wrap" class="order-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<span id="order-time-close-wrap" class="order-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<label style="display:inline-flex;align-items:center;gap:4px;margin:0;cursor:pointer">
|
||||
<input type="checkbox" name="time_close_enabled" value="1" id="order-time-close-cb"> 时间平仓
|
||||
<select name="time_close_hours" id="order-time-close-hours" disabled>
|
||||
</label>
|
||||
<select name="time_close_hours" id="order-time-close-hours" title="持仓满该时长后自动平仓">
|
||||
<option value="1">1h</option>
|
||||
<option value="2">2h</option>
|
||||
<option value="4" selected>4h</option>
|
||||
</select>
|
||||
</label>
|
||||
</span>
|
||||
<label style="display:flex;align-items:center;gap:4px;font-size:.82rem;color:#cfd3ef">
|
||||
<input type="checkbox" name="order_chart" value="true"> 开仓后生成多周期K线图(各周期100根,含开平仓标记)
|
||||
</label>
|
||||
@@ -808,7 +810,7 @@
|
||||
</div>
|
||||
|
||||
<script src="/static/instance_ui.js?v=1"></script>
|
||||
<script src="/static/time_close_ui.js?v=1"></script>
|
||||
<script src="/static/time_close_ui.js?v=2"></script>
|
||||
<script src="/static/ai_review_render.js?v=2"></script>
|
||||
<script src="/static/form_submit_guard.js?v=2"></script>
|
||||
<script>
|
||||
|
||||
@@ -55,6 +55,7 @@ def install_instance_theme_static(app) -> None:
|
||||
"instance_ui.js": "application/javascript; charset=utf-8",
|
||||
"ai_review_render.js": "application/javascript; charset=utf-8",
|
||||
"form_submit_guard.js": "application/javascript; charset=utf-8",
|
||||
"time_close_ui.js": "application/javascript; charset=utf-8",
|
||||
"focus_chart_page.js": "application/javascript; charset=utf-8",
|
||||
"focus_chart_page.css": "text/css; charset=utf-8",
|
||||
}
|
||||
@@ -450,6 +451,7 @@ def register_hub_routes(app):
|
||||
try:
|
||||
trades = fetch_trades_for_archive(
|
||||
conn,
|
||||
exchange_key=str(c.get("exchange") or ""),
|
||||
days=days,
|
||||
row_to_dict_fn=c.get("row_to_dict"),
|
||||
reset_hour=reset_hour,
|
||||
|
||||
@@ -320,7 +320,10 @@ def upsert_trades_cache(
|
||||
if not sym:
|
||||
continue
|
||||
active_ids.append(tid)
|
||||
payload = {k: t.get(k) for k in t.keys()}
|
||||
row = dict(t)
|
||||
row["exchange_key"] = ex_k
|
||||
row.pop("account_exchange_key", None)
|
||||
payload = {k: row.get(k) for k in row.keys()}
|
||||
entry_label = _trade_entry_reason_for_cache(t)
|
||||
conn.execute(
|
||||
"""
|
||||
@@ -441,6 +444,10 @@ def _trade_row_to_dict(row: sqlite3.Row, overlay: dict | None = None) -> dict[st
|
||||
out["behavior_tag"] = ov.get("behavior_tag") or ""
|
||||
out["note"] = ov.get("note") or ""
|
||||
out["trade_id"] = out.get("trade_id") or out.get("id")
|
||||
ex_col = str(d.get("exchange_key") or "").strip().lower()
|
||||
if ex_col:
|
||||
out["exchange_key"] = ex_col
|
||||
out.pop("account_exchange_key", None)
|
||||
return _enrich_trade_display_fields(out)
|
||||
|
||||
|
||||
|
||||
+11
-2
@@ -422,6 +422,7 @@ def _existing_trend_plan_ids(conn) -> set[int]:
|
||||
def _normalize_snapshot_archive_row(
|
||||
snap: dict,
|
||||
*,
|
||||
exchange_key: str = "",
|
||||
reset_hour: int = 8,
|
||||
) -> dict[str, Any] | None:
|
||||
result = str(snap.get("result_label") or "").strip()
|
||||
@@ -458,6 +459,7 @@ def _normalize_snapshot_archive_row(
|
||||
entry_type = entry_reason_for_monitor_type(monitor_type) or monitor_type
|
||||
return {
|
||||
"id": -snap_id,
|
||||
"exchange_key": (exchange_key or "").strip().lower(),
|
||||
"symbol": (snap.get("symbol") or "").strip().upper(),
|
||||
"direction": snap.get("direction"),
|
||||
"result": result,
|
||||
@@ -495,6 +497,7 @@ def _parse_ms_from_row(raw: Any) -> int | None:
|
||||
def _fetch_strategy_snapshots_for_archive(
|
||||
conn,
|
||||
*,
|
||||
exchange_key: str = "",
|
||||
days: int = 365,
|
||||
reset_hour: int = 8,
|
||||
limit: int = 2000,
|
||||
@@ -527,7 +530,9 @@ def _fetch_strategy_snapshots_for_archive(
|
||||
source_id = 0
|
||||
if source_id > 0 and source_id in skip:
|
||||
continue
|
||||
norm = _normalize_snapshot_archive_row(d, reset_hour=reset_hour)
|
||||
norm = _normalize_snapshot_archive_row(
|
||||
d, exchange_key=exchange_key, reset_hour=reset_hour
|
||||
)
|
||||
if norm:
|
||||
out.append(norm)
|
||||
if len(out) >= lim:
|
||||
@@ -538,6 +543,7 @@ def _fetch_strategy_snapshots_for_archive(
|
||||
def fetch_trades_for_archive(
|
||||
conn,
|
||||
*,
|
||||
exchange_key: str = "",
|
||||
days: int = 365,
|
||||
row_to_dict_fn: Optional[Callable] = None,
|
||||
reset_hour: int = 8,
|
||||
@@ -565,7 +571,9 @@ def fetch_trades_for_archive(
|
||||
records = []
|
||||
for row in rows:
|
||||
d = _row_dict(row, row_to_dict_fn)
|
||||
norm = _normalize_archive_trade_row(d, reset_hour=reset_hour)
|
||||
norm = _normalize_archive_trade_row(
|
||||
d, exchange_key=exchange_key, reset_hour=reset_hour
|
||||
)
|
||||
if norm:
|
||||
records.append(norm)
|
||||
if len(records) >= lim:
|
||||
@@ -586,6 +594,7 @@ def fetch_trades_for_archive(
|
||||
snaps = _fetch_strategy_snapshots_for_archive(
|
||||
conn,
|
||||
days=days,
|
||||
exchange_key=exchange_key,
|
||||
reset_hour=reset_hour,
|
||||
limit=max(0, lim - len(records)),
|
||||
skip_plan_ids=skip_ids,
|
||||
|
||||
@@ -325,7 +325,7 @@ async def _run_archive_sync_once() -> dict:
|
||||
trades = trades_resp.get("trades") or []
|
||||
for t in trades:
|
||||
if isinstance(t, dict):
|
||||
t.setdefault("exchange_key", ex_key)
|
||||
t["exchange_key"] = ex_key
|
||||
|
||||
def remote_fetch(**kwargs):
|
||||
return _fetch_instance_ohlcv_sync(
|
||||
|
||||
@@ -5757,7 +5757,7 @@ body.funds-fullscreen-open {
|
||||
}
|
||||
.archive-trades-table {
|
||||
width: 100%;
|
||||
min-width: 920px;
|
||||
min-width: 1000px;
|
||||
border-collapse: collapse;
|
||||
font-size: 0.78rem;
|
||||
}
|
||||
@@ -5768,6 +5768,10 @@ body.funds-fullscreen-open {
|
||||
.archive-trades-table .archive-hold {
|
||||
white-space: nowrap;
|
||||
}
|
||||
.archive-trades-table .archive-symbol {
|
||||
white-space: nowrap;
|
||||
font-weight: 500;
|
||||
}
|
||||
.archive-review-mark {
|
||||
display: inline-block;
|
||||
margin-right: 4px;
|
||||
@@ -5854,6 +5858,21 @@ body.funds-fullscreen-open {
|
||||
color: var(--text);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.archive-tag-select.is-tag-sick {
|
||||
color: var(--red);
|
||||
border-color: color-mix(in srgb, var(--red) 45%, var(--border-soft));
|
||||
background: color-mix(in srgb, var(--red) 14%, var(--inset-surface));
|
||||
}
|
||||
.archive-tag-select.is-tag-emotion {
|
||||
color: #60a5fa;
|
||||
border-color: color-mix(in srgb, #60a5fa 45%, var(--border-soft));
|
||||
background: color-mix(in srgb, #60a5fa 14%, var(--inset-surface));
|
||||
}
|
||||
.archive-trade-row.archive-trade-sick .archive-tag-select.is-tag-sick {
|
||||
color: var(--red);
|
||||
border-color: color-mix(in srgb, var(--red) 50%, var(--border-soft));
|
||||
background: color-mix(in srgb, var(--red) 18%, var(--inset-surface));
|
||||
}
|
||||
.archive-empty {
|
||||
padding: 16px;
|
||||
color: var(--muted);
|
||||
|
||||
@@ -223,10 +223,17 @@
|
||||
|
||||
function tradeRowExchange(tr) {
|
||||
if (!tr) return "—";
|
||||
const exKey = tr.exchange_key || tr.account_exchange_key || "";
|
||||
if (exKey) return exchangeLabel(exKey);
|
||||
const name = tr.account_name || tr.exchange_name || "";
|
||||
return name || "—";
|
||||
const exKey = String(tr.exchange_key || "").toLowerCase();
|
||||
return exKey ? exchangeLabel(exKey) : "—";
|
||||
}
|
||||
|
||||
function applyTagSelectStyle(sel) {
|
||||
if (!sel) return;
|
||||
const v = sel.value || "";
|
||||
sel.classList.remove("is-tag-empty", "is-tag-sick", "is-tag-emotion");
|
||||
if (v === "sick") sel.classList.add("is-tag-sick");
|
||||
else if (v === "emotion") sel.classList.add("is-tag-emotion");
|
||||
else sel.classList.add("is-tag-empty");
|
||||
}
|
||||
|
||||
function exchangeLabel(exKey) {
|
||||
@@ -898,7 +905,7 @@
|
||||
|
||||
async function openTradeChart(tr) {
|
||||
if (!tr) return;
|
||||
const exKey = tr.exchange_key || tr.account_exchange_key || "";
|
||||
const exKey = String(tr.exchange_key || "").toLowerCase();
|
||||
const sym = tr.symbol || "";
|
||||
if (!exKey || !sym) {
|
||||
setStatus("该笔交易缺少交易所或合约,无法加载图表");
|
||||
@@ -920,13 +927,13 @@
|
||||
}
|
||||
elTrades.innerHTML =
|
||||
'<table class="archive-trades-table"><thead><tr>' +
|
||||
"<th>交易所</th><th>开仓类型</th><th>开仓时间</th><th>平仓时间</th><th>持仓时长</th>" +
|
||||
"<th>交易所</th><th>合约</th><th>开仓类型</th><th>开仓时间</th><th>平仓时间</th><th>持仓时长</th>" +
|
||||
"<th>方向</th><th>结果</th><th>盈亏</th><th>标签</th><th>备注</th><th>操作</th>" +
|
||||
"</tr></thead><tbody>" +
|
||||
dailyTrades
|
||||
.map(function (t) {
|
||||
const tid = t.trade_id || t.id;
|
||||
const exKey = t.exchange_key || t.account_exchange_key || "";
|
||||
const exKey = String(t.exchange_key || "").toLowerCase();
|
||||
const tag = t.behavior_tag || "";
|
||||
const sick = tag === "sick";
|
||||
const active = String(tid) === String(selectedTradeId) ? " is-active" : "";
|
||||
@@ -945,6 +952,9 @@
|
||||
"<td>" +
|
||||
esc(tradeRowExchange(t)) +
|
||||
"</td>" +
|
||||
'<td class="archive-symbol">' +
|
||||
esc(t.symbol || "—") +
|
||||
"</td>" +
|
||||
"<td>" +
|
||||
(rev ? '<span class="archive-review-mark">' + rev + "</span>" : "") +
|
||||
esc(fmtEntryType(t)) +
|
||||
@@ -1027,7 +1037,9 @@
|
||||
});
|
||||
});
|
||||
elTrades.querySelectorAll(".archive-tag-select").forEach(function (sel) {
|
||||
applyTagSelectStyle(sel);
|
||||
sel.addEventListener("change", function () {
|
||||
applyTagSelectStyle(sel);
|
||||
saveOverlay(sel.getAttribute("data-id"), sel.getAttribute("data-ex"), sel.value, null);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
||||
<link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" media="print" onload="this.media='all'" />
|
||||
<noscript><link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;500;600&family=Orbitron:wght@500;600;700&display=swap" rel="stylesheet" /></noscript>
|
||||
<link rel="stylesheet" href="/assets/app.css?v=20260612-sick-row-red-text" />
|
||||
<link rel="stylesheet" href="/assets/app.css?v=20260612-trade-symbol-col" />
|
||||
<link rel="stylesheet" href="/assets/dashboard.css?v=20260612-dash-monitor-count" />
|
||||
</head>
|
||||
<body>
|
||||
@@ -584,11 +584,11 @@
|
||||
<script src="https://unpkg.com/lightweight-charts@4.2.0/dist/lightweight-charts.standalone.production.js"></script>
|
||||
<script src="/assets/chart_draw.js?v=20260609-market-day-split"></script>
|
||||
<script src="/assets/chart.js?v=20260609-market-day-split"></script>
|
||||
<script src="/assets/archive.js?v=20260612-stats-table-layout"></script>
|
||||
<script src="/assets/archive.js?v=20260612-trade-symbol-col"></script>
|
||||
<script src="/assets/funds.js?v=20260609-hub-funds-fold"></script>
|
||||
<script src="/assets/dashboard.js?v=20260612-dash-monitor-count"></script>
|
||||
<script src="/assets/ai_review_render.js?v=3"></script>
|
||||
<script src="/assets/time_close_ui.js?v=1"></script>
|
||||
<script src="/assets/time_close_ui.js?v=2"></script>
|
||||
<script src="/assets/app.js?v=20260612-time-close"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@@ -23,9 +23,16 @@
|
||||
if (!cb || !sel) return;
|
||||
function sync() {
|
||||
const on = !!cb.checked;
|
||||
sel.disabled = !on;
|
||||
sel.disabled = false;
|
||||
sel.tabIndex = 0;
|
||||
if (wrap) wrap.classList.toggle("is-disabled", !on);
|
||||
}
|
||||
sel.addEventListener("mousedown", function (ev) {
|
||||
ev.stopPropagation();
|
||||
});
|
||||
sel.addEventListener("click", function (ev) {
|
||||
ev.stopPropagation();
|
||||
});
|
||||
cb.addEventListener("change", sync);
|
||||
sync();
|
||||
}
|
||||
|
||||
@@ -348,9 +348,13 @@ html[data-theme="light"] .pos-meta-item::after {
|
||||
font-variant-numeric: tabular-nums;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
.key-time-close-wrap.is-disabled select,
|
||||
.order-time-close-wrap.is-disabled select {
|
||||
opacity: 0.55;
|
||||
.key-time-close-wrap.is-disabled > label,
|
||||
.order-time-close-wrap.is-disabled > label {
|
||||
opacity: 0.72;
|
||||
}
|
||||
.key-time-close-wrap select,
|
||||
.order-time-close-wrap select {
|
||||
cursor: pointer;
|
||||
}
|
||||
html[data-theme="light"] .pos-meta-on {
|
||||
color: #006e9a !important;
|
||||
|
||||
@@ -23,9 +23,16 @@
|
||||
if (!cb || !sel) return;
|
||||
function sync() {
|
||||
const on = !!cb.checked;
|
||||
sel.disabled = !on;
|
||||
sel.disabled = false;
|
||||
sel.tabIndex = 0;
|
||||
if (wrap) wrap.classList.toggle("is-disabled", !on);
|
||||
}
|
||||
sel.addEventListener("mousedown", function (ev) {
|
||||
ev.stopPropagation();
|
||||
});
|
||||
sel.addEventListener("click", function (ev) {
|
||||
ev.stopPropagation();
|
||||
});
|
||||
cb.addEventListener("change", sync);
|
||||
sync();
|
||||
}
|
||||
|
||||
@@ -157,14 +157,16 @@
|
||||
<label id="key-breakeven-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.85rem;color:#9aa">
|
||||
<input type="checkbox" name="breakeven_enabled" value="1" id="key-breakeven-cb"> 移动保本
|
||||
</label>
|
||||
<label id="key-time-close-wrap" class="key-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.85rem;color:#9aa">
|
||||
<span id="key-time-close-wrap" class="key-time-close-wrap" style="display:inline-flex;align-items:center;gap:4px;font-size:.85rem;color:#9aa">
|
||||
<label style="display:inline-flex;align-items:center;gap:4px;margin:0;cursor:pointer">
|
||||
<input type="checkbox" name="time_close_enabled" value="1" id="key-time-close-cb"> 时间平仓
|
||||
<select name="time_close_hours" id="key-time-close-hours" disabled>
|
||||
</label>
|
||||
<select name="time_close_hours" id="key-time-close-hours" title="持仓满该时长后自动平仓">
|
||||
<option value="1">1h</option>
|
||||
<option value="2">2h</option>
|
||||
<option value="4" selected>4h</option>
|
||||
</select>
|
||||
</label>
|
||||
</span>
|
||||
<button type="submit">添加</button>
|
||||
</form>
|
||||
<details class="tip-collapse key-rule-collapse">
|
||||
|
||||
@@ -245,3 +245,29 @@ def test_resolve_archive_chart_history_uses_trade_span_not_200_bars():
|
||||
assert out["ok"] is True
|
||||
assert out["range_mode"] == "history"
|
||||
assert out["bar_count"] > 200
|
||||
|
||||
|
||||
def test_upsert_forces_sync_exchange_key():
|
||||
with tempfile.TemporaryDirectory() as td:
|
||||
db = Path(td) / "archive.db"
|
||||
init_db(db)
|
||||
upsert_trades_cache(
|
||||
"gate_bot",
|
||||
[
|
||||
{
|
||||
"id": 77,
|
||||
"exchange_key": "gate",
|
||||
"account_exchange_key": "gate",
|
||||
"symbol": "ETH/USDT",
|
||||
"result": "止损",
|
||||
"pnl_amount": -1,
|
||||
"opened_at_ms": 1_700_000_000_000,
|
||||
"closed_at_ms": 1_700_007_200_000,
|
||||
}
|
||||
],
|
||||
db_path=db,
|
||||
)
|
||||
rows = load_symbol_trades("gate_bot", "ETH/USDT", db_path=db)
|
||||
assert len(rows) == 1
|
||||
assert rows[0]["exchange_key"] == "gate_bot"
|
||||
assert "account_exchange_key" not in rows[0]
|
||||
|
||||
Reference in New Issue
Block a user