@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 +5 -2
- package/dist/index.d.ts +2 -1
- package/dist/index.js +76 -22
- package/dist/indicators/definitions.d.ts +5 -0
- package/dist/objects/trading.d.ts +25 -0
- package/package.json +1 -1
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
1767
|
-
const segments = [
|
|
1768
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1789
|
-
const
|
|
1790
|
-
const segments = [{ text: main, bg: color, fg: "#FFFFFF" }];
|
|
1791
|
-
if (
|
|
1792
|
-
const rects =
|
|
1793
|
-
if (
|
|
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:
|
|
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>;
|