Yan Wang commited on
Commit
46472e0
·
1 Parent(s): b029aed

remove function of multi-day intraday

Browse files
Files changed (1) hide show
  1. src/App_New.tsx +3 -161
src/App_New.tsx CHANGED
@@ -1,7 +1,7 @@
1
  import { useEffect, useMemo, useState } from "react";
2
  import {
3
  LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, Legend, CartesianGrid,
4
- AreaChart, Area, BarChart, Bar, ReferenceLine
5
  } from "recharts";
6
  import {
7
  apiGetLatestDataset,
@@ -12,7 +12,7 @@ import {
12
  } from "./api";
13
 
14
  type SeriesPoint = { date: string; close: number[] };
15
- type ViewMode = "daily" | "intraday" | "multi";
16
 
17
  const START_DAY = 7;
18
 
@@ -103,7 +103,6 @@ export default function App() {
103
 
104
  const [viewMode, setViewMode] = useState<ViewMode>("daily");
105
  const [intradayDayIdx, setIntradayDayIdx] = useState<number | null>(null);
106
- const [multiNormalize, setMultiNormalize] = useState<boolean>(true); // toggle for multi view
107
 
108
  /* ---------- localStorage keys ---------- */
109
  const LS_DATASET_KEY = "annot_dataset_meta";
@@ -314,88 +313,6 @@ export default function App() {
314
  return rows;
315
  }, [assets, rawData, dates.length, activeIntradayIdx]);
316
 
317
- // ---------------- Multi-day intraday (稳健逐日拼接 + 跨天累计) ----------------
318
- const { multiRows, dayStarts } = useMemo(() => {
319
- const rows: any[] = [];
320
- const starts: { x: number; date: string }[] = [];
321
- if (!dates.length || windowLen < 1) return { multiRows: rows, dayStarts: starts };
322
-
323
- // 跨天累计倍率(仅 normalized 使用)
324
- const cumIdx: Record<string, number> = {};
325
- assets.forEach(a => { cumIdx[a] = 1; });
326
-
327
- let x = 0;
328
- const D = Math.min(windowLen, dates.length);
329
-
330
- for (let d = 0; d < D; d++) {
331
- starts.push({ x: x + 1, date: dates[d] });
332
-
333
- // 本日最长分钟数(至少 1)
334
- const dayLen = Math.max(
335
- 1,
336
- assets.reduce((m, a) => Math.max(m, rawData[a]?.[d]?.close?.length ?? 0), 0)
337
- );
338
-
339
- // 找“当日首个有效价”作为锚,并准备“本日最后一个有效价”
340
- const dayFirst: Record<string, number | null> = {};
341
- const lastSeen: Record<string, number | null> = {};
342
-
343
- assets.forEach(a => {
344
- const arr = rawData[a]?.[d]?.close ?? [];
345
- let first: number | null = null;
346
- for (let i = 0; i < arr.length; i++) {
347
- const v = arr[i];
348
- if (typeof v === "number" && Number.isFinite(v)) { first = v; break; }
349
- }
350
- dayFirst[a] = first;
351
- lastSeen[a] = null;
352
- });
353
-
354
- // 逐分钟行;优先当前值,其次上一分钟值,再次用 dayFirst 兜底
355
- for (let i = 0; i < dayLen; i++) {
356
- const row: Record<string, any> = { x: x + 1, day: dates[d], minute: i + 1 };
357
-
358
- assets.forEach(a => {
359
- const arr = rawData[a]?.[d]?.close ?? [];
360
- const v = arr[i];
361
-
362
- let price: number | null = null;
363
- if (typeof v === "number" && Number.isFinite(v)) {
364
- price = v;
365
- lastSeen[a] = v;
366
- } else if (lastSeen[a] != null) {
367
- price = lastSeen[a];
368
- } else if (dayFirst[a] != null) {
369
- price = dayFirst[a];
370
- lastSeen[a] = price;
371
- } else {
372
- price = null; // 这一整天没有任何有效价
373
- }
374
-
375
- if (price == null) {
376
- row[a] = null;
377
- } else {
378
- row[a] = multiNormalize
379
- ? (dayFirst[a] && Number.isFinite(dayFirst[a]) ? cumIdx[a] * (price / (dayFirst[a] as number)) : null)
380
- : price;
381
- }
382
- });
383
-
384
- rows.push(row);
385
- x++;
386
- }
387
-
388
- // 天末:若有有效价,则把“末价/首价”乘到 cumIdx,保证下一天从累计值继续
389
- assets.forEach(a => {
390
- if (multiNormalize && dayFirst[a] != null && lastSeen[a] != null && dayFirst[a]! > 0) {
391
- cumIdx[a] *= (lastSeen[a]! / dayFirst[a]!);
392
- }
393
- });
394
- }
395
-
396
- return { multiRows: rows, dayStarts: starts };
397
- }, [assets, rawData, dates, windowLen, multiNormalize]);
398
-
399
  function realizedNextDayReturn(asset: string) {
400
  const t = windowLen - 1;
401
  if (t + 1 >= dates.length) return null as any;
@@ -558,20 +475,6 @@ export default function App() {
558
  </div>
559
  );
560
 
561
- // ------- helper to give Y-axis safe padding even when dataMin == dataMax -------
562
- const multiYAxisDomain: any = [
563
- (min: number) => {
564
- if (!Number.isFinite(min)) return "auto";
565
- const pad = Math.abs(min) * 0.005 || 0.01;
566
- return min - pad;
567
- },
568
- (max: number) => {
569
- if (!Number.isFinite(max)) return "auto";
570
- const pad = Math.abs(max) * 0.005 || 0.01;
571
- return max + pad;
572
- }
573
- ];
574
-
575
  return (
576
  <div className="p-6 bg-gray-50 min-h-screen flex flex-col gap-6">
577
  <div className="flex flex-wrap justify-between items-center gap-3">
@@ -590,19 +493,8 @@ export default function App() {
590
  <div className="flex rounded-xl overflow-hidden border border-gray-200">
591
  <button className={`text-xs px-3 py-1.5 ${viewMode==="daily" ? "bg-gray-900 text-white" : "bg-white text-gray-700 hover:bg-gray-50"}`} onClick={()=>setViewMode("daily")}>Daily</button>
592
  <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>
593
- <button className={`text-xs px-3 py-1.5 ${viewMode==="multi" ? "bg-gray-900 text-white" : "bg-white text-gray-700 hover:bg-gray-50"}`} onClick={()=>setViewMode("multi")}>Multi-day Intraday</button>
594
  </div>
595
 
596
- {viewMode==="multi" && (
597
- <button
598
- className="text-xs px-3 py-1.5 rounded-xl border border-gray-200 hover:bg-gray-50"
599
- onClick={() => setMultiNormalize(v => !v)}
600
- title="Toggle normalized cumulative index vs raw price"
601
- >
602
- {multiNormalize ? "Normalized" : "Raw Price"}
603
- </button>
604
- )}
605
-
606
  <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>
607
  </div>
608
  </div>
@@ -650,56 +542,6 @@ export default function App() {
650
  />
651
  ))}
652
  </LineChart>
653
- ) : viewMode === "multi" ? (
654
- <>
655
- <div className="absolute right-2 top-2 z-10 text-[10px] px-2 py-0.5 rounded bg-black/60 text-white">
656
- Multi • {multiNormalize ? "Normalized (cum ×)" : "Raw Price"}
657
- </div>
658
- <LineChart data={multiRows}>
659
- <CartesianGrid strokeDasharray="3 3" />
660
- <XAxis
661
- dataKey="x"
662
- tick={{ fontSize: 10 }}
663
- label={{ value: "Minute (concatenated days)", position: "insideBottomRight", offset: -2, fontSize: 10 }}
664
- />
665
- {/* Y 轴安全 padding,避免平直小波动时看不见 */}
666
- <YAxis domain={multiYAxisDomain} tick={{ fontSize: 10 }} allowDataOverflow />
667
- <Tooltip
668
- contentStyle={{ fontSize: 12 }}
669
- labelFormatter={(_, payload: any) => {
670
- const p = payload?.[0]?.payload;
671
- return p ? `${p.day} #${p.minute}` : "";
672
- }}
673
- formatter={(value: any, name: any) => {
674
- const display = multiNormalize
675
- ? (Number.isFinite(value) ? `${(value as number).toFixed(4)}×` : value)
676
- : value;
677
- return [display, aliasMap[name] || name];
678
- }}
679
- />
680
- <Legend onClick={(o: any) => setSelectedAsset(o.value)} wrapperStyle={{ cursor: "pointer" }} formatter={(v: any) => aliasMap[v] || v} />
681
- {dayStarts.map(s => (
682
- <ReferenceLine key={s.x} x={s.x} stroke="#6b7280" strokeDasharray="4 2" ifOverflow="extendDomain" />
683
- ))}
684
- {assets.map((a, i) => (
685
- <Line
686
- key={a}
687
- type="linear"
688
- dataKey={a}
689
- name={aliasMap[a] || a}
690
- strokeWidth={selectedAsset === a ? 5 : hoverAsset === a ? 4 : 2.5}
691
- strokeOpacity={selectedAsset && selectedAsset !== a ? 0.3 : 1}
692
- dot={false}
693
- isAnimationActive={false}
694
- stroke={`hsl(${(360 / assets.length) * i},70%,50%)`}
695
- onMouseEnter={() => setHoverAsset(a)}
696
- onMouseLeave={() => setHoverAsset(null)}
697
- onClick={() => setSelectedAsset((p) => (p === a ? null : a))}
698
- connectNulls={true} // 关键:跨缺口仍然连线
699
- />
700
- ))}
701
- </LineChart>
702
- </>
703
  ) : (
704
  <LineChart data={windowData}>
705
  <CartesianGrid strokeDasharray="3 3" />
@@ -734,7 +576,7 @@ export default function App() {
734
  </div>
735
  )}
736
 
737
- {viewMode!=="multi" && (
738
  <div className="flex justify-between items-center mt-3">
739
  <div className="text-sm text-gray-600">
740
  Selected: {selectedAsset ? aliasOf(selectedAsset) : "(none)"} {message && <span className="ml-2 text-gray-500">{message}</span>}
 
1
  import { useEffect, useMemo, useState } from "react";
2
  import {
3
  LineChart, Line, XAxis, YAxis, Tooltip, ResponsiveContainer, Legend, CartesianGrid,
4
+ AreaChart, Area, BarChart, Bar
5
  } from "recharts";
6
  import {
7
  apiGetLatestDataset,
 
12
  } from "./api";
13
 
14
  type SeriesPoint = { date: string; close: number[] };
15
+ type ViewMode = "daily" | "intraday"; // 移除 multi
16
 
17
  const START_DAY = 7;
18
 
 
103
 
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";
 
313
  return rows;
314
  }, [assets, rawData, dates.length, activeIntradayIdx]);
315
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
316
  function realizedNextDayReturn(asset: string) {
317
  const t = windowLen - 1;
318
  if (t + 1 >= dates.length) return null as any;
 
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">
 
493
  <div className="flex rounded-xl overflow-hidden border border-gray-200">
494
  <button className={`text-xs px-3 py-1.5 ${viewMode==="daily" ? "bg-gray-900 text-white" : "bg-white text-gray-700 hover:bg-gray-50"}`} onClick={()=>setViewMode("daily")}>Daily</button>
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>
 
542
  />
543
  ))}
544
  </LineChart>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
545
  ) : (
546
  <LineChart data={windowData}>
547
  <CartesianGrid strokeDasharray="3 3" />
 
576
  </div>
577
  )}
578
 
579
+ {viewMode!=="intraday" && (
580
  <div className="flex justify-between items-center mt-3">
581
  <div className="text-sm text-gray-600">
582
  Selected: {selectedAsset ? aliasOf(selectedAsset) : "(none)"} {message && <span className="ml-2 text-gray-500">{message}</span>}