@adrienhobbs/candlekit 0.2.2 → 0.2.4
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/index.d.ts +19 -2
- package/dist/index.js +84 -5
- package/dist/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -21,6 +21,16 @@ interface ChartLine {
|
|
|
21
21
|
title?: string;
|
|
22
22
|
type?: 'entry' | 'stopLoss' | 'takeProfit' | 'mfe' | 'mae';
|
|
23
23
|
}
|
|
24
|
+
/** A shaded horizontal price band (e.g. an MFE↔MAE excursion zone). */
|
|
25
|
+
interface PriceBand {
|
|
26
|
+
id: string;
|
|
27
|
+
/** Upper price bound of the band. */
|
|
28
|
+
top: number;
|
|
29
|
+
/** Lower price bound of the band. */
|
|
30
|
+
bottom: number;
|
|
31
|
+
/** CSS color (use an rgba/low-opacity fill so candles show through). */
|
|
32
|
+
color: string;
|
|
33
|
+
}
|
|
24
34
|
interface ChartTrade {
|
|
25
35
|
id: string;
|
|
26
36
|
entryTime: number;
|
|
@@ -177,8 +187,15 @@ interface ChartComponentProps {
|
|
|
177
187
|
* view. Pairs with `selectedTradeId` to drive both selection and focus.
|
|
178
188
|
*/
|
|
179
189
|
focusTradeId?: string | null;
|
|
190
|
+
/**
|
|
191
|
+
* IANA timezone (e.g. "America/New_York") for the axis ticks + crosshair
|
|
192
|
+
* labels. Omit to use the viewer's local timezone.
|
|
193
|
+
*/
|
|
194
|
+
timeZone?: string;
|
|
195
|
+
/** Shaded horizontal price bands (e.g. an MFE↔MAE excursion zone). */
|
|
196
|
+
priceBands?: PriceBand[];
|
|
180
197
|
}
|
|
181
|
-
declare function ChartComponent({ bars, onLoadMoreData, indicators, lines, onBarUpdate, onNewBar, onDeleteLine, onAddLine, onClearAllLines, enableBarSelection, onBarClick, trades, selectedTradeId, renderTradePopup, height, focusTradeId, }: ChartComponentProps): react_jsx_runtime.JSX.Element;
|
|
198
|
+
declare function ChartComponent({ bars, onLoadMoreData, indicators, lines, onBarUpdate, onNewBar, onDeleteLine, onAddLine, onClearAllLines, enableBarSelection, onBarClick, trades, selectedTradeId, renderTradePopup, height, focusTradeId, timeZone, priceBands, }: ChartComponentProps): react_jsx_runtime.JSX.Element;
|
|
182
199
|
|
|
183
200
|
interface IndicatorBrowserProps {
|
|
184
201
|
isOpen: boolean;
|
|
@@ -445,4 +462,4 @@ declare function getOldestBar(bars: OHLCVBar[]): OHLCVBar | null;
|
|
|
445
462
|
declare function getNewestBar(bars: OHLCVBar[]): OHLCVBar | null;
|
|
446
463
|
declare function updateCurrentBar(bars: OHLCVBar[], tradePrice: number, tradeVolume: number): OHLCVBar[];
|
|
447
464
|
|
|
448
|
-
export { ADXIndicator, ATRIndicator, AlpacaBarAdapter, type BarDataAdapter, type BarDataAdapterOptions, BollingerBandsIndicator, CCIIndicator, ChartComponent, type ChartLine, ChartSeriesType, type ChartTrade, DonchianChannelsIndicator, EMAIndicator, ForceIndexIndicator, type HistoricalDataParams, IchimokuIndicator, IndicatorBrowser, type IndicatorCalculation, IndicatorCategory, type IndicatorDefinition, type IndicatorInstance, IndicatorInstanceSchema, type IndicatorMetadata, IndicatorMetadataSchema, type IndicatorOutput, type IndicatorPanel, type IndicatorSettings$1 as IndicatorSettings, IndicatorSettingsForm, IndicatorSettingsSchema, KeltnerChannelsIndicator, type LineStyle, LineStyleSchema, LocalStoragePersistenceAdapter, MACDIndicator, MFIIndicator, MockAdapter, NoOpPersistenceAdapter, OBVIndicator, type OHLCVBar, PSARIndicator, type PersistenceAdapter, ROCIndicator, RSIIndicator, type RealtimeHandlers, type RealtimeSubscription, type RenderConfig, RenderConfigSchema, SMAIndicator, type SettingField, SettingFieldSchema, type SettingFieldType, SettingFieldTypeSchema, SettingsDialog, StochRSIIndicator, StochasticIndicator, SuperTrendIndicator, VWAPIndicator, WMAIndicator, WilliamsRIndicator, appendBar, calculateBollingerBands, calculateEMA, calculateRSI, calculateSMA, calculateStandardDeviation, createPersistenceAdapter, deduplicateBars, displaceArray, getNewestBar, getOldestBar, indicatorCalculator, indicatorRegistry, isValidBar, mergeBars, normalizeTimestamp, padIndicatorArray, prependBars, registerBuiltInIndicators, sortBars, updateBarInArray, updateCurrentBar, useBarsData, useChartAPI, useRealtimeUpdates, validateAndNormalizeBars, validateBar };
|
|
465
|
+
export { ADXIndicator, ATRIndicator, AlpacaBarAdapter, type BarDataAdapter, type BarDataAdapterOptions, BollingerBandsIndicator, CCIIndicator, ChartComponent, type ChartLine, ChartSeriesType, type ChartTrade, DonchianChannelsIndicator, EMAIndicator, ForceIndexIndicator, type HistoricalDataParams, IchimokuIndicator, IndicatorBrowser, type IndicatorCalculation, IndicatorCategory, type IndicatorDefinition, type IndicatorInstance, IndicatorInstanceSchema, type IndicatorMetadata, IndicatorMetadataSchema, type IndicatorOutput, type IndicatorPanel, type IndicatorSettings$1 as IndicatorSettings, IndicatorSettingsForm, IndicatorSettingsSchema, KeltnerChannelsIndicator, type LineStyle, LineStyleSchema, LocalStoragePersistenceAdapter, MACDIndicator, MFIIndicator, MockAdapter, NoOpPersistenceAdapter, OBVIndicator, type OHLCVBar, PSARIndicator, type PersistenceAdapter, type PriceBand, ROCIndicator, RSIIndicator, type RealtimeHandlers, type RealtimeSubscription, type RenderConfig, RenderConfigSchema, SMAIndicator, type SettingField, SettingFieldSchema, type SettingFieldType, SettingFieldTypeSchema, SettingsDialog, StochRSIIndicator, StochasticIndicator, SuperTrendIndicator, VWAPIndicator, WMAIndicator, WilliamsRIndicator, appendBar, calculateBollingerBands, calculateEMA, calculateRSI, calculateSMA, calculateStandardDeviation, createPersistenceAdapter, deduplicateBars, displaceArray, getNewestBar, getOldestBar, indicatorCalculator, indicatorRegistry, isValidBar, mergeBars, normalizeTimestamp, padIndicatorArray, prependBars, registerBuiltInIndicators, sortBars, updateBarInArray, updateCurrentBar, useBarsData, useChartAPI, useRealtimeUpdates, validateAndNormalizeBars, validateBar };
|
package/dist/index.js
CHANGED
|
@@ -260,13 +260,11 @@ var BandsPrimitive = class {
|
|
|
260
260
|
// src/components/trade-markers.ts
|
|
261
261
|
var WIN = "#10b981";
|
|
262
262
|
var LOSS = "#ef4444";
|
|
263
|
-
var SEL = "#3b82f6";
|
|
264
263
|
function buildTradeMarkers(trades, selectedTradeId) {
|
|
265
264
|
const markers = [];
|
|
266
265
|
for (const t of trades) {
|
|
267
266
|
const selected = t.id === selectedTradeId;
|
|
268
|
-
const
|
|
269
|
-
const color = selected ? SEL : base;
|
|
267
|
+
const color = t.outcome === "win" ? WIN : LOSS;
|
|
270
268
|
markers.push({
|
|
271
269
|
time: t.entryTime / 1e3,
|
|
272
270
|
position: "belowBar",
|
|
@@ -301,7 +299,9 @@ function ChartComponent({
|
|
|
301
299
|
selectedTradeId = null,
|
|
302
300
|
renderTradePopup,
|
|
303
301
|
height,
|
|
304
|
-
focusTradeId = null
|
|
302
|
+
focusTradeId = null,
|
|
303
|
+
timeZone,
|
|
304
|
+
priceBands = []
|
|
305
305
|
}) {
|
|
306
306
|
const chartContainerRef = useRef(null);
|
|
307
307
|
const chartRef = useRef(null);
|
|
@@ -315,6 +315,7 @@ function ChartComponent({
|
|
|
315
315
|
const [isLoadingMore, setIsLoadingMore] = useState(false);
|
|
316
316
|
const [contextMenu, setContextMenu] = useState(null);
|
|
317
317
|
const [linePositions, setLinePositions] = useState(/* @__PURE__ */ new Map());
|
|
318
|
+
const [bandRects, setBandRects] = useState([]);
|
|
318
319
|
const [selectedBar, setSelectedBar] = useState(null);
|
|
319
320
|
const selectedBarRef = useRef(null);
|
|
320
321
|
const [spotlightPosition, setSpotlightPosition] = useState(null);
|
|
@@ -346,6 +347,12 @@ function ChartComponent({
|
|
|
346
347
|
vertLines: { color: "#1e293b" },
|
|
347
348
|
horzLines: { color: "#1e293b" }
|
|
348
349
|
},
|
|
350
|
+
// Render axis ticks + crosshair in `timeZone` (default: the viewer's local
|
|
351
|
+
// timezone). lightweight-charts otherwise renders numeric times as UTC,
|
|
352
|
+
// which mismatches local/exchange-time labels alongside the chart.
|
|
353
|
+
localization: {
|
|
354
|
+
timeFormatter: makeCrosshairTimeFormatter(timeZone)
|
|
355
|
+
},
|
|
349
356
|
width: chartContainerRef.current.clientWidth,
|
|
350
357
|
// Auto-fill the container's height unless an explicit `height` is given.
|
|
351
358
|
// Fall back to 600 only when the container hasn't been laid out yet (a
|
|
@@ -354,7 +361,8 @@ function ChartComponent({
|
|
|
354
361
|
timeScale: {
|
|
355
362
|
timeVisible: true,
|
|
356
363
|
secondsVisible: true,
|
|
357
|
-
borderColor: "#334155"
|
|
364
|
+
borderColor: "#334155",
|
|
365
|
+
tickMarkFormatter: makeTickMarkFormatter(timeZone)
|
|
358
366
|
},
|
|
359
367
|
rightPriceScale: {
|
|
360
368
|
borderColor: "#334155"
|
|
@@ -680,6 +688,36 @@ function ChartComponent({
|
|
|
680
688
|
window.removeEventListener("resize", updatePositions);
|
|
681
689
|
};
|
|
682
690
|
}, [lines]);
|
|
691
|
+
useEffect(() => {
|
|
692
|
+
const series = candlestickSeriesRef.current;
|
|
693
|
+
if (!chartRef.current || !series) return;
|
|
694
|
+
if (priceBands.length === 0) {
|
|
695
|
+
setBandRects([]);
|
|
696
|
+
return;
|
|
697
|
+
}
|
|
698
|
+
const recompute = () => {
|
|
699
|
+
if (!candlestickSeriesRef.current) return;
|
|
700
|
+
const rects = [];
|
|
701
|
+
for (const band of priceBands) {
|
|
702
|
+
const yTop = candlestickSeriesRef.current.priceToCoordinate(band.top);
|
|
703
|
+
const yBottom = candlestickSeriesRef.current.priceToCoordinate(band.bottom);
|
|
704
|
+
if (yTop == null || yBottom == null) continue;
|
|
705
|
+
const top = Math.min(yTop, yBottom);
|
|
706
|
+
const height2 = Math.abs(yBottom - yTop);
|
|
707
|
+
rects.push({ id: band.id, top, height: height2, color: band.color });
|
|
708
|
+
}
|
|
709
|
+
setBandRects(rects);
|
|
710
|
+
};
|
|
711
|
+
const raf = requestAnimationFrame(recompute);
|
|
712
|
+
const timeScale = chartRef.current.timeScale();
|
|
713
|
+
timeScale.subscribeVisibleLogicalRangeChange(recompute);
|
|
714
|
+
window.addEventListener("resize", recompute);
|
|
715
|
+
return () => {
|
|
716
|
+
cancelAnimationFrame(raf);
|
|
717
|
+
chartRef.current?.timeScale().unsubscribeVisibleLogicalRangeChange(recompute);
|
|
718
|
+
window.removeEventListener("resize", recompute);
|
|
719
|
+
};
|
|
720
|
+
}, [priceBands]);
|
|
683
721
|
useEffect(() => {
|
|
684
722
|
if (!chartRef.current) return;
|
|
685
723
|
indicatorSeriesRef.current.forEach((series) => {
|
|
@@ -880,6 +918,14 @@ function ChartComponent({
|
|
|
880
918
|
isLoadingMore && /* @__PURE__ */ jsx("div", { className: "absolute top-4 left-1/2 transform -translate-x-1/2 bg-slate-800 text-slate-200 px-4 py-2 rounded-lg shadow-lg z-10", children: "Loading more data..." }),
|
|
881
919
|
/* @__PURE__ */ jsxs("div", { className: "relative", children: [
|
|
882
920
|
/* @__PURE__ */ jsx("div", { ref: chartContainerRef, className: "w-full" }),
|
|
921
|
+
bandRects.map((b) => /* @__PURE__ */ jsx(
|
|
922
|
+
"div",
|
|
923
|
+
{
|
|
924
|
+
className: "absolute left-0 right-0 pointer-events-none z-4",
|
|
925
|
+
style: { top: `${b.top}px`, height: `${b.height}px`, background: b.color }
|
|
926
|
+
},
|
|
927
|
+
b.id
|
|
928
|
+
)),
|
|
883
929
|
enableBarSelection && spotlightPosition && /* @__PURE__ */ jsx(
|
|
884
930
|
"div",
|
|
885
931
|
{
|
|
@@ -1007,6 +1053,39 @@ function getLineStyle(style) {
|
|
|
1007
1053
|
return LineStyle.Solid;
|
|
1008
1054
|
}
|
|
1009
1055
|
}
|
|
1056
|
+
function makeTickMarkFormatter(timeZone) {
|
|
1057
|
+
const time = new Intl.DateTimeFormat("en-US", { timeZone, hour: "2-digit", minute: "2-digit", hourCycle: "h23" });
|
|
1058
|
+
const timeSec = new Intl.DateTimeFormat("en-US", { timeZone, hour: "2-digit", minute: "2-digit", second: "2-digit", hourCycle: "h23" });
|
|
1059
|
+
const month = new Intl.DateTimeFormat("en-US", { timeZone, month: "short" });
|
|
1060
|
+
const day = new Intl.DateTimeFormat("en-US", { timeZone, month: "short", day: "numeric" });
|
|
1061
|
+
const year = new Intl.DateTimeFormat("en-US", { timeZone, year: "numeric" });
|
|
1062
|
+
return (t, tickMarkType) => {
|
|
1063
|
+
const d = new Date(t * 1e3);
|
|
1064
|
+
switch (tickMarkType) {
|
|
1065
|
+
case 0:
|
|
1066
|
+
return year.format(d);
|
|
1067
|
+
case 1:
|
|
1068
|
+
return month.format(d);
|
|
1069
|
+
case 2:
|
|
1070
|
+
return day.format(d);
|
|
1071
|
+
case 4:
|
|
1072
|
+
return timeSec.format(d);
|
|
1073
|
+
default:
|
|
1074
|
+
return time.format(d);
|
|
1075
|
+
}
|
|
1076
|
+
};
|
|
1077
|
+
}
|
|
1078
|
+
function makeCrosshairTimeFormatter(timeZone) {
|
|
1079
|
+
const fmt = new Intl.DateTimeFormat("en-US", {
|
|
1080
|
+
timeZone,
|
|
1081
|
+
month: "short",
|
|
1082
|
+
day: "2-digit",
|
|
1083
|
+
hour: "2-digit",
|
|
1084
|
+
minute: "2-digit",
|
|
1085
|
+
hourCycle: "h23"
|
|
1086
|
+
});
|
|
1087
|
+
return (t) => fmt.format(new Date(t * 1e3));
|
|
1088
|
+
}
|
|
1010
1089
|
var IndicatorCategory = /* @__PURE__ */ ((IndicatorCategory2) => {
|
|
1011
1090
|
IndicatorCategory2["TREND"] = "Trend";
|
|
1012
1091
|
IndicatorCategory2["MOMENTUM"] = "Momentum";
|