Show all UI times in GMT+7 and unify the timeframe list across the app

Every displayed timestamp previously relied on either the viewer's
browser-local timezone (toLocaleString/toLocaleDateString/toLocaleTimeString)
or raw UTC ISO-string slicing (.slice(0,10)/.slice(5,16)) — both wrong for
a Vietnam-based system, and the string-slicing approach could show the
wrong calendar date entirely near the UTC/GMT+7 day boundary. Added
frontend/src/utils/dateTime.ts with formatVN* helpers that explicitly
render in Asia/Ho_Chi_Minh regardless of the viewer's machine, and applied
them across AdminPage, OrderPanel's live clock, SignalPanel, ProfilePage,
AuditLogPage, and BacktestPage (including the new walk-forward fold/history
dates) — 10 display sites total.

Also consolidated the timeframe list (15m/30m/1h/4h/1d/1w/1M), which had
drifted into 4 different copies across BacktestPage, ProfilePage,
ChartToolbar, and AlertsPage, into a single frontend/src/utils/timeframes.ts
source of truth. Extended Walk-Forward's timeframe options from 1h/4h to
1h/4h/1d, and fixed a latent backend bug where backtest_engine.py's
tf_minutes map was missing "1d", silently defaulting to 30 minutes for
any daily-timeframe backtest.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Le
2026-07-04 09:31:48 +07:00
parent 625c2b3773
commit 56325113b3
13 changed files with 113 additions and 39 deletions
+1 -1
View File
@@ -70,7 +70,7 @@ def _precompute_indicators(candles: list[Candle], timeframe: str) -> dict:
(see `_simulate_trades`).
"""
# MTF config
tf_minutes = {"15m": 15, "30m": 30, "1h": 60, "4h": 240}
tf_minutes = {"15m": 15, "30m": 30, "1h": 60, "4h": 240, "1d": 1440, "1w": 10080, "1M": 43200}
main_minutes = tf_minutes.get(timeframe, 30)
mtf_config = []
for mtf_tf, mtf_minutes, mtf_w in [("15m", 15, 0.5), ("1h", 60, 1.5), ("4h", 240, 2.0)]:
+1 -1
View File
@@ -45,7 +45,7 @@ DEFAULT_PARAM_GRID: dict[str, list[float]] = {
MIN_TRADES_PER_FOLD = 5 # reject param combos too sparse to trust
WARMUP_BUFFER_CANDLES = 60 # extra history fetched before each window so indicators aren't cold at window start
_TF_MINUTES = {"15m": 15, "30m": 30, "1h": 60, "4h": 240, "1d": 1440}
_TF_MINUTES = {"15m": 15, "30m": 30, "1h": 60, "4h": 240, "1d": 1440, "1w": 10080, "1M": 43200}
def _fold_score(closed_trades: list[dict], min_trades: int = MIN_TRADES_PER_FOLD) -> float: