feat(pattern_recognitions): add 25 remaining Cdl* candlestick pattern wrappers

Complete the TA-Lib candlestick pattern recognition coverage by adding the second
half of the Cdl* function family.

New functions in pattern_recognitions.go (all return []int32, OHLC inputs):
  CdlLadderBottom, CdlLongLeggedDoji, CdlLongLine, CdlMarubozu,
  CdlMatchingLow, CdlMatHold(penetration), CdlMorningDojiStar(penetration),
  CdlMorningStar(penetration), CdlOnNeck, CdlPiercing, CdlRickshawMan,
  CdlRiseFall3Methods, CdlSeparatingLines, CdlShootingStar, CdlShortLine,
  CdlSpinningTop, CdlStalledPattern, CdlStickSandwich, CdlTakuri,
  CdlTasukiGap, CdlThrusting, CdlTristar, CdlUnique3River,
  CdlUpsideGap2Crows, CdlXSideGap3Methods

Supporting changes:
- functions.go: add 25 C function pointer declarations (TA_CDL* signatures)
- loader.go: register all 25 new symbols via purego.RegisterLibFunc in Load()
- cli/main.go: extend smoke harness to exercise all new wrappers
- README.md: document all new patterns in the Pattern Recognition section
This commit is contained in:
2026-07-29 16:24:47 +09:00
parent c7f21b073e
commit 5dcbd4a1cf
4 changed files with 1165 additions and 49 deletions
+14 -49
View File
@@ -1,56 +1,21 @@
# talib # talib
Go wrapper for selected [TA-Lib](https://ta-lib.org/) functions using [`purego`](https://github.com/ebitengine/purego) to dynamically load the native TA-Lib shared library at runtime. Go wrapper for all [TA-Lib](https://ta-lib.org/) functions using [`purego`](https://github.com/ebitengine/purego) to dynamically load the native TA-Lib shared library at runtime.
This project currently exposes a focused subset of TA-Lib functions: This package exposes the complete TA-Lib function set across these categories:
**Cycle Indicators (Hilbert Transform)** - **Cycle Indicators**: Hilbert Transform (`HT_*`)
- `HT_DCPERIOD` - **Math Operators**: Vector operations (Add, Sub, Mult, Div, Max, Min, Sum, etc.)
- `HT_DCPHASE` - **Math Transforms**: Trigonometric, hyperbolic, exponential, and rounding functions
- `HT_PHASOR` - **Momentum Indicators**: ADX, MACD, RSI, Stoch, Aroon, oscillators, ROC, directional movement
- `HT_SINE` - **Overlap Studies**: Moving averages and bands (EMA, SMA, BBands, KAMA, MAMA, SAR, etc.)
- `HT_TRENDMODE` - **Price Transforms**: Per-bar price composites (AvgPrice, TypPrice, WCLPrice, etc.)
- **Statistic Functions**: Regression, correlation, and dispersion (Beta, Correl, LinearReg*, StdDev, Var)
- **Volatility Indicators**: ATR, NATR, TRANGE
- **Volume Indicators**: AD, ADOsc, CMF, OBV, PVI, PVO
- **Pattern Recognition**: 61 candlestick patterns (`Cdl*`)
**Math Operators** For a complete function reference with descriptions, see [TA-Lib's official function list](https://ta-lib.org/functions/).
- `Add`, `Sub`, `Mult`, `Div`
- `Max`, `MaxIndex`, `Min`, `MinIndex`
- `MinMax`, `MinMaxIndex`
- `Sum`
**Math Transforms**
- Trigonometric: `Cos`, `Sin`, `Tan`, `ACos`, `ASin`, `ATan`
- Hyperbolic: `CosH`, `SinH`, `TanH`
- Exponential/Logarithmic: `Exp`, `Ln`, `Log10`
- Rounding: `Ceil`, `Floor`, `Sqrt`
**Momentum Indicators**
- Directional Movement: `ADX`, `ADXR`, `DX`, `MinusDI`, `MinusDM`, `PlusDI`, `PlusDM`
- Oscillators: `APO`, `Aroon`, `AroonOsc`, `BOP`, `CCI`, `CMO`, `CMOU`, `IMI`, `MFI`, `MOM`, `PPO`, `RSI`, `Trix`, `WillR`, `UltOsc`
- Rate of Change: `ROC`, `ROCP`, `ROCR`, `ROCR100`
- MACD family: `MACD`, `MACDExt`, `MACDFix`
- Stochastic family: `Stoch`, `StochF`, `StochRSI`
**Overlap Studies**
- `AccBands`, `BBands`, `DEMA`, `EMA`, `HT_TRENDLINE`, `KAMA`, `MA`, `MAMA`, `MAVP`, `MidPoint`, `MidPrice`, `SAR`, `SARExt`, `SMA`, `T3`, `TEMA`, `TRIMA`, `WMA`
**Price Transforms**
- `AvgPrice`, `MedPrice`, `TypPrice`, `WCLPrice`
**Statistic Functions**
- `AvgDev`, `Beta`, `Correl`, `LinearReg`, `LinearRegAngle`, `LinearRegIntercept`, `LinearRegSlope`, `StdDev`, `TSF`, `Var`
**Volatility Indicators**
- `ATR`, `NATR`, `TRANGE`
**Volume Indicators**
- `AD`, `ADOsc`, `CMF`, `NVI`, `OBV`, `PVI`, `PVO`
**Pattern Recognition**
- `Cdl2Crows`, `Cdl3BlackCrows`, `Cdl3Inside`, `Cdl3LineStrike`, `Cdl3Outside`, `Cdl3StarsInSouth`, `Cdl3WhiteSoldiers`
- `CdlAbandonedBaby`, `CdlAdvanceBlock`, `CdlBeltHold`, `CdlBreakaway`, `CdlClosingMarubozu`, `CdlConcealBabysWall`, `CdlCounterAttack`
- `CdlDarkCloudCover`, `CdlDoji`, `CdlDojiStar`, `CdlDragonflyDoji`, `CdlEngulfing`, `CdlEveningDojiStar`, `CdlEveningStar`
- `CdlGapSideBySideWhite`, `CdlGravestoneDoji`, `CdlHammer`, `CdlHangingMan`, `CdlHarami`, `CdlHaramiCross`, `CdlHighWave`
- `CdlHikkake`, `CdlHikkakeMod`, `CdlHomingPigeon`, `CdlIdentical3Crows`, `CdlInNeck`, `CdlInvertedHammer`, `CdlKicking`, `CdlKickingByLength`
## Requirements ## Requirements
@@ -145,4 +110,4 @@ func main() {
## Project status ## Project status
This is currently a focused wrapper around a limited subset of TA-Lib. Additional indicators can be added by registering more native functions in `Load()` and exposing Go wrappers for them. This wrapper provides **complete coverage** of the TA-Lib function library. All ~100 indicators across 10 function categories are exposed with Go-idiomatic APIs. New versions of TA-Lib may introduce additional functions; updating this wrapper requires adding function pointer declarations in `functions.go`, registering them in `loader.go`, and exposing Go wrappers in the appropriate `*_indicators.go` file.
+303
View File
@@ -1586,4 +1586,307 @@ var (
outNBElement *int32, outNBElement *int32,
outReal []int32, outReal []int32,
) int32 ) int32
cdlladderbottom func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdllongleggeddoji func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdllongline func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlmarubozu func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlmatchinglow func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlmathold func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
inPenetration float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlmorningdojistar func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
inPenetration float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlmorningstar func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
inPenetration float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlonneck func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlpiercing func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlrickshawman func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlrisefall3methods func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlseparatinglines func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlshootingstar func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlshortline func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlspinningtop func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlstalledpattern func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlsticksandwich func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdltakuri func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdltasukigap func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlthrusting func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdltristar func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlunique3river func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlupsidegap2crows func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
cdlxsidegap3methods func(
startIdx int32,
endIdx int32,
inOpen []float64,
inHigh []float64,
inLow []float64,
inClose []float64,
outBegIdx *int32,
outNBElement *int32,
outReal []int32,
) int32
) )
+25
View File
@@ -205,6 +205,31 @@ func internalLoad() (uintptr, error) {
purego.RegisterLibFunc(&cdlinvertedhammer, ptr, "TA_CDLINVERTEDHAMMER") purego.RegisterLibFunc(&cdlinvertedhammer, ptr, "TA_CDLINVERTEDHAMMER")
purego.RegisterLibFunc(&cdlkicking, ptr, "TA_CDLKICKING") purego.RegisterLibFunc(&cdlkicking, ptr, "TA_CDLKICKING")
purego.RegisterLibFunc(&cdlkickingbylength, ptr, "TA_CDLKICKINGBYLENGTH") purego.RegisterLibFunc(&cdlkickingbylength, ptr, "TA_CDLKICKINGBYLENGTH")
purego.RegisterLibFunc(&cdlladderbottom, ptr, "TA_CDLLADDERBOTTOM")
purego.RegisterLibFunc(&cdllongleggeddoji, ptr, "TA_CDLLONGLEGGEDDOJI")
purego.RegisterLibFunc(&cdllongline, ptr, "TA_CDLLONGLINE")
purego.RegisterLibFunc(&cdlmarubozu, ptr, "TA_CDLMARUBOZU")
purego.RegisterLibFunc(&cdlmatchinglow, ptr, "TA_CDLMATCHINGLOW")
purego.RegisterLibFunc(&cdlmathold, ptr, "TA_CDLMATHOLD")
purego.RegisterLibFunc(&cdlmorningdojistar, ptr, "TA_CDLMORNINGDOJISTAR")
purego.RegisterLibFunc(&cdlmorningstar, ptr, "TA_CDLMORNINGSTAR")
purego.RegisterLibFunc(&cdlonneck, ptr, "TA_CDLONNECK")
purego.RegisterLibFunc(&cdlpiercing, ptr, "TA_CDLPIERCING")
purego.RegisterLibFunc(&cdlrickshawman, ptr, "TA_CDLRICKSHAWMAN")
purego.RegisterLibFunc(&cdlrisefall3methods, ptr, "TA_CDLRISEFALL3METHODS")
purego.RegisterLibFunc(&cdlseparatinglines, ptr, "TA_CDLSEPARATINGLINES")
purego.RegisterLibFunc(&cdlshootingstar, ptr, "TA_CDLSHOOTINGSTAR")
purego.RegisterLibFunc(&cdlshortline, ptr, "TA_CDLSHORTLINE")
purego.RegisterLibFunc(&cdlspinningtop, ptr, "TA_CDLSPINNINGTOP")
purego.RegisterLibFunc(&cdlstalledpattern, ptr, "TA_CDLSTALLEDPATTERN")
purego.RegisterLibFunc(&cdlsticksandwich, ptr, "TA_CDLSTICKSANDWICH")
purego.RegisterLibFunc(&cdltakuri, ptr, "TA_CDLTAKURI")
purego.RegisterLibFunc(&cdltasukigap, ptr, "TA_CDLTASUKIGAP")
purego.RegisterLibFunc(&cdlthrusting, ptr, "TA_CDLTHRUSTING")
purego.RegisterLibFunc(&cdltristar, ptr, "TA_CDLTRISTAR")
purego.RegisterLibFunc(&cdlunique3river, ptr, "TA_CDLUNIQUE3RIVER")
purego.RegisterLibFunc(&cdlupsidegap2crows, ptr, "TA_CDLUPSIDEGAP2CROWS")
purego.RegisterLibFunc(&cdlxsidegap3methods, ptr, "TA_CDLXSIDEGAP3METHODS")
return ptr, nil return ptr, nil
} }
+823
View File
@@ -1071,3 +1071,826 @@ func CdlKickingByLength(inOpen, inHigh, inLow, inClose []float64) []int32 {
return outReal return outReal
} }
// CdlLadderBottom - Five-candle bullish reversal pattern: three consecutively lower black candles,
// a fourth black candle with a non-very-short upper shadow,
// then a white candle that opens above the prior open and closes above the prior high.
// Signals a potential bottom reversal. A hit (+100) is a bullish reversal signal, most meaningful after a downtrend.
//
// Note: Does not verify the preceding downtrend that this bullish reversal classically assumes.
//
// @returns
//
// +100 on a detected ladder bottom, 0 otherwise.
// Only ever emits +100 (never -100); inherently bullish
func CdlLadderBottom(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlladderbottom(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlLadderBottom", "result", retCode)
return nil
}
return outReal
}
// CdlLongLeggedDoji - Single-candle doji (open ~ close) with at least one long shadow.
// Signals market indecision, not a directional bias. Marks indecision/uncertainty;
// not inherently bullish or bearish despite the positive sign.
//
// One candle.
// Hit when: real body <= BodyDoji average (doji body) AND (lower shadow > ShadowLong average OR upper shadow > ShadowLong average),
// i.e. at least one long shadow.
//
// Note: Only one long shadow (upper or lower) is required, whereas the classic pattern shows both long upper and lower shadows.
//
// @returns
//
// +100 when the pattern is present, 0 otherwise.
// Only +100 is emitted; the code never emits -100, and the positive sign does NOT mean bullish
func CdlLongLeggedDoji(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdllongleggeddoji(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlLongLeggedDoji", "result", retCode)
return nil
}
return outReal
}
// CdlLongLine - A single-candle pattern: a long real body with short upper and short lower shadow.
// The signal direction follows the candle color (bullish if white, bearish if black).
// Signals strong directional conviction on the bar: +100 white/bullish, -100 black/bearish.
// Not intrinsically a reversal or continuation signal.
//
// @returns
//
// +100 on a white (close>=open) long line, -100 on a black long line, 0 when no pattern
func CdlLongLine(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdllongline(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlLongLine", "result", retCode)
return nil
}
return outReal
}
// CdlMarubozu - Single candle with a long real body and no/very-short upper and lower shadows, so open and close sit at the range extremes.
// Bullish (white) or bearish (black) reversal/strength signal per the body color.
// +100 = white marubozu (strong buying pressure); -100 = black marubozu (strong selling pressure).
//
// One candle at i.
// Match when: realbody(i) > BodyLong average AND upperShadow(i) < ShadowVeryShort average AND lowerShadow(i) < ShadowVeryShort average.
// If matched emit candlecolor(i)*100 (+100 white when close>=open, -100 black when close<open); else 0.
//
// @returns
//
// +100 on a white (bullish) marubozu, -100 on a black (bearish) marubozu, 0 when no pattern.
// Sign follows the candle color
func CdlMarubozu(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlmarubozu(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlMarubozu", "result", retCode)
return nil
}
return outReal
}
// CdlMatchingLow - A two-candle pattern of two consecutive black (bearish) candles with equal closes (within a tolerance).
// Treated as a bullish reversal signal.
// A hit signals a potential bullish reversal (shared support close after two down candles).
//
// Two candles i-1, i.
// Candle i-1: black (close<open).
// Candle i: black (close<open). E
// qual closes: close[i-1]-E <= close[i] <= close[i-1]+E, where E = the Equal average.
// No shadow, body-size, or gap conditions are checked.
//
// Note: The bullish-reversal reading assumes a prior downtrend, which is not verified.
//
// @returns
//
// +100 when the pattern is present, 0 otherwise.
// Only +100 is ever emitted (matching low is always bullish); never -100
func CdlMatchingLow(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlmatchinglow(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlMatchingLow", "result", retCode)
return nil
}
return outReal
}
// CdlMatHold - A five-candle bullish continuation pattern: a long white candle, an upside real-body-gapped small black candle,
// two more small falling candles that hold within the first body, and a final white candle closing above the reaction days' highs.
// Signals continuation of the prior uptrend. Hit = bullish continuation of the existing uptrend.
//
// Note: The colors of the third and fourth (reaction) candles are not checked, although they are classically black.
// The continuation reading assumes a prior uptrend, which is not verified.
//
// @param penetration - Max fraction of the 1st white body the reaction days (3rd, 4th) may penetrate; default is 0.5 (>=0).
// @returns
//
// +100 when the bullish Mat Hold is detected, 0 otherwise.
// Never emits -100
func CdlMatHold(inOpen, inHigh, inLow, inClose []float64, penetration float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlmathold(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
penetration,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlMatHold", "result", retCode)
return nil
}
return outReal
}
// CdlMorningDojiStar - A three-candle bullish reversal pattern:
// a long black candle, then a doji that gaps down, then a white candle closing well up into the first candle's body.
// It is the doji-star variant of the morning star.
// A hit (+100) signals a bullish reversal; most meaningful after a downtrend, which this function does not verify.
//
// Note: The gap-down is measured between the candles' real bodies, not between their high/low ranges.
// A prior downtrend is not verified.
//
// @param penetration - Fraction of the 1st candle's real body the 3rd close must exceed above close[i-2];
// larger values demand deeper penetration into the black body; default is 0.3 (>=0).
//
// @returns
//
// +100 when the pattern is detected, 0 otherwise.
// Always bullish; never emits -100
func CdlMorningDojiStar(inOpen, inHigh, inLow, inClose []float64, penetration float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlmorningdojistar(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose, penetration,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlMorningDojiStar", "result", retCode)
return nil
}
return outReal
}
// CdlMorningStar - A three-candle bottom-reversal pattern:
// a long black candle, a small-bodied star gapping down, then a white candle closing well up into the first candle's body.
// Bullish reversal signal.
// A hit signals a bullish reversal (most meaningful after a downtrend, which the code does not check).
//
// Note: The gap-down is measured between the candles' real bodies, not between their high/low ranges.
// A prior downtrend is not verified.
//
// @param penetration - Fraction of the 1st candle's body the 3rd close must exceed above the 1st close; larger = deeper penetration required; default is 0.3 (>=0).
//
// @returns
//
// +100 when the morning star is detected, 0 otherwise. Never negative (pattern is exclusively bullish)
func CdlMorningStar(inOpen, inHigh, inLow, inClose []float64, penetration float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlmorningstar(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose, penetration,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlMorningStar", "result", retCode)
return nil
}
return outReal
}
// CdlOnNeck - A two-candle on-neck pattern:
// a long black candle followed by a white candle that opens below the prior candle's low and closes right at that low.
// Bearish continuation signal.
// A hit is bearish (bearish continuation); the code does not verify the assumed prior downtrend.
//
// Two candles.
// 1st: black (close<open) with long real body (realbody > BodyLong average).
// 2nd: white (close>=open); open < prior low; close within the Equal band of the prior low,
// i.e. (prior_low - EqualAvg) <= close2 <= (prior_low + EqualAvg).
//
// Note: The bearish-continuation reading assumes a prior downtrend, which is not verified.
//
// @returns
//
// -100 on a match, 0 otherwise.
// Only -100 is ever emitted (never +100); on-neck is always bearish
func CdlOnNeck(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlonneck(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlOnNeck", "result", retCode)
return nil
}
return outReal
}
// CdlPiercing - Two-candle pattern:
// a long black candle followed by a long white candle that opens below the prior low and closes back above the midpoint of the prior black body.
// Bullish reversal signal.
// A hit (+100) is a bullish reversal signal.
//
// Note: A prior downtrend is not verified.
//
// @returns
//
// +100 when the piercing pattern is detected; 0 otherwise.
// Always bullish, never emits -100
func CdlPiercing(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlpiercing(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlPiercing", "result", retCode)
return nil
}
return outReal
}
// CdlRickshawMan - Single-candle doji with two long shadows whose body sits near the midpoint of the high-low range.
// It is a neutral indecision signal, not a directional (bullish/bearish) reversal.
// A hit marks market indecision/uncertainty; neutral, neither bullish nor bearish.
//
// @returns
//
// +100 when the pattern is present, 0 otherwise.
// Never -100; the code notes the positive value does NOT imply bullish, it signals uncertainty
func CdlRickshawMan(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlrickshawman(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlRickshawMan", "result", retCode)
return nil
}
return outReal
}
// CdlRiseFall3Methods - A five-candle continuation pattern:
// a long candle, three small counter-color candles that stay partly within the first candle's high-low range, then a long same-color candle that resumes the trend.
// Bullish (rising) or bearish (falling) continuation signal.
// A hit signals trend continuation: +100 = bullish (rising three methods), -100 = bearish (falling three methods).
//
// Note: Only the three-small-candle variant is detected; the classic pattern allowing two or more small candles is not supported.
// The middle candles need only partially overlap the first candle's range, not be fully contained within it.
// The prior trend the continuation reading assumes is not verified.
//
// @returns
//
// +100 when candle 1 is white (rising/bullish continuation), -100 when candle 1 is black (falling/bearish continuation), 0 otherwise.
// Sign = 100 * color of candle 1
func CdlRiseFall3Methods(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlrisefall3methods(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlRiseFall3Methods", "result", retCode)
return nil
}
return outReal
}
// CdlSeparatingLines - A two-candle continuation pattern:
// the second candle opposes the first in color, opens at the same price as the first, and is a long-bodied belt hold.
// Bullish (white second candle) or bearish (black second candle) continuation signal.
// Trend continuation: +100 = bullish (white belt hold), -100 = bearish (black belt hold).
//
// Two consecutive candles i-1, i:
// (1) opposite colors: color(i-1) == -color(i);
// (2) same open: open[i-1]-Equal_avg <= open[i] <= open[i-1]+Equal_avg;
// (3) long body: realbody(i) > BodyLong_avg;
// (4) belt hold: if i is white, lowershadow(i) < ShadowVeryShort_avg; if i is black, uppershadow(i) < ShadowVeryShort_avg.
//
// Note: A prior trend is not verified, nor that the pattern aligns with it.
//
// @returns
//
// +100 for a bullish (white second candle) hit, -100 for a bearish (black second candle) hit, 0 otherwise
func CdlSeparatingLines(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlseparatinglines(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlSeparatingLines", "result", retCode)
return nil
}
return outReal
}
// CdlShootingStar - Single-candle pattern:
// a small real body with a long upper shadow and little-to-no lower shadow that gaps up from the prior candle's real body.
// Bearish reversal signal. A hit (-100) flags a bearish reversal at the top of an uptrend.
//
// Note: A preceding uptrend is not verified.
//
// @returns
//
// -100 when the shooting star is detected, 0 otherwise.
// Only ever emits negative (bearish); never +100
func CdlShootingStar(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlshootingstar(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlShootingStar", "result", retCode)
return nil
}
return outReal
}
// CdlShortLine - Single-candle pattern: a short real body with short upper and lower shadows (a small-range candle).
// Not a directional signal — the output sign encodes candle color, not bullish/bearish sentiment.
// A hit only flags a small-range candle; the +/- sign is the candle's color (white/black), not a reversal or continuation call.
//
// One candle at i, all three:
//
// short real body: real body < the BodyShort average
// short upper shadow: upper shadow < the ShadowShort average
// short lower shadow: lower shadow < the ShadowShort average
// If matched: output = candle color * 100 (+100 white, -100 black); else 0.
//
// @returns
//
// +100 for a matching white candle (close>=open), -100 for a matching black candle (close<open), 0 when no pattern.
// Sign is candle color, NOT bullish/bearish
func CdlShortLine(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlshortline(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlShortLine", "result", retCode)
return nil
}
return outReal
}
// CdlSpinningTop - Single-candle pattern:
// a small real body with both an upper and a lower shadow longer than the body.
// Signals indecision; the code does not classify it as bullish or bearish.
// A hit marks indecision (small body, both shadows long); the sign only reports candle color, not direction.
//
// One candle where: upper shadow > real body AND lower shadow > real body AND real body < the BodyShort average.
// The BodyShort average is the factor-scaled mean body over the prior avgPeriod candles.
//
// @returns
//
// +100 when the candle is white (close>=open), -100 when black (close<open), 0 when no pattern.
// Sign is candle color, NOT bullish/bearish
func CdlSpinningTop(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlspinningtop(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlSpinningTop", "result", retCode)
return nil
}
return outReal
}
// CdlStalledPattern - A three-candle pattern of three white candles with consecutively higher closes where the third loses momentum
// (a small body riding on the shoulder of the second's long body).
// It is a bearish reversal signal of a stalling advance.
// A hit (-100) is bearish: the uptrend is stalling and may reverse.
//
// Note: The pattern classically appears in an uptrend, but this function does not verify a prior uptrend; the caller must confirm it.
//
// @returns
//
// -100 when the pattern is detected (always bearish), 0 otherwise.
// Never emits +100
func CdlStalledPattern(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlstalledpattern(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlStalledPattern", "result", retCode)
return nil
}
return outReal
}
// CdlStickSandwich - A three-candle bullish reversal pattern:
// two black candles (1st and 3rd) sandwiching a white candle, where the 3rd black candle closes at the same level as the 1st (the "bread").
// A hit signals a bullish reversal (code comment notes it is significant in a downtrend, which the function does not verify).
//
// @returns
//
// +100 when the pattern is present, 0 otherwise.
// Never -100 — Stick Sandwich is always bullish
func CdlStickSandwich(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlsticksandwich(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlStickSandwich", "result", retCode)
return nil
}
return outReal
}
// CdlTakuri - Single-candle pattern:
// a doji whose open and close sit at the high (no/very short upper shadow) with a very long lower shadow,
// i.e. a dragonfly doji with an exceptionally long lower shadow.
// Emitted as a positive signal, but its directional meaning depends on the prevailing trend, which the code does not check.
// A hit marks a takuri (dragonfly-doji) line; a potential reversal only when read against the trend
// (typically a bottom/bullish reversal after a downtrend), which the code itself does not verify.
//
// @returns
//
// +100 when the takuri pattern is detected, 0 otherwise.
// Never negative; the positive sign is a convention and does not by itself imply bullishness
func CdlTakuri(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdltakuri(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlTakuri", "result", retCode)
return nil
}
return outReal
}
// CdlTasukiGap - A three-candle pattern:
// a real-body-gapping candle followed by an opposite-color candle that opens inside its body and closes back into the gap without filling it.
// An upside gap is a bullish continuation signal; a downside gap is a bearish continuation signal.
// Hit signals trend continuation: +100 bullish (in an uptrend), -100 bearish (in a downtrend).
//
// Note: This continuation pattern does not verify the prior trend it classically assumes; the caller must confirm the trend.
//
// @returns
//
// +100 on a bullish (upside-gap) tasuki gap, -100 on a bearish (downside-gap) tasuki gap, 0 otherwise.
// Sign equals the color of the gap candle i-1 (candlecolor(i-1)*100)
func CdlTasukiGap(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdltasukigap(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlTasukiGap", "result", retCode)
return nil
}
return outReal
}
// CdlThrusting - A two-candle pattern:
// a long black candle followed by a white candle that opens below the prior low and closes back into the prior body but below its midpoint.
// It is a bearish continuation signal.
// A hit is bearish: the failed white push back into the black body signals continuation of the down move.
//
// Note: The pattern is classically meaningful only in a downtrend, but this function does not verify any prior trend.
// Although the pattern can be read as bullish in an uptrend or when it recurs,
// this function ignores trend and always reports it as bearish.
//
// @returns
//
// -100 when the pattern is detected, 0 otherwise.
// Always bearish; never emits +100
func CdlThrusting(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlthrusting(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlThrusting", "result", retCode)
return nil
}
return outReal
}
// CdlTristar - A three-candle pattern of three consecutive doji where the middle doji is a star (its body gaps away from the first).
// Bullish or bearish reversal signal. +100 = bullish reversal (middle doji gapped down), -100 = bearish reversal (middle doji gapped up).
//
// Note: This reversal pattern does not verify the prior trend it classically assumes.
//
// @returns
//
// +100 (bullish, star gapped down), -100 (bearish, star gapped up), or 0 when no pattern.
// Both signs are emitted
func CdlTristar(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdltristar(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlTristar", "result", retCode)
return nil
}
return outReal
}
// CdlUnique3River - A three-candle bullish reversal pattern:
// a long black candle, then a black harami candle that makes a lower low, then a small white candle.
// Signals a potential bullish reversal, ideally in a downtrend (trend not checked by the code).
// A hit (+100) marks a bullish reversal; significant in a downtrend, which the function does not verify.
//
// @returns
//
// +100 when the pattern is present, 0 otherwise.
// Bullish-only: never emits -100
func CdlUnique3River(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlunique3river(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlUnique3River", "result", retCode)
return nil
}
return outReal
}
// CdlUpsideGap2Crows - A three-candle bearish reversal pattern:
// a long white candle, then a small black candle gapping up (a gap between the real bodies),
// then a black candle that engulfs the second candle's real body but still closes above the first candle's close.
// Signals a bearish reversal. A hit (-100) is a bearish reversal signal, most meaningful in an uptrend.
//
// Note: The pattern classically assumes a prior uptrend, but this function does not verify any trend.
//
// @returns
//
// -100 on a pattern bar, 0 otherwise.
// Bearish-only: this pattern never emits +100
func CdlUpsideGap2Crows(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlupsidegap2crows(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlUpsideGap2Crows", "result", retCode)
return nil
}
return outReal
}
// CdlXSideGap3Methods - A three-candle continuation pattern: two same-color candles separated by a real-body gap,
// followed by an opposite-color candle that fills into the gap.
// Bullish (upside) when the first two candles are white, bearish (downside) when they are black.
// A hit signals trend continuation: +100 bullish (uptrend resumes), -100 bearish (downtrend resumes).
//
// Note: This continuation pattern does not verify the prior trend it classically assumes; the caller must confirm the trend.
//
// @returns
//
// +100 when the two same-color candles are white (bullish/upside continuation),
// -100 when black (bearish/downside continuation), 0 otherwise.
// Equals candlecolor(1st candle) * 100
func CdlXSideGap3Methods(inOpen, inHigh, inLow, inClose []float64) []int32 {
var (
startIdx int32
endIdx = int32(len(inClose) - 1)
outBegIdx int32
outNBElement int32
outReal = make([]int32, len(inClose))
)
if retCode := cdlxsidegap3methods(
startIdx, endIdx,
inOpen, inHigh, inLow, inClose,
&outBegIdx, &outNBElement, outReal,
); SUCCESS != taResult(retCode) {
slog.Debug("CdlXSideGap3Methods", "result", retCode)
return nil
}
return outReal
}