A modern, modular JavaScript library for candlestick pattern detection. Detects classic reversal and continuation patterns in OHLC (Open, High, Low, Close) price data, with a clean API and no native dependencies.
- 📊 18 candlestick patterns, 29 variants across single, two, and three-candle formations
- 📦 ESM & CommonJS dual export with full TypeScript definitions
- 🌊 Streaming API for massive datasets (~70% memory reduction)
- 🔌 Plugin system for custom patterns, data validation, pattern metadata
- ✅ Comprehensive test suite with high coverage (run
npm testandnpm run coverage) - 🪶 Zero runtime dependencies
Requires Node.js >= 20
- Quick Start
- Usage
- Pattern Detection Functions
- High-Level Pattern Chaining
- Examples
- Full Example Files
- Performance
- Development
- Architecture
- Contributing
- Upgrading from v1.x
- FAQ
- Changelog
- Roadmap
- Code of Conduct
- License
npm install candlestickconst { 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 = [
{ open: 14, high: 15, low: 8, close: 14.5 },
{ open: 13, high: 18, low: 13, close: 13.2 },
{ open: 12, high: 12.5, low: 7, close: 12.1 },
];
console.log(hammer(candles)); // [ 0, 2 ]
// 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 = [
{ open: 14, high: 15, low: 8, close: 14.5 },
{ open: 13, high: 18, low: 13, close: 13.2 },
{ open: 12, high: 12.5, low: 7, close: 12.1 },
];
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); // 61000Every pattern has two API styles: a boolean function for checking individual candles, and an array function that scans a series and returns matching indices.
Boolean (Single/Pair) Detection — returns boolean
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)
Array (Series) Detection — returns number[] (indices)
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)
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.
The library detects 18 patterns across 29 variants:
| Category | Patterns |
|---|---|
| Single candle | Hammer, Inverted Hammer, Doji, Marubozu, Spinning Top |
| Two candle | Engulfing, Harami, Kicker, Hanging Man, Shooting Star, Piercing Line, Dark Cloud Cover, Tweezers Top/Bottom |
| Three candle | Morning Star, Evening Star, Three White Soldiers, Three Black Crows |
Each pattern includes bullish/bearish variants where applicable. For detailed descriptions with detection thresholds, see docs/PATTERNS.md.
Note: The library does not mutate your input data. Pattern functions return arrays of indices;
precomputeCandlePropsreturns new enriched candle objects. When calling multiple pattern functions on the same raw array, precompute once for better performance (see Performance).patternChainhandles this internally.
const { isBullishKicker, isBearishKicker } = require("candlestick");
// Bullish candle, then bearish candle gapping down → bearish kicker
const prev = { open: 40, high: 41, low: 39.5, close: 40.8 };
const curr = { open: 39.5, high: 39.8, low: 38.5, close: 38.9 };
console.log(isBullishKicker(prev, curr)); // false
console.log(isBearishKicker(prev, curr)); // trueconst { shootingStar } = require("candlestick");
const data = [
{ open: 29.01, high: 29.03, low: 28.56, close: 28.64 },
// ...
];
console.log(shootingStar(data)); // [index, ...]const { patternChain, allPatterns } = require("candlestick");
const matches = patternChain(data, allPatterns);
console.log(matches);
// [ { index: 3, pattern: 'hammer', match: [Object] }, ... ]For processing very large datasets efficiently with reduced memory usage:
const { streaming } = require("candlestick");
// Option 1: Using createStream with callbacks
const stream = streaming.createStream({
patterns: ["hammer", "doji", "marubozu"],
chunkSize: 1000,
onMatch: (match) => console.log(match),
enrichMetadata: true,
});
// Process data in chunks
for (const chunk of dataChunks) {
stream.process(chunk);
}
stream.end();
// Option 2: Simple helper for large datasets
const results = streaming.processLargeDataset(largeData, {
patterns: null, // all patterns
chunkSize: 1000,
enrichMetadata: true,
});Benefits: Reduces memory usage by ~70% for datasets > 100K candles
const { validateOHLC, validateOHLCArray } = require("candlestick").utils;
// Validate single candle
try {
validateOHLC({ open: 10, high: 15, low: 8, close: 12 });
console.log("Valid candle ✓");
} catch (error) {
console.error("Invalid:", error.message);
}
// Validate array of candles
validateOHLCArray(candles); // throws on invalid dataconst { plugins, patternChain } = require('candlestick');
// Register custom pattern
plugins.registerPattern({
name: 'myCustomPattern',
fn: (dataArray) => {
return dataArray
.map((c, i) => (c.close > c.open && c.close === c.high) ? i : -1)
.filter(idx => idx !== -1);
},
paramCount: 1,
metadata: { type: 'reversal', confidence: 0.85 }
});
// Use with patternChain
const customPattern = plugins.getPattern('myCustomPattern');
const results = patternChain(data, [customPattern]);For more details on the plugin system, see docs/PLUGIN_API.md.
Detect patterns from command line:
# Install globally
npm install -g candlestick
# Detect patterns in JSON file
candlestick -i data.json --output table --metadata
# Filter by confidence
candlestick -i data.csv --confidence 0.85 --output csv
# Bullish reversals only
candlestick -i data.json --type reversal --direction bullish
# Use with pipes
cat data.json | candlestick --output tableFor complete CLI documentation, see docs/CLI_GUIDE.md.
See the examples/ directory for runnable, copy-pasteable usage of every pattern and utility:
Single Candle Patterns:
examples/hammer.js— Hammer pattern detectionexamples/invertedHammer.js— Inverted Hammer pattern detectionexamples/doji.js— Doji pattern detection
Two Candle Patterns:
examples/engulfing.js— Engulfing pattern detectionexamples/harami.js— Harami pattern detectionexamples/kicker.js— Kicker pattern detectionexamples/reversal.js— Hanging Man and Shooting Star
Multi-Pattern Detection:
examples/patternChain.js— Multi-pattern detection with patternChainexamples/newPatterns.js— Morning/Evening Star, Three Soldiers/Crows, Piercing Line, Dark Cloud Coverexamples/newPatternsV2.js— Marubozu, Spinning Top, Tweezersexamples/streaming.js— Streaming API for large datasetsexamples/esm-example.mjs— ESM module syntax exampleexamples/metadata.js— Pattern metadata, filtering, and sorting
Utilities:
examples/utils.js— Utility functions: bodyLen, wickLen, tailLen, isBullish, isBearish, hasGapUp, hasGapDown, findPatternexamples/real-data.js— Real market data with date/volume fields, precomputeCandleProps, gap detection, and frequency breakdown
See examples/README.md for more details and instructions.
| Dataset Size | Pattern Chain (ms) | Throughput (candles/sec) | Memory (MB) |
|---|---|---|---|
| 1,000 | 7.0 | 142K | 1.7 |
| 10,000 | 29.9 | 334K | 16.6 |
| 100,000 | 294.7 | 339K | 114.8 |
| 1,000,000 | 2288.8 | 437K | 667.5 |
When calling multiple pattern functions on the same dataset, use precomputeCandleProps to avoid redundant work:
const { hammer, doji, utils } = require("candlestick");
const precomputed = utils.precomputeCandleProps(data);
const hammers = hammer(precomputed);
const dojis = doji(precomputed);patternChain handles this internally — no manual call needed there.
Run npm run bench for full benchmark results on your hardware. The numbers above were measured on the maintainer's machine and may vary.
npm test # run tests
npm run test:watch # watch mode
npm run coverage # coverage report (c8)
npm run lint # eslint
npm run format # prettier
npm run bench # benchmark suiteSee docs/ARCHITECTURE.md for an overview of the library's design and module structure.
- Please open issues or pull requests for bugs, features, or questions.
- Add tests for new patterns or utilities.
- Follow the code style enforced by ESLint and Prettier.
- Run
npm run lintandnpm run formatbefore submitting. - See CONTRIBUTING.md for full guidelines.
- Create
src/myPattern.jswith a boolean detector (isMyPattern) and an array scanner (myPattern) - Export both from
src/candlestick.jsandsrc/index.mjs - Add TypeScript definitions in
types/index.d.ts - Register the pattern in
allPatternsinsidesrc/patternChain.js(setparamCountto the number of candles) - Write tests in
test/myPattern.test.jscovering valid matches, non-matches, and edge cases - Add an example file in
examples/myPattern.js - Run
npm test && npm run lintto verify
v2.0.0 is a breaking release. Required changes:
-
Node.js >= 20 required. Node 18 reached EOL on 2025-04-30 and is no longer supported. Update your runtime and CI matrix.
-
Error cause chain in
validateOHLCArray. Re-thrown errors now include{ cause: originalError }. If you inspect error objects (e.g.,error instanceofchecks orerror.messageparsing), be aware that the original error is now available viaerror.cause.
No API changes — all pattern functions, exports, and types remain the same.
Q: Why is my pattern not detected?
Ensure your candle objects have all required fields (open, high, low, close). Check that the pattern's technical thresholds are met (see Pattern Descriptions). The library does not check for trend context (e.g., uptrend/downtrend) — it only looks at candle shapes.
Q: Does this work in the browser?
The core library is pure JavaScript with no Node.js-specific APIs, so it works in any bundler (webpack, Vite, esbuild, etc.). The candlestick/cli subpath is Node-only and is excluded from browser builds automatically via the "node" export condition.
Q: Does this library mutate my data?
No. All computations are done on copies; your input data is never changed.
Q: Can I use this with TypeScript?
Yes. The library includes complete TypeScript definitions in types/index.d.ts. Full type safety and IntelliSense support available.
Q: How do I add a custom pattern?
Use the plugin system — call plugins.registerPattern() with your detection function, then pass it to patternChain. See the Plugin System example or docs/PLUGIN_API.md.
Q: What's the performance with 1M candles?
See the Performance table for current numbers. Run npm run bench to measure on your own hardware.
Q: Are there visual examples of patterns?
Not yet, but this is planned (see ROADMAP.md). For now, see the Pattern Descriptions section.
See CHANGELOG.md for full release history.
See ROADMAP.md for planned features and future directions.
See CODE_OF_CONDUCT.md for community standards and enforcement.
MIT. See LICENSE.