File size: 1,767 Bytes
5545e4d
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Per-trader client state. The chart is seeded once from the stored time series,
// then grows a point on every poll so the line keeps moving while you watch.

import type { Holding, TraderDetail, TraderInfo } from "./api";

export const CHART_MAX_POINTS = 5000;

export interface ChartPoint {
  t: number; // unix seconds
  value: number;
}

function toUnixSeconds(stamp: string): number {
  // Stored timestamps are "YYYY-MM-DD HH:MM:SS" local time.
  return new Date(stamp.replace(" ", "T")).getTime() / 1000;
}

export class TraderState {
  readonly info: TraderInfo;
  detail: TraderDetail | null = null;
  chart: ChartPoint[] = [];
  previousPrices: Record<string, number> = {};
  private seeded = false;

  constructor(info: TraderInfo) {
    this.info = info;
  }

  recordDetail(detail: TraderDetail): void {
    this.detail = detail;
    if (!this.seeded) {
      this.chart = detail.time_series.map((p) => ({ t: toUnixSeconds(p.datetime), value: p.value }));
      this.seeded = true;
    }
    this.chart.push({ t: Date.now() / 1000, value: detail.portfolio_value });
    if (this.chart.length > CHART_MAX_POINTS) {
      this.chart.splice(0, this.chart.length - CHART_MAX_POINTS);
    }
  }

  priceDirections(): Record<string, "up" | "down" | "same"> {
    const out: Record<string, "up" | "down" | "same"> = {};
    if (!this.detail) return out;
    for (const h of this.detail.holdings) {
      const prev = this.previousPrices[h.symbol];
      out[h.symbol] =
        prev === undefined || prev === h.price ? "same" : h.price > prev ? "up" : "down";
    }
    return out;
  }

  rememberPrices(): void {
    if (!this.detail) return;
    this.previousPrices = Object.fromEntries(this.detail.holdings.map((h: Holding) => [h.symbol, h.price]));
  }
}