@funnest/charts 0.6.6 → 0.6.8

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.
package/dist/chart.d.ts CHANGED
@@ -3,7 +3,7 @@ import { TimeScale, type IndexRange, type TimeScaleOptions } from './model/time-
3
3
  import type { IndicatorDefinition, IndicatorParams, IndicatorType } from './indicators/definitions';
4
4
  import { type IndicatorConfig, type IndicatorPatch } from './indicators/manager';
5
5
  import { type Drawing, type DrawingDefinition, type DrawingType } from './objects/drawings';
6
- import { type AlertLineOptions, type ExecutionMarker, type LineHandle, type OrderLineOptions, type PositionLineOptions } from './objects/trading';
6
+ import { type AlertLineOptions, type ExecutionMarker, type LineHandle, type OrderLineOptions, type PositionLineOptions, type TradingLabelOptions } from './objects/trading';
7
7
  import { type ChartSettings, type ChartState } from './settings';
8
8
  import { type TradingAdapter } from './objects/trading-sync';
9
9
  import { type ChartType, type CrosshairInfo, type PaneId } from './render/renderer';
@@ -411,8 +411,11 @@ export declare class Chart {
411
411
  addOrderLine(options: OrderLineOptions): LineHandle<OrderLineOptions>;
412
412
  /** Price alert line (dotted, gray, "알림" label). Pass `onMove` to make it draggable and `onCancel` for an ×. */
413
413
  addAlertLine(options: AlertLineOptions): LineHandle<AlertLineOptions>;
414
- /** Fill markers (▲ buy under the bar, ▼ sell above). Replaces the previous set. */
414
+ /** Fill markers (▲ buy under the bar, ▼ sell above; ⚡ at the price for liquidations). Replaces the previous set. */
415
415
  setExecutions(markers: readonly ExecutionMarker[]): void;
416
+ /** What position/order/alert line labels show and where (direction, price, size, P&L, ×, alignment, line width). */
417
+ setTradingLabels(patch: Partial<TradingLabelOptions>): void;
418
+ get tradingLabels(): TradingLabelOptions;
416
419
  setTheme(theme: Partial<ThemeTokens>): void;
417
420
  /** Re-reads CSS variables used by the theme (call after changing them outside of class/style attributes). */
418
421
  refreshTheme(): void;
package/dist/index.d.ts CHANGED
@@ -19,7 +19,8 @@ export type { PaneCoords } from './objects/types';
19
19
  export type { Painter } from './render/painter';
20
20
  export type { Palette } from './render/renderer';
21
21
  export { snapToOhlc } from './objects/magnet';
22
- export type { AlertLineOptions, ExecutionGroup, ExecutionMarker, LineHandle, OrderLineOptions, PositionLineOptions } from './objects/trading';
22
+ export { DEFAULT_TRADING_LABEL_OPTIONS } from './objects/trading';
23
+ export type { AlertLineOptions, ExecutionGroup, ExecutionMarker, LineHandle, OrderLineOptions, PositionLineOptions, TradingLabelOptions } from './objects/trading';
23
24
  export type { Execution, OrderInfo, PositionInfo, TradingAdapter, TradingCallbacks } from './objects/trading-sync';
24
25
  export { bucketStart, TickAggregator, timeZoneOffset } from './realtime';
25
26
  export type { Tick } from './realtime';
package/dist/index.js CHANGED
@@ -1583,6 +1583,15 @@ var Painter = class {
1583
1583
  var inRect = (r, x, y, pad = 0) => x >= r.x - pad && x <= r.x + r.w + pad && y >= r.y - pad && y <= r.y + r.h + pad;
1584
1584
 
1585
1585
  // src/objects/trading.ts
1586
+ var DEFAULT_TRADING_LABEL_OPTIONS = {
1587
+ side: true,
1588
+ price: false,
1589
+ quantity: true,
1590
+ pnl: true,
1591
+ buttons: true,
1592
+ align: "left",
1593
+ fullWidth: true
1594
+ };
1586
1595
  var LABEL_X = 12;
1587
1596
  var LABEL_HEIGHT = 20;
1588
1597
  var LABEL_GAP = 6;
@@ -1600,6 +1609,14 @@ var TradingLayer = class {
1600
1609
  labelRects = /* @__PURE__ */ new Map();
1601
1610
  drag = null;
1602
1611
  groups = [];
1612
+ labelOptions = { ...DEFAULT_TRADING_LABEL_OPTIONS };
1613
+ get labelSettings() {
1614
+ return { ...this.labelOptions };
1615
+ }
1616
+ setLabelOptions(patch) {
1617
+ this.labelOptions = { ...this.labelOptions, ...patch };
1618
+ this.onChange();
1619
+ }
1603
1620
  addPosition(options) {
1604
1621
  const id = newId("pos");
1605
1622
  this.positions.set(id, { ...options });
@@ -1704,19 +1721,21 @@ var TradingLayer = class {
1704
1721
  const index = barIndexAt(e.time);
1705
1722
  const bar = bars[index];
1706
1723
  if (!bar) continue;
1707
- const key = `${bar.time}:${e.side}`;
1724
+ const liquidation = e.kind === "liquidation";
1725
+ const key = `${bar.time}:${liquidation ? "liq" : e.side}`;
1708
1726
  let g = byKey.get(key);
1709
1727
  if (!g) {
1710
1728
  const buy = e.side === "buy";
1711
1729
  g = {
1712
1730
  key,
1713
1731
  side: e.side,
1732
+ kind: liquidation ? "liquidation" : "fill",
1714
1733
  count: 0,
1715
1734
  quantity: 0,
1716
1735
  averagePrice: 0,
1717
1736
  time: e.time,
1718
1737
  x: indexToX(index),
1719
- y: buy ? c.priceToY(bar.low) + 10 : c.priceToY(bar.high) - 10,
1738
+ y: liquidation ? c.priceToY(e.price) : buy ? c.priceToY(bar.low) + 10 : c.priceToY(bar.high) - 10,
1720
1739
  weighted: 0,
1721
1740
  weight: 0
1722
1741
  };
@@ -1733,6 +1752,23 @@ var TradingLayer = class {
1733
1752
  const { ctx } = p;
1734
1753
  for (const g of this.groups) {
1735
1754
  if (g.x < -10 || g.x > c.plotWidth + 10) continue;
1755
+ if (g.kind === "liquidation") {
1756
+ ctx.beginPath();
1757
+ ctx.arc(g.x, g.y, 8, 0, Math.PI * 2);
1758
+ ctx.fillStyle = palette.down;
1759
+ ctx.fill();
1760
+ ctx.beginPath();
1761
+ ctx.moveTo(g.x + 1.5, g.y - 5.5);
1762
+ ctx.lineTo(g.x - 3.5, g.y + 1);
1763
+ ctx.lineTo(g.x - 0.2, g.y + 1);
1764
+ ctx.lineTo(g.x - 1.5, g.y + 5.5);
1765
+ ctx.lineTo(g.x + 3.5, g.y - 1);
1766
+ ctx.lineTo(g.x + 0.2, g.y - 1);
1767
+ ctx.closePath();
1768
+ ctx.fillStyle = "#FFFFFF";
1769
+ ctx.fill();
1770
+ continue;
1771
+ }
1736
1772
  const buy = g.side === "buy";
1737
1773
  ctx.beginPath();
1738
1774
  ctx.moveTo(g.x, buy ? g.y - 5 : g.y + 5);
@@ -1743,15 +1779,17 @@ var TradingLayer = class {
1743
1779
  ctx.fill();
1744
1780
  if (g.count > 1) p.text(String(g.count), g.x + 7, g.y, buy ? palette.up : palette.down, "left", "middle");
1745
1781
  }
1782
+ const opts = this.labelOptions;
1746
1783
  const placed = [];
1747
1784
  const placeX = (width, y) => {
1748
- let x = LABEL_X;
1785
+ const toLeft = opts.align === "right";
1786
+ let x = opts.align === "left" ? LABEL_X : toLeft ? c.plotWidth - width - LABEL_X : (c.plotWidth - width) / 2;
1749
1787
  for (let moved = true; moved; ) {
1750
1788
  moved = false;
1751
1789
  for (const r of placed) {
1752
1790
  const overlapsY = Math.abs(r.y + LABEL_HEIGHT / 2 - y) < LABEL_HEIGHT + 2;
1753
1791
  if (overlapsY && x < r.x + r.w + LABEL_GAP && x + width > r.x - LABEL_GAP) {
1754
- x = r.x + r.w + LABEL_GAP;
1792
+ x = toLeft ? r.x - width - LABEL_GAP : r.x + r.w + LABEL_GAP;
1755
1793
  moved = true;
1756
1794
  }
1757
1795
  }
@@ -1759,15 +1797,23 @@ var TradingLayer = class {
1759
1797
  placed.push({ x, y: y - LABEL_HEIGHT / 2, w: width, h: LABEL_HEIGHT });
1760
1798
  return x;
1761
1799
  };
1800
+ const drawLine = (y, color, segments, dash) => {
1801
+ if (segments.length === 0) {
1802
+ p.hLine(y, 0, c.plotWidth, color, dash);
1803
+ return null;
1804
+ }
1805
+ const x = placeX(p.pillWidth(segments), y);
1806
+ const from = opts.fullWidth ? 0 : Math.min(x + p.pillWidth(segments), c.plotWidth);
1807
+ p.hLine(y, from, c.plotWidth, color, dash);
1808
+ return p.pill(segments, x, y, LABEL_HEIGHT);
1809
+ };
1762
1810
  for (const pos of this.positions.values()) {
1763
1811
  const y = c.priceToY(pos.price);
1764
1812
  badges.push({ y, text: c.formatPrice(pos.price), bg: palette.text, fg: palette.background });
1765
1813
  if (!visible(y)) continue;
1766
- p.hLine(y, 0, c.plotWidth, palette.text);
1767
- const segments = [
1768
- { text: `${pos.label ?? labels.position} \xB7 ${pos.quantity}`, bg: palette.text, fg: palette.background }
1769
- ];
1770
- if (last) {
1814
+ const main = [opts.side ? pos.label ?? labels.position : "", opts.price ? c.formatPrice(pos.price) : "", opts.quantity ? String(pos.quantity) : ""].filter(Boolean).join(" \xB7 ");
1815
+ const segments = main ? [{ text: main, bg: palette.text, fg: palette.background }] : [];
1816
+ if (last && opts.pnl) {
1771
1817
  const pnl = (last.close - pos.price) * pos.quantity * (pos.side === "long" ? 1 : -1);
1772
1818
  const pct = pos.price ? (last.close - pos.price) / pos.price * 100 * (pos.side === "long" ? 1 : -1) : 0;
1773
1819
  const sign = pnl > 0 ? "+" : "";
@@ -1777,7 +1823,7 @@ var TradingLayer = class {
1777
1823
  fg: "#FFFFFF"
1778
1824
  });
1779
1825
  }
1780
- p.pill(segments, placeX(p.pillWidth(segments), y), y, LABEL_HEIGHT);
1826
+ drawLine(y, palette.text, segments);
1781
1827
  }
1782
1828
  for (const [id, order] of this.orders) {
1783
1829
  const price = this.drag?.id === id ? this.drag.price : order.price;
@@ -1785,15 +1831,15 @@ var TradingLayer = class {
1785
1831
  const color = order.alert ? palette.textSecondary : order.side === "buy" ? palette.up : palette.down;
1786
1832
  badges.push({ y, text: c.formatPrice(price), bg: color, fg: "#FFFFFF" });
1787
1833
  if (!visible(y)) continue;
1788
- p.hLine(y, 0, c.plotWidth, color, order.alert ? ALERT_DASH : ORDER_DASH);
1789
- const main = order.alert ? order.label ?? labels.alert : [order.side === "buy" ? labels.buy : labels.sell, order.label, String(order.quantity)].filter(Boolean).join(" ");
1790
- const segments = [{ text: main, bg: color, fg: "#FFFFFF" }];
1791
- if (order.onCancel) segments.push({ text: "\xD7", bg: color, fg: "#FFFFFF", padding: 7 });
1792
- const rects = p.pill(segments, placeX(p.pillWidth(segments), y), y, LABEL_HEIGHT);
1793
- if (order.onCancel && rects[1]) p.vLine(rects[1].x, y - 6, y + 6, "rgba(255,255,255,0.4)");
1834
+ const main = (order.alert ? [order.label ?? labels.alert, opts.price ? c.formatPrice(price) : ""] : [opts.side ? order.side === "buy" ? labels.buy : labels.sell : "", order.label, opts.price ? c.formatPrice(price) : "", opts.quantity ? String(order.quantity) : ""]).filter(Boolean).join(" ");
1835
+ const cancellable = !!order.onCancel && opts.buttons;
1836
+ const segments = [{ text: main || c.formatPrice(price), bg: color, fg: "#FFFFFF" }];
1837
+ if (cancellable) segments.push({ text: "\xD7", bg: color, fg: "#FFFFFF", padding: 7 });
1838
+ const rects = drawLine(y, color, segments, order.alert ? ALERT_DASH : ORDER_DASH);
1839
+ if (cancellable && rects[1]) p.vLine(rects[1].x, y - 6, y + 6, "rgba(255,255,255,0.4)");
1794
1840
  this.labelRects.set(id, {
1795
1841
  label: { x: rects[0].x, y: rects[0].y, w: rects.reduce((w, r) => w + r.w, 0), h: LABEL_HEIGHT },
1796
- cancel: order.onCancel ? rects[1] : null
1842
+ cancel: cancellable ? rects[1] : null
1797
1843
  });
1798
1844
  }
1799
1845
  }
@@ -2673,8 +2719,8 @@ function firstFinite(values, from) {
2673
2719
  return Number.NaN;
2674
2720
  }
2675
2721
  var TOOLTIP_LABELS = {
2676
- ko: { buy: "\uB9E4\uC218", sell: "\uB9E4\uB3C4", fills: (n) => `${n}\uAC74`, price: "\uCCB4\uACB0\uAC00", avg: "\uD3C9\uADE0\uAC00", qty: "\uC218\uB7C9" },
2677
- en: { buy: "Buy", sell: "Sell", fills: (n) => `${n} fills`, price: "Price", avg: "Avg", qty: "Qty" }
2722
+ ko: { buy: "\uB9E4\uC218", sell: "\uB9E4\uB3C4", liquidation: "\uCCAD\uC0B0", fills: (n) => `${n}\uAC74`, price: "\uCCB4\uACB0\uAC00", avg: "\uD3C9\uADE0\uAC00", qty: "\uC218\uB7C9" },
2723
+ en: { buy: "Buy", sell: "Sell", liquidation: "Liquidation", fills: (n) => `${n} fills`, price: "Price", avg: "Avg", qty: "Qty" }
2678
2724
  };
2679
2725
  function fallbackResolution(requested, supported) {
2680
2726
  if (!supported?.length || supported.includes(requested)) return requested;
@@ -3676,10 +3722,17 @@ var Chart = class {
3676
3722
  addAlertLine(options) {
3677
3723
  return this.trading.addAlert(options);
3678
3724
  }
3679
- /** Fill markers (▲ buy under the bar, ▼ sell above). Replaces the previous set. */
3725
+ /** Fill markers (▲ buy under the bar, ▼ sell above; ⚡ at the price for liquidations). Replaces the previous set. */
3680
3726
  setExecutions(markers) {
3681
3727
  this.trading.setExecutions(markers);
3682
3728
  }
3729
+ /** What position/order/alert line labels show and where (direction, price, size, P&L, ×, alignment, line width). */
3730
+ setTradingLabels(patch) {
3731
+ this.trading.setLabelOptions(patch);
3732
+ }
3733
+ get tradingLabels() {
3734
+ return this.trading.labelSettings;
3735
+ }
3683
3736
  setTheme(theme) {
3684
3737
  this.theme = { ...this.theme, ...theme };
3685
3738
  this.colors.clear();
@@ -4177,14 +4230,14 @@ var Chart = class {
4177
4230
  const g = this.tooltipKey ? this.trading.executionGroup(this.tooltipKey) : void 0;
4178
4231
  if (!g) return null;
4179
4232
  const t = TOOLTIP_LABELS[this.locale];
4180
- const side = g.side === "buy" ? t.buy : t.sell;
4233
+ const side = g.kind === "liquidation" ? t.liquidation : g.side === "buy" ? t.buy : t.sell;
4181
4234
  const lines = [
4182
4235
  g.count > 1 ? `${side} ${t.fills(g.count)}` : side,
4183
4236
  `${g.count > 1 ? t.avg : t.price} ${formatPrice(roundToTick(g.averagePrice, this.tickSize), this.pricePrecision)}`
4184
4237
  ];
4185
4238
  if (g.quantity > 0) lines.push(`${t.qty} ${Number(g.quantity.toFixed(8))}`);
4186
4239
  lines.push(this.formatter.crosshairLabel(g.time, true));
4187
- return { x: g.x, y: g.y, side: g.side, lines };
4240
+ return { x: g.x, y: g.y, side: g.kind === "liquidation" ? "sell" : g.side, lines };
4188
4241
  }
4189
4242
  emitCrosshair(info) {
4190
4243
  const key = info ? `${info.index}|${info.value}|${info.bar.close}|${info.bar.volume ?? ""}` : "";
@@ -4578,6 +4631,7 @@ export {
4578
4631
  CHART_THEMES,
4579
4632
  Chart,
4580
4633
  DEFAULT_CHART_SETTINGS,
4634
+ DEFAULT_TRADING_LABEL_OPTIONS,
4581
4635
  FIB_LEVELS,
4582
4636
  INDICATORS,
4583
4637
  PRICE_SOURCES,
@@ -77,6 +77,11 @@ export interface IndicatorDefinition {
77
77
  defaults: IndicatorParams;
78
78
  title(params: IndicatorParams): string;
79
79
  compute(bars: readonly Bar[], params: IndicatorParams): IndicatorOutput;
80
+ /**
81
+ * Left out of the indicator picker: added only from code (e.g. an app's user scripts, whose
82
+ * params say which script). Charts that already have it show and save it as usual.
83
+ */
84
+ hidden?: boolean;
80
85
  }
81
86
  /** Distinct line colors that avoid the up/down candle colors. */
82
87
  export declare const SERIES_COLORS: string[];
@@ -37,11 +37,33 @@ export interface ExecutionMarker {
37
37
  price: number;
38
38
  side: 'buy' | 'sell';
39
39
  quantity?: number;
40
+ /** 'liquidation': a forced close, drawn as a ⚡ badge at its price instead of ▲/▼. Default 'fill'. */
41
+ kind?: 'fill' | 'liquidation';
40
42
  }
43
+ /**
44
+ * What the labels of position, order and alert lines show, and where (chart settings like
45
+ * "라벨" in trading terminals). Defaults keep the original look.
46
+ */
47
+ export interface TradingLabelOptions {
48
+ /** 매수/매도 and the position name. */
49
+ side: boolean;
50
+ /** The line's price inside the label (the price axis always shows it). */
51
+ price: boolean;
52
+ quantity: boolean;
53
+ /** Positions: P&L against the last price. */
54
+ pnl: boolean;
55
+ /** The × on cancellable lines. */
56
+ buttons: boolean;
57
+ align: 'left' | 'center' | 'right';
58
+ /** Draw lines across the whole chart; off, only from the label to the price axis. */
59
+ fullWidth: boolean;
60
+ }
61
+ export declare const DEFAULT_TRADING_LABEL_OPTIONS: TradingLabelOptions;
41
62
  /** Fills on the same bar and side, shown as one marker. */
42
63
  export interface ExecutionGroup {
43
64
  key: string;
44
65
  side: 'buy' | 'sell';
66
+ kind: 'fill' | 'liquidation';
45
67
  count: number;
46
68
  quantity: number;
47
69
  /** Quantity-weighted average price (plain average when quantities are missing). */
@@ -72,7 +94,10 @@ export declare class TradingLayer {
72
94
  private labelRects;
73
95
  private drag;
74
96
  private groups;
97
+ private labelOptions;
75
98
  constructor(onChange: () => void);
99
+ get labelSettings(): TradingLabelOptions;
100
+ setLabelOptions(patch: Partial<TradingLabelOptions>): void;
76
101
  addPosition(options: PositionLineOptions): LineHandle<PositionLineOptions>;
77
102
  addOrder(options: OrderLineOptions): LineHandle<OrderLineOptions>;
78
103
  addAlert(options: AlertLineOptions): LineHandle<AlertLineOptions>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@funnest/charts",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
4
4
  "description": "Canvas charting engine for financial charts",
5
5
  "keywords": [
6
6
  "chart",