@funnest/charts 0.5.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,9 @@
1
1
  import type { Locale } from '../model/time-format';
2
2
  import type { Bar } from '../types';
3
- export type IndicatorType = 'ma' | 'ema' | 'bb' | 'rsi' | 'macd';
3
+ /** Built-in indicators. */
4
+ export type BuiltinIndicatorType = 'ma' | 'ema' | 'bb' | 'rsi' | 'macd';
5
+ /** A built-in type, or the `type` of a definition passed in `ChartOptions.indicators`. */
6
+ export type IndicatorType = BuiltinIndicatorType | (string & {});
4
7
  /** Which price an indicator reads from each bar. */
5
8
  export type PriceSource = 'close' | 'open' | 'high' | 'low' | 'hl2' | 'hlc3' | 'ohlc4';
6
9
  export declare const PRICE_SOURCES: {
@@ -39,14 +42,23 @@ export interface IndicatorSeries {
39
42
  width: number;
40
43
  visible: boolean;
41
44
  kind: 'line' | 'histogram';
45
+ /**
46
+ * Draw `values[i]` this many bars to the right (Ichimoku spans lead by 25). Points past the
47
+ * last bar land in the empty space on the right. Default 0.
48
+ */
49
+ shift?: number;
42
50
  }
43
51
  export interface IndicatorOutput {
44
52
  series: IndicatorSeries[];
45
- /** Shaded area between two series (Bollinger band). */
53
+ /**
54
+ * Shaded area between two series (Bollinger band). With `downColor`, stretches where
55
+ * `lower` is above `upper` use it instead (Ichimoku cloud).
56
+ */
46
57
  fill?: {
47
58
  upper: string;
48
59
  lower: string;
49
60
  color: string;
61
+ downColor?: string;
50
62
  };
51
63
  /** Horizontal reference lines (RSI 30/70). */
52
64
  levels?: number[];
@@ -71,4 +83,4 @@ export declare const SERIES_COLORS: string[];
71
83
  export declare function priceSource(bars: readonly Bar[], source?: PriceSource): Float64Array;
72
84
  /** Reads `lines`, accepting the legacy `periods: number[]` shape too. */
73
85
  export declare function maLines(p: IndicatorParams): MaLine[];
74
- export declare const INDICATORS: Record<IndicatorType, IndicatorDefinition>;
86
+ export declare const INDICATORS: Record<BuiltinIndicatorType, IndicatorDefinition>;
@@ -1,5 +1,5 @@
1
1
  import type { Bar } from '../types';
2
- import { type IndicatorOutput, type IndicatorParams, type IndicatorType, type SeriesStyle } from './definitions';
2
+ import { type IndicatorDefinition, type IndicatorOutput, type IndicatorParams, type IndicatorType, type SeriesStyle } from './definitions';
3
3
  export interface IndicatorConfig {
4
4
  id: string;
5
5
  type: IndicatorType;
@@ -10,13 +10,20 @@ export interface IndicatorConfig {
10
10
  styles?: Record<string, SeriesStyle>;
11
11
  }
12
12
  export type IndicatorPatch = Partial<Pick<IndicatorConfig, 'params' | 'visible' | 'styles'>>;
13
+ /** Built-ins plus definitions passed to the chart (`ChartOptions.indicators`); later ones replace earlier ones of the same type. */
14
+ export declare function indicatorRegistry(extra?: readonly IndicatorDefinition[]): Map<string, IndicatorDefinition>;
13
15
  /** Indicator instances plus a cache of computed series keyed by data version + settings. */
14
16
  export declare class IndicatorManager {
17
+ readonly registry: Map<string, IndicatorDefinition>;
15
18
  list: IndicatorConfig[];
16
19
  private readonly cache;
20
+ constructor(registry?: Map<string, IndicatorDefinition>);
21
+ def(type: IndicatorType): IndicatorDefinition;
22
+ private normalize;
17
23
  add(type: IndicatorType, params?: IndicatorParams): string;
18
24
  remove(id: string): boolean;
19
25
  update(id: string, patch: IndicatorPatch): boolean;
26
+ /** Unknown types (e.g. a plugin this chart wasn't given) are dropped. */
20
27
  set(configs: readonly IndicatorConfig[]): void;
21
28
  snapshot(): IndicatorConfig[];
22
29
  get overlays(): IndicatorConfig[];
@@ -4,10 +4,16 @@ export interface PriceScaleMargins {
4
4
  /** Fraction of pane height kept empty below the min price. */
5
5
  bottom: number;
6
6
  }
7
+ /**
8
+ * 'normal': linear prices. 'log': equal distances are equal ratios. 'percent': linear, but labels
9
+ * show the change from a base price (the first visible bar's close).
10
+ */
11
+ export type PriceScaleMode = 'normal' | 'log' | 'percent';
7
12
  /** Vertical scale for one pane. y is relative to the pane's top. */
8
13
  export declare class PriceScale {
9
14
  readonly margins: PriceScaleMargins;
10
15
  height: number;
16
+ mode: PriceScaleMode;
11
17
  /** Pixels reserved at the top for overlays (e.g. a legend); margins apply below it. */
12
18
  insetTop: number;
13
19
  autoScale: boolean;
@@ -21,13 +27,23 @@ export declare class PriceScale {
21
27
  private get available();
22
28
  private get innerTop();
23
29
  private get innerHeight();
30
+ /** Price → the space the axis is linear in (log prices in log mode). */
31
+ private toAxis;
32
+ private fromAxis;
24
33
  priceToY(price: number): number;
25
34
  yToPrice(y: number): number;
35
+ /** Sets the range in axis space (log prices in log mode). */
36
+ private setAxisRange;
26
37
  /** Scales around the center. factor > 1 shows a wider price range. Disables auto-scale. */
27
38
  zoom(factor: number): void;
28
39
  /** Moves the content by `dy` pixels (positive = content moves down). Disables auto-scale. */
29
40
  pan(dy: number): void;
30
41
  }
42
+ /**
43
+ * Tick values for a log axis: roughly one per `spacing` px, each rounded to a nice step for the
44
+ * price distance at that height (1/2/2.5/5 × 10ⁿ, at least `minStep`). Spacing in price grows upward.
45
+ */
46
+ export declare function logTicks(scale: PriceScale, spacing: number, minStep?: number): number[];
31
47
  /**
32
48
  * "Nice" tick values (1/2/2.5/5 × 10ⁿ) covering [min, max] with at most ~maxCount ticks.
33
49
  * `minStep` (e.g. the instrument's tick size) prevents sub-tick grid lines.
@@ -1,5 +1,11 @@
1
+ import type { Locale } from '../model/time-format';
2
+ import type { Painter } from '../render/painter';
3
+ import type { Palette } from '../render/renderer';
1
4
  import { type AxisBadge, type ObjectHit, type ObjectRenderContext, type PaneCoords } from './types';
2
- export type DrawingType = 'trendline' | 'horizontal' | 'fibonacci';
5
+ /** Built-in drawing tools. */
6
+ export type BuiltinDrawingType = 'trendline' | 'horizontal' | 'fibonacci';
7
+ /** A built-in tool, or the `type` of a definition passed in `ChartOptions.drawings`. */
8
+ export type DrawingType = BuiltinDrawingType | (string & {});
3
9
  export interface DrawingPoint {
4
10
  /** UTC ms — anchored to time, so drawings survive history loads and resolution changes. */
5
11
  time: number;
@@ -11,42 +17,92 @@ export interface Drawing {
11
17
  points: DrawingPoint[];
12
18
  color?: string;
13
19
  }
14
- export declare const DRAWING_POINTS: Record<DrawingType, number>;
20
+ /** What a drawing definition gets to measure and paint itself. Pixels are relative to the price pane. */
21
+ export interface DrawingGeometry {
22
+ coords: PaneCoords;
23
+ /** `drawing.points` in pane pixels. */
24
+ points: [number, number][];
25
+ }
26
+ export interface DrawingRenderContext extends DrawingGeometry {
27
+ painter: Painter;
28
+ palette: Palette;
29
+ /** `drawing.color`, or the theme accent. */
30
+ color: string;
31
+ /** Adds a price-axis badge (drawn above the axis labels, clamped to the pane). */
32
+ badge(y: number, text: string, bg: string, fg?: string): void;
33
+ }
34
+ /**
35
+ * A drawing tool. Built-ins use the same shape; pass your own in `ChartOptions.drawings` to add
36
+ * tools to the chart and the widget's drawing bar.
37
+ */
38
+ export interface DrawingDefinition {
39
+ type: string;
40
+ name: Record<Locale, string>;
41
+ /**
42
+ * Drawing bar icon: SVG markup — either a whole `<svg>`, or just the shapes for a 20×20 viewBox
43
+ * drawn with `stroke="currentColor"` (like the built-in icons).
44
+ */
45
+ icon?: string;
46
+ /** Tools with the same group share one drawing-bar button with a menu. Default: the type. */
47
+ group?: string;
48
+ /** Clicks (or drag ends) needed to place it. */
49
+ clicks: number;
50
+ /**
51
+ * Turns the placed click points into the stored points, e.g. a position tool that creates
52
+ * entry/target/stop from one click. Called for the live preview too. Default: as placed.
53
+ */
54
+ create?(placed: DrawingPoint[], coords: PaneCoords): DrawingPoint[];
55
+ /** Keeps points consistent after point `index` was dragged (e.g. target and stop share a time). */
56
+ constrain?(points: DrawingPoint[], index: number): DrawingPoint[];
57
+ /** Drag handle positions; dragging handle i moves point i. Default: the points. */
58
+ handles?(geometry: DrawingGeometry): [number, number][];
59
+ /** Distance in px from (x, y) to the shape, for hit testing. */
60
+ distance(geometry: DrawingGeometry, x: number, y: number): number;
61
+ render(drawing: Drawing, rc: DrawingRenderContext): void;
62
+ }
15
63
  export declare const FIB_LEVELS: number[];
64
+ /** Distance from a point to the segment (x1,y1)–(x2,y2). Handy for `DrawingDefinition.distance`. */
65
+ export declare function distToSegment(px: number, py: number, x1: number, y1: number, x2: number, y2: number): number;
66
+ export declare const BUILTIN_DRAWINGS: readonly DrawingDefinition[];
67
+ /** Built-ins plus definitions passed to the chart; later ones replace earlier ones of the same type. */
68
+ export declare function drawingRegistry(extra?: readonly DrawingDefinition[]): Map<string, DrawingDefinition>;
16
69
  export type DrawingsChange = 'preview' | 'commit' | 'tool' | 'selection';
17
70
  /**
18
71
  * Owns drawings: creation (click-click or drag), selection, handle/body dragging,
19
72
  * hit testing and rendering. Pure state + canvas; pointer routing lives in Interaction.
73
+ * What each tool looks like comes from its DrawingDefinition.
20
74
  */
21
75
  export declare class DrawingManager {
22
76
  private readonly onChange;
77
+ readonly registry: Map<string, DrawingDefinition>;
23
78
  drawings: Drawing[];
24
79
  tool: DrawingType | null;
25
80
  selectedId: string | null;
26
81
  private draft;
27
82
  private drag;
28
- constructor(onChange: (change: DrawingsChange) => void);
83
+ constructor(onChange: (change: DrawingsChange) => void, registry?: Map<string, DrawingDefinition>);
29
84
  get selected(): Drawing | null;
30
85
  setTool(tool: DrawingType | null): void;
31
86
  select(id: string | null): void;
87
+ /** Drawings of unknown types are kept (and saved) but not shown. */
32
88
  set(drawings: readonly Drawing[]): void;
33
89
  update(id: string, patch: Partial<Omit<Drawing, 'id'>>): void;
34
90
  remove(id: string): void;
35
91
  clear(): void;
36
92
  /** Esc: abort an in-progress drawing, else drop the tool, else deselect. */
37
93
  cancel(): void;
94
+ private expand;
38
95
  createAt(c: PaneCoords, x: number, y: number): void;
39
96
  /** Moves the not-yet-placed points (live preview while hovering or dragging). */
40
97
  previewAt(c: PaneCoords, x: number, y: number): void;
41
98
  /** Pointer released: a drag since the first point places the next one (drag-to-draw). */
42
99
  releaseAt(c: PaneCoords, x: number, y: number, moved: boolean): void;
43
100
  private finish;
101
+ private geometry;
102
+ private handles;
44
103
  hitTest(c: PaneCoords, x: number, y: number, tolerance: number): ObjectHit | null;
45
104
  beginDrag(c: PaneCoords, id: string, part: 'body' | number, x: number, y: number): void;
46
105
  dragTo(c: PaneCoords, x: number, y: number): void;
47
106
  endDrag(moved: boolean): void;
48
- private screenPoints;
49
- private distance;
50
107
  render(rc: ObjectRenderContext, badges: AxisBadge[]): void;
51
- private renderOne;
52
108
  }
@@ -22,6 +22,16 @@ export interface OrderLineOptions {
22
22
  /** Shows an × button on the label. */
23
23
  onCancel?: () => void;
24
24
  }
25
+ /** A price alert shown as a dotted line with a bell label. The alert logic itself is yours. */
26
+ export interface AlertLineOptions {
27
+ price: number;
28
+ /** Overrides the default "알림" / "Alert" text. */
29
+ label?: string;
30
+ /** Called when the user drags the line; same contract as OrderLineOptions.onMove. */
31
+ onMove?: (price: number) => void | boolean | Promise<void | boolean>;
32
+ /** Shows an × button on the label. */
33
+ onCancel?: () => void;
34
+ }
25
35
  export interface ExecutionMarker {
26
36
  time: number;
27
37
  price: number;
@@ -50,11 +60,13 @@ export interface TradingLabels {
50
60
  buy: string;
51
61
  sell: string;
52
62
  position: string;
63
+ alert: string;
53
64
  }
54
65
  /** Position (average price) lines, draggable order lines and execution markers. */
55
66
  export declare class TradingLayer {
56
67
  private readonly onChange;
57
68
  private readonly positions;
69
+ /** Order lines and alert lines (same dragging and × behavior). */
58
70
  private readonly orders;
59
71
  private executions;
60
72
  private labelRects;
@@ -63,6 +75,7 @@ export declare class TradingLayer {
63
75
  constructor(onChange: () => void);
64
76
  addPosition(options: PositionLineOptions): LineHandle<PositionLineOptions>;
65
77
  addOrder(options: OrderLineOptions): LineHandle<OrderLineOptions>;
78
+ addAlert(options: AlertLineOptions): LineHandle<AlertLineOptions>;
66
79
  setExecutions(markers: readonly ExecutionMarker[]): void;
67
80
  private handle;
68
81
  get executionList(): readonly ExecutionMarker[];
@@ -5,6 +5,8 @@ export interface PaneCoords {
5
5
  plotWidth: number;
6
6
  height: number;
7
7
  timeToX(time: number): number;
8
+ /** Fractional bar index of `time` (extrapolated past either end), e.g. to count bars between points. */
9
+ timeToIndex(time: number): number;
8
10
  /** Point under (x, y): time snapped to the nearest bar (extrapolated into the future), price to the tick size. */
9
11
  pointAt(x: number, y: number): {
10
12
  time: number;
@@ -57,6 +57,8 @@ export interface Frame {
57
57
  palette: Palette;
58
58
  font: string;
59
59
  radius: number;
60
+ /** Time left in the current bar ("04:59"), shown under the last-price badge. */
61
+ countdown: string | null;
60
62
  }
61
63
  export interface CrosshairInfo {
62
64
  index: number;
@@ -89,6 +91,8 @@ export interface RenderModel {
89
91
  low: string;
90
92
  };
91
93
  chartType: ChartType;
94
+ /** Replay start picking: the area right of the crosshair is shaded. */
95
+ replaySelecting?: boolean;
92
96
  objects: {
93
97
  coords: PaneCoords;
94
98
  drawings: DrawingManager;
@@ -1,5 +1,6 @@
1
1
  import type { Drawing } from './objects/drawings';
2
2
  import type { IndicatorConfig } from './indicators/manager';
3
+ import type { PriceScaleMode } from './model/price-scale';
3
4
  import type { ChartType } from './render/renderer';
4
5
  import type { Resolution } from './types';
5
6
  /** User-facing chart preferences (the "차트 설정" dialog). */
@@ -14,6 +15,10 @@ export interface ChartSettings {
14
15
  volume: boolean;
15
16
  /** Zoom/scroll buttons at the bottom center of the chart (@funnest/charts-ui). */
16
17
  navigationButtons: boolean;
18
+ /** Price axis: linear prices, log scale, or % change from the first visible bar. Comparing symbols forces '%'. */
19
+ priceScale: PriceScaleMode;
20
+ /** Time left until the current bar closes, under the last-price badge. */
21
+ countdown: boolean;
17
22
  /** Up/down candle colors; null follows the theme. */
18
23
  upColor: string | null;
19
24
  downColor: string | null;
@@ -37,6 +42,8 @@ export interface ChartState {
37
42
  barSpacing: number;
38
43
  rightOffset: number;
39
44
  };
45
+ /** Symbols overlaid for comparison (see `Chart.addCompare`). */
46
+ compares?: string[];
40
47
  }
41
48
  /** Validates untrusted JSON (e.g. from storage) into a ChartState; throws on an incompatible shape. */
42
49
  export declare function parseChartState(input: unknown): ChartState;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnest/charts",
3
- "version": "0.5.1",
3
+ "version": "0.6.0",
4
4
  "description": "Canvas charting engine for financial charts",
5
5
  "keywords": [
6
6
  "chart",