Spaces:
Sleeping
Sleeping
Yan Wang
commited on
Commit
·
4115887
1
Parent(s):
36b100b
remove normalization and raw prices button
Browse files- src/App_New.tsx +13 -43
src/App_New.tsx
CHANGED
|
@@ -12,7 +12,7 @@ import {
|
|
| 12 |
} from "./api";
|
| 13 |
|
| 14 |
type SeriesPoint = { date: string; close: number[] };
|
| 15 |
-
type ViewMode = "daily" | "intraday";
|
| 16 |
|
| 17 |
const START_DAY = 7;
|
| 18 |
|
|
@@ -59,6 +59,7 @@ function generateFakeJSON(maxLen = 500): DataDict {
|
|
| 59 |
const sigma = 0.15 + Math.random() * 0.35;
|
| 60 |
const series: SeriesPoint[] = [];
|
| 61 |
for (let i = 0; i < dates.length; i++) {
|
|
|
|
| 62 |
const mins = 200 + Math.floor(Math.random() * 100);
|
| 63 |
const intraday: number[] = [];
|
| 64 |
let p = price;
|
|
@@ -103,9 +104,6 @@ export default function App() {
|
|
| 103 |
const [viewMode, setViewMode] = useState<ViewMode>("daily");
|
| 104 |
const [intradayDayIdx, setIntradayDayIdx] = useState<number | null>(null);
|
| 105 |
|
| 106 |
-
// NEW: global normalize toggle
|
| 107 |
-
const [normalize, setNormalize] = useState<boolean>(true);
|
| 108 |
-
|
| 109 |
/* ---------- localStorage keys ---------- */
|
| 110 |
const LS_DATASET_KEY = "annot_dataset_meta";
|
| 111 |
const LS_DATA_PREFIX = (id: string) => `annot_dataset_${id}`;
|
|
@@ -276,7 +274,7 @@ export default function App() {
|
|
| 276 |
/* ---------- computed ---------- */
|
| 277 |
const isFinal = windowLen >= maxDays || selections.length >= maxSteps;
|
| 278 |
|
| 279 |
-
// Daily
|
| 280 |
const windowData = useMemo(() => {
|
| 281 |
if (!dates.length || windowLen < 2) return [] as any[];
|
| 282 |
const sliceDates = dates.slice(0, windowLen);
|
|
@@ -284,18 +282,14 @@ export default function App() {
|
|
| 284 |
const row: Record<string, any> = { date };
|
| 285 |
assets.forEach((a) => {
|
| 286 |
const base = rawData[a]?.[0] ? latestClose(rawData[a][0]) : 1;
|
| 287 |
-
const val = rawData[a]?.[idx] ? latestClose(rawData[a][idx]) :
|
| 288 |
-
|
| 289 |
-
row[a] = base ? (val as number) / base : 1; // normalize to day 0
|
| 290 |
-
} else {
|
| 291 |
-
row[a] = typeof val === "number" ? val : null; // raw price
|
| 292 |
-
}
|
| 293 |
});
|
| 294 |
return row;
|
| 295 |
});
|
| 296 |
-
}, [assets, dates, windowLen, rawData
|
| 297 |
|
| 298 |
-
// Intraday (single day)
|
| 299 |
const visibleDates = useMemo(() => dates.slice(0, Math.max(0, windowLen)), [dates, windowLen]);
|
| 300 |
const activeIntradayIdx = intradayDayIdx ?? Math.min(windowLen - 1, dates.length - 1);
|
| 301 |
const intradayData = useMemo(() => {
|
|
@@ -312,16 +306,12 @@ export default function App() {
|
|
| 312 |
assets.forEach(a => {
|
| 313 |
const arr = rawData[a]?.[activeIntradayIdx]?.close ?? [];
|
| 314 |
const val = arr[i];
|
| 315 |
-
|
| 316 |
-
row[a] = typeof val === "number" ? (dayFirstPrice[a] ? val / dayFirstPrice[a] : null) : null; // normalized to first tick
|
| 317 |
-
} else {
|
| 318 |
-
row[a] = typeof val === "number" ? val : null; // raw price
|
| 319 |
-
}
|
| 320 |
});
|
| 321 |
rows.push(row);
|
| 322 |
}
|
| 323 |
return rows;
|
| 324 |
-
}, [assets, rawData, dates.length, activeIntradayIdx
|
| 325 |
|
| 326 |
function realizedNextDayReturn(asset: string) {
|
| 327 |
const t = windowLen - 1;
|
|
@@ -450,7 +440,7 @@ export default function App() {
|
|
| 450 |
useEffect(() => {
|
| 451 |
function onKey(e: KeyboardEvent) {
|
| 452 |
const tag = (e.target && (e.target as HTMLElement).tagName) || "";
|
| 453 |
-
if (tag === "INPUT" || "TEXTAREA") return;
|
| 454 |
const idx = parseInt((e as any).key, 10) - 1;
|
| 455 |
if (!Number.isNaN(idx) && idx >= 0 && idx < assets.length) setSelectedAsset(assets[idx]);
|
| 456 |
if ((e as any).key === "Enter" && Boolean(selectedAsset) && windowLen < maxDays) confirmSelection();
|
|
@@ -485,20 +475,12 @@ export default function App() {
|
|
| 485 |
</div>
|
| 486 |
);
|
| 487 |
|
| 488 |
-
// Tooltip 格式化:normalized 用“×”,raw 用价格两位小数
|
| 489 |
-
function tooltipFormatter(value: any, name: any) {
|
| 490 |
-
if (normalize) {
|
| 491 |
-
return [Number.isFinite(value) ? `${Number(value).toFixed(4)}×` : value, aliasMap[name] || name];
|
| 492 |
-
}
|
| 493 |
-
return [Number.isFinite(value) ? Number(value).toFixed(2) : value, aliasMap[name] || name];
|
| 494 |
-
}
|
| 495 |
-
|
| 496 |
return (
|
| 497 |
<div className="p-6 bg-gray-50 min-h-screen flex flex-col gap-6">
|
| 498 |
<div className="flex flex-wrap justify-between items-center gap-3">
|
| 499 |
<div className="flex items-center gap-2">
|
| 500 |
<h1 className="text-xl font-semibold">Asset Choice Simulation</h1>
|
| 501 |
-
<span className="text-[10px] ml-2 px-1.5 py-0.5 rounded bg-amber-200 text-amber-900">BUILD_TAG: 2025-10-
|
| 502 |
<span className="text-xs text-gray-500">Dataset: {datasetName || datasetId}</span>
|
| 503 |
<span className="text-xs text-gray-500">User: {userId.slice(0,8)}…</span>
|
| 504 |
<span className="text-xs text-gray-500">Day {windowLen} / {maxDays}</span>
|
|
@@ -513,15 +495,6 @@ export default function App() {
|
|
| 513 |
<button className={`text-xs px-3 py-1.5 ${viewMode==="intraday" ? "bg-gray-900 text-white" : "bg-white text-gray-700 hover:bg-gray-50"}`} onClick={()=>setViewMode("intraday")}>Intraday (1 day)</button>
|
| 514 |
</div>
|
| 515 |
|
| 516 |
-
{/* NEW: Normalize/Raw toggle */}
|
| 517 |
-
<button
|
| 518 |
-
className="text-xs px-3 py-1.5 rounded-xl border border-gray-200 hover:bg-gray-50"
|
| 519 |
-
onClick={() => setNormalize(v => !v)}
|
| 520 |
-
title="Toggle normalized ratio vs raw price"
|
| 521 |
-
>
|
| 522 |
-
{normalize ? "Normalized" : "Raw Price"}
|
| 523 |
-
</button>
|
| 524 |
-
|
| 525 |
<button onClick={exportLog} className="text-sm px-3 py-1.5 rounded-xl bg-gray-900 text-white hover:bg-black">Export My Log</button>
|
| 526 |
</div>
|
| 527 |
</div>
|
|
@@ -540,9 +513,6 @@ export default function App() {
|
|
| 540 |
</button>
|
| 541 |
))}
|
| 542 |
{assets.length>0 && <span className="text-xs text-gray-500 ml-1">Hotkeys: 1..{assets.length}, Enter confirm</span>}
|
| 543 |
-
<span className="text-[10px] px-2 py-0.5 rounded bg-black/60 text-white">
|
| 544 |
-
{normalize ? "Viewing: Normalized (×)" : "Viewing: Raw Price"}
|
| 545 |
-
</span>
|
| 546 |
</div>
|
| 547 |
|
| 548 |
<div className="h-80 relative">
|
|
@@ -552,7 +522,7 @@ export default function App() {
|
|
| 552 |
<CartesianGrid strokeDasharray="3 3" />
|
| 553 |
<XAxis dataKey="idx" tick={{ fontSize: 10 }} />
|
| 554 |
<YAxis domain={["auto", "auto"]} tick={{ fontSize: 10 }} />
|
| 555 |
-
<Tooltip contentStyle={{ fontSize: 12 }} formatter={
|
| 556 |
<Legend onClick={(o: any) => setSelectedAsset(o.value)} wrapperStyle={{ cursor: "pointer" }} formatter={(v: any) => aliasMap[v] || v} />
|
| 557 |
{assets.map((a, i) => (
|
| 558 |
<Line
|
|
@@ -577,7 +547,7 @@ export default function App() {
|
|
| 577 |
<CartesianGrid strokeDasharray="3 3" />
|
| 578 |
<XAxis dataKey="date" tick={{ fontSize: 10 }} />
|
| 579 |
<YAxis domain={["auto", "auto"]} tick={{ fontSize: 10 }} />
|
| 580 |
-
<Tooltip contentStyle={{ fontSize: 12 }} formatter={
|
| 581 |
<Legend onClick={(o: any) => setSelectedAsset(o.value)} wrapperStyle={{ cursor: "pointer" }} formatter={(v: any) => aliasMap[v] || v} />
|
| 582 |
{assets.map((a, i) => (
|
| 583 |
<Line
|
|
|
|
| 12 |
} from "./api";
|
| 13 |
|
| 14 |
type SeriesPoint = { date: string; close: number[] };
|
| 15 |
+
type ViewMode = "daily" | "intraday"; // 移除 multi
|
| 16 |
|
| 17 |
const START_DAY = 7;
|
| 18 |
|
|
|
|
| 59 |
const sigma = 0.15 + Math.random() * 0.35;
|
| 60 |
const series: SeriesPoint[] = [];
|
| 61 |
for (let i = 0; i < dates.length; i++) {
|
| 62 |
+
// Fake minute series (varying length)
|
| 63 |
const mins = 200 + Math.floor(Math.random() * 100);
|
| 64 |
const intraday: number[] = [];
|
| 65 |
let p = price;
|
|
|
|
| 104 |
const [viewMode, setViewMode] = useState<ViewMode>("daily");
|
| 105 |
const [intradayDayIdx, setIntradayDayIdx] = useState<number | null>(null);
|
| 106 |
|
|
|
|
|
|
|
|
|
|
| 107 |
/* ---------- localStorage keys ---------- */
|
| 108 |
const LS_DATASET_KEY = "annot_dataset_meta";
|
| 109 |
const LS_DATA_PREFIX = (id: string) => `annot_dataset_${id}`;
|
|
|
|
| 274 |
/* ---------- computed ---------- */
|
| 275 |
const isFinal = windowLen >= maxDays || selections.length >= maxSteps;
|
| 276 |
|
| 277 |
+
// Daily (normalized to day 0)
|
| 278 |
const windowData = useMemo(() => {
|
| 279 |
if (!dates.length || windowLen < 2) return [] as any[];
|
| 280 |
const sliceDates = dates.slice(0, windowLen);
|
|
|
|
| 282 |
const row: Record<string, any> = { date };
|
| 283 |
assets.forEach((a) => {
|
| 284 |
const base = rawData[a]?.[0] ? latestClose(rawData[a][0]) : 1;
|
| 285 |
+
const val = rawData[a]?.[idx] ? latestClose(rawData[a][idx]) : base;
|
| 286 |
+
row[a] = base ? val / base : 1;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
});
|
| 288 |
return row;
|
| 289 |
});
|
| 290 |
+
}, [assets, dates, windowLen, rawData]);
|
| 291 |
|
| 292 |
+
// Intraday (single day, normalized to first tick of the day)
|
| 293 |
const visibleDates = useMemo(() => dates.slice(0, Math.max(0, windowLen)), [dates, windowLen]);
|
| 294 |
const activeIntradayIdx = intradayDayIdx ?? Math.min(windowLen - 1, dates.length - 1);
|
| 295 |
const intradayData = useMemo(() => {
|
|
|
|
| 306 |
assets.forEach(a => {
|
| 307 |
const arr = rawData[a]?.[activeIntradayIdx]?.close ?? [];
|
| 308 |
const val = arr[i];
|
| 309 |
+
row[a] = typeof val === "number" ? (dayFirstPrice[a] ? val / dayFirstPrice[a] : null) : null;
|
|
|
|
|
|
|
|
|
|
|
|
|
| 310 |
});
|
| 311 |
rows.push(row);
|
| 312 |
}
|
| 313 |
return rows;
|
| 314 |
+
}, [assets, rawData, dates.length, activeIntradayIdx]);
|
| 315 |
|
| 316 |
function realizedNextDayReturn(asset: string) {
|
| 317 |
const t = windowLen - 1;
|
|
|
|
| 440 |
useEffect(() => {
|
| 441 |
function onKey(e: KeyboardEvent) {
|
| 442 |
const tag = (e.target && (e.target as HTMLElement).tagName) || "";
|
| 443 |
+
if (tag === "INPUT" || tag === "TEXTAREA") return;
|
| 444 |
const idx = parseInt((e as any).key, 10) - 1;
|
| 445 |
if (!Number.isNaN(idx) && idx >= 0 && idx < assets.length) setSelectedAsset(assets[idx]);
|
| 446 |
if ((e as any).key === "Enter" && Boolean(selectedAsset) && windowLen < maxDays) confirmSelection();
|
|
|
|
| 475 |
</div>
|
| 476 |
);
|
| 477 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 478 |
return (
|
| 479 |
<div className="p-6 bg-gray-50 min-h-screen flex flex-col gap-6">
|
| 480 |
<div className="flex flex-wrap justify-between items-center gap-3">
|
| 481 |
<div className="flex items-center gap-2">
|
| 482 |
<h1 className="text-xl font-semibold">Asset Choice Simulation</h1>
|
| 483 |
+
<span className="text-[10px] ml-2 px-1.5 py-0.5 rounded bg-amber-200 text-amber-900">BUILD_TAG: 2025-10-28</span>
|
| 484 |
<span className="text-xs text-gray-500">Dataset: {datasetName || datasetId}</span>
|
| 485 |
<span className="text-xs text-gray-500">User: {userId.slice(0,8)}…</span>
|
| 486 |
<span className="text-xs text-gray-500">Day {windowLen} / {maxDays}</span>
|
|
|
|
| 495 |
<button className={`text-xs px-3 py-1.5 ${viewMode==="intraday" ? "bg-gray-900 text-white" : "bg-white text-gray-700 hover:bg-gray-50"}`} onClick={()=>setViewMode("intraday")}>Intraday (1 day)</button>
|
| 496 |
</div>
|
| 497 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 498 |
<button onClick={exportLog} className="text-sm px-3 py-1.5 rounded-xl bg-gray-900 text-white hover:bg-black">Export My Log</button>
|
| 499 |
</div>
|
| 500 |
</div>
|
|
|
|
| 513 |
</button>
|
| 514 |
))}
|
| 515 |
{assets.length>0 && <span className="text-xs text-gray-500 ml-1">Hotkeys: 1..{assets.length}, Enter confirm</span>}
|
|
|
|
|
|
|
|
|
|
| 516 |
</div>
|
| 517 |
|
| 518 |
<div className="h-80 relative">
|
|
|
|
| 522 |
<CartesianGrid strokeDasharray="3 3" />
|
| 523 |
<XAxis dataKey="idx" tick={{ fontSize: 10 }} />
|
| 524 |
<YAxis domain={["auto", "auto"]} tick={{ fontSize: 10 }} />
|
| 525 |
+
<Tooltip contentStyle={{ fontSize: 12 }} formatter={(v: any, n: any) => [v, aliasMap[n] || n]} />
|
| 526 |
<Legend onClick={(o: any) => setSelectedAsset(o.value)} wrapperStyle={{ cursor: "pointer" }} formatter={(v: any) => aliasMap[v] || v} />
|
| 527 |
{assets.map((a, i) => (
|
| 528 |
<Line
|
|
|
|
| 547 |
<CartesianGrid strokeDasharray="3 3" />
|
| 548 |
<XAxis dataKey="date" tick={{ fontSize: 10 }} />
|
| 549 |
<YAxis domain={["auto", "auto"]} tick={{ fontSize: 10 }} />
|
| 550 |
+
<Tooltip contentStyle={{ fontSize: 12 }} formatter={(v: any, n: any) => [v, aliasMap[n] || n]} />
|
| 551 |
<Legend onClick={(o: any) => setSelectedAsset(o.value)} wrapperStyle={{ cursor: "pointer" }} formatter={(v: any) => aliasMap[v] || v} />
|
| 552 |
{assets.map((a, i) => (
|
| 553 |
<Line
|