A modern, modular JavaScript library for candlestick pattern detection. Detects classic reversal and continuation patterns in OHLC data, with a clean API and no native dependencies.
✨ Highlights:
validateOHLC, validateOHLCArray)npm install candlestick
const { isHammer, hammer, patternChain, allPatterns } = require("candlestick");
// Check single candle (small body in upper third, long lower shadow, tiny upper shadow)
const candle = { open: 14, high: 15, low: 8, close: 14.5 };
console.log(isHammer(candle)); // true
// Find patterns in series
const candles = [
/* array of OHLC objects */
];
console.log(hammer(candles)); // [indices where pattern found]
// Detect all patterns at once
const results = patternChain(candles, allPatterns);
console.log(results); // [{ index, pattern, match }]
import { isHammer, hammer, patternChain, allPatterns } from "candlestick";
const candles = [
/* array of OHLC objects */
];
const results = patternChain(candles, allPatterns);
console.log(results);
import { OHLC, PatternMatch, patternChain, allPatterns } from "candlestick";
const candles: OHLC[] = [
{ open: 10, high: 15, low: 8, close: 12 },
{ open: 12, high: 16, low: 11, close: 14 },
];
const results: PatternMatch[] = patternChain(candles, allPatterns);
// Full IntelliSense support ✓
CommonJS (Node.js):
// Import all patterns
const candlestick = require("candlestick");
// Or import only what you need
const { isHammer, hammer, patternChain } = require("candlestick");
ESM (Modern JavaScript):
// Import all patterns
import candlestick from "candlestick";
// Or import only what you need (recommended for tree-shaking)
import { isHammer, hammer, patternChain } from "candlestick";
All functions expect objects with at least:
{
open: Number,
high: Number,
low: Number,
close: Number
}
Extra fields (date, volume, etc.) are preserved unchanged and passed through to every match result, so you can attach any metadata you need:
const data = [
{
date: "2024-01-06",
open: 41490,
high: 41500,
low: 39200,
close: 41500,
volume: 61000,
},
// ...
];
const results = patternChain(data, allPatterns);
console.log(results[0].match[0].date); // "2024-01-06"
console.log(results[0].match[0].volume); // 61000
Single candle:
isHammer(candle) / isBullishHammer(candle) / isBearishHammer(candle)isInvertedHammer(candle) / isBullishInvertedHammer(candle) / isBearishInvertedHammer(candle)isDoji(candle)isMarubozu(candle) / isBullishMarubozu(candle) / isBearishMarubozu(candle)isSpinningTop(candle) / isBullishSpinningTop(candle) / isBearishSpinningTop(candle)Two candles:
isBullishEngulfing(prev, curr) / isBearishEngulfing(prev, curr)isBullishHarami(prev, curr) / isBearishHarami(prev, curr)isBullishKicker(prev, curr) / isBearishKicker(prev, curr)isHangingMan(prev, curr) / isShootingStar(prev, curr)isPiercingLine(prev, curr) / isDarkCloudCover(prev, curr)isTweezers(prev, curr) / isTweezersTop(prev, curr) / isTweezersBottom(prev, curr)Three candles:
isMorningStar(c1, c2, c3) / isEveningStar(c1, c2, c3)isThreeWhiteSoldiers(c1, c2, c3) / isThreeBlackCrows(c1, c2, c3)Single candle:
hammer(dataArray) / bullishHammer(dataArray) / bearishHammer(dataArray)invertedHammer(dataArray) / bullishInvertedHammer(dataArray) / bearishInvertedHammer(dataArray)doji(dataArray)marubozu(dataArray) / bullishMarubozu(dataArray) / bearishMarubozu(dataArray)spinningTop(dataArray) / bullishSpinningTop(dataArray) / bearishSpinningTop(dataArray)Two candles:
bullishEngulfing(dataArray) / bearishEngulfing(dataArray)bullishHarami(dataArray) / bearishHarami(dataArray)bullishKicker(dataArray) / bearishKicker(dataArray)hangingMan(dataArray) / shootingStar(dataArray)piercingLine(dataArray) / darkCloudCover(dataArray)tweezers(dataArray) / tweezersTop(dataArray) / tweezersBottom(dataArray)Three candles:
morningStar(dataArray) / eveningStar(dataArray)threeWhiteSoldiers(dataArray) / threeBlackCrows(dataArray)All array functions return an array of indices where the pattern occurs.
Scan a series for multiple patterns in one pass:
const { patternChain, allPatterns } = require("candlestick");
const matches = patternChain(dataArray, allPatterns);
// matches: [
// { index: 3, pattern: 'hammer', match: [candleObj] },
// { index: 7, pattern: 'bullishEngulfing', match: [candleObj, candleObj] },
// ...
// ]
You can also pass a custom list of patterns:
const { patternChain, doji, bullishEngulfing } = require("candlestick");
const matches = patternChain(dataArray, [
{ name: "doji", fn: doji },
{ name: "bullishEngulfing", fn: bullishEngulfing, paramCount: 2 },
]);
Pass { strict: true } to throw on invalid OHLC data instead of silently skipping:
patternChain(dataArray, allPatterns, { strict: true });
// throws if any candle has high < low, NaN fields, etc.
Multi-candle patterns: Two-candle patterns (Engulfing, Harami, Kicker, Hanging Man, Shooting Star, Piercing Line, Dark Cloud Cover, Tweezers Top/Bottom) return a
matcharray with 2 candles. Three-candle patterns (Morning Star, Evening Star, Three White Soldiers, Three Black Crows) return 3. Single-candle patterns return 1. This is driven by theparamCountproperty on each pattern definition.
Note: The library does not mutate your input data. Pattern functions return arrays of indices;
precomputeCandlePropsreturns new enriched candle objects. If you call individual pattern series functions (e.g.,hammer(),doji()) multiple times on the same raw array, precompute once for better performance (see below). When usingpatternChain, precomputation is handled internally and no manual call is needed.
When calling multiple pattern functions on the same dataset, use `precomputeCandleProp
$ claude mcp add candlestick \
-- python -m otcore.mcp_server <graph>