60초 튜토리얼
아직 npm에 발행 전이다. 지금은 저장소 루트 README의
pnpm pack절차를 쓴다.
bash
# 아래 예제가 @finchart/indicators까지 쓴다 — 셋을 같이 넣는다.
pnpm add @finchart/core @finchart/dom @finchart/indicators@finchart/core와 @finchart/dom은 한 버전으로 같이 나간다 — 그리는 법 (시리즈·스케일·상태)은 코어에, 브라우저에 붙이는 배선 (PlotBuilder·browserDeps)은 @finchart/dom에 산다.
ts
/**
* 60초 예제의 실물 — getting-started.md가 이 파일을 그대로 임베드하고,
* `pnpm --filter charts-docs type-check`가 컴파일을 지킨다. 예제가
* 낡으면 CI가 잡는다 (cold-consumer 2026-08-15 마찰 #1의 재발 방지).
* 루트 README의 "60초" 절과 같은 코드다 — 그쪽은 GitHub 렌더라 임베드가
* 안 되므로, 바꿀 때 둘을 같이 바꾼다.
*/
import { candleSeries } from "@finchart/core";
import { PlotBuilder, browserDeps } from "@finchart/dom";
const plot = PlotBuilder.create(browserDeps(), candleSeries())
.addDataPoints([
{ x: 0, open: 100, high: 108, low: 98, close: 106 },
{ x: 1, open: 106, high: 112, low: 104, close: 109 },
{ x: 2, open: 109, high: 111, low: 101, close: 103 },
])
.setSize(800, 400)
.build(document.getElementById("chart")!);
export { plot };끝이다. 드래그하면 움직이고 휠로 확대된다.
데이터는 x 오름차순이어야 한다. 아니면
DataError가 난다.
다음 걸음 — 거래량 pane · 이동평균 · 실시간
위 예제는 시리즈를 빌더에 넘긴다. 실서비스는 보통 한 걸음 더 간다:
ts
/**
* **60초 다음 걸음의 실물** — 캔들 + 거래량 pane + 이동평균 + 실시간.
* getting-started.md가 이 파일을 그대로 임베드하고,
* `pnpm --filter charts-docs type-check`가 컴파일을 지킨다.
*
* 36차 콜드 컨슈머 #6·#7이 이 파일을 만들게 했다: React는
* `packages/react/README.md`에 완결 예제가 있는데 바닐라는 없어서,
* 소비자가 60초 예제 + `plot-contract.md` 단편 + `d.ts`의 JSDoc을
* 스스로 종합해야 했다. 그 종합이 여기 있다.
*/
import { candleSeries, histogramSeries, priceFormat, timeTicks } from "@finchart/core";
import type { OHLC } from "@finchart/core";
import { PlotBuilder, browserDeps } from "@finchart/dom";
import { attachMovingAverage } from "@finchart/indicators";
declare const bars: OHLC[]; // 네가 들고 오는 것 — x 오름차순
const plot = PlotBuilder.create<OHLC>(browserDeps({ autoSize: true }))
.setSize(900, 480)
.setAxis({
x: { ticks: timeTicks({ locale: "ko" }) },
y: { position: "right", format: priceFormat({ compact: true, locale: "ko" }) },
})
.build(document.getElementById("chart")!);
/**
* **시리즈는 빌더가 아니라 pane에 얹는다.**
*
* 빌더에 시리즈를 넘겨도(`PlotBuilder.create(deps, candleSeries())`) 되지만
* 그러면 **손잡이를 못 받는다** — 그리고 그 손잡이가 실시간 갱신의 문이자
* 지표의 입력이다. 손잡이가 필요하면 빌더는 무대만 세우고, 시리즈는 이렇게 얹는다.
*/
const price = plot.mainPane.addSeries({
series: candleSeries(),
data: bars,
name: "가격",
});
// 이동평균은 가격 pane 위에 겹친다 — 손잡이가 그대로 입력이 된다.
plot.mainPane.use(attachMovingAverage({ source: price, period: 20 }));
// 거래량은 자기 pane. 높이는 flex가 정한다 — 픽셀 산술을 하지 않는다.
const volumePane = plot.addPane({ flex: 0.25, minHeight: 48 });
const volume = volumePane.addSeries({
series: histogramSeries(),
data: bars.map((bar) => ({ x: bar.x, y: bar.volume ?? 0 })),
name: "거래량",
});
/**
* 실시간 — **틱과 새 봉이 같은 함수다.**
*
* `updateLast`는 마지막 점과 x가 같으면 교체하고 크면 붙인다. 그래서
* "진행 중인 봉이 갱신됐나 / 새 봉이 열렸나"를 소비자가 판별하지 않는다.
* 도메인도 안 건드리므로 사용자가 과거를 보고 있는 중이라면 화면이
* 튀지 않는다. 지표는 손잡이를 물고 있어서 저절로 따라온다.
*/
export function onTick(bar: OHLC): void {
price.updateLast(bar);
volume.updateLast({ x: bar.x, y: bar.volume ?? 0 });
}
export { plot };바뀐 것 하나가 나머지를 끌고 온다 — 시리즈를 빌더가 아니라 pane.addSeries()로 얹는다. 그래야 손잡이를 받고, 그 손잡이가 실시간 갱신의 문(updateLast)이자 지표의 입력(source)이다. 둘이 같은 물건이라, 시리즈를 얹는 순간 나머지가 다 열린다.
지표를 더 얹거나 계산을 여러 그림이 나눠 쓰는 법은 @finchart/indicators, React로 같은 차트를 짓는 법은 @finchart/react의 60초 예제에 있다.
크기가 궁금하면
이 예제는 ≈16 KB brotli로 번들된다. 안 쓰는 확장·시리즈·틱 전략은 import하지 않는 한 0 B고(라인 하나만 headless로 쓰면 748 B), 프리셋이 싣는 것을 조각 단위로 고르고 싶으면 명시 배선으로 내려간다 — 같은 차트가 45 KB min으로 선다. pointer: false 같은 옵션은 동작을 끄는 문이다. 바이트의 문은 import다.