Back to Community
TA-Lib Candle Patterns: Help Wanted

Some of you have probably realised I like TA-Lib. And I like ta-lib. And I'm extremely happy that Quantopian have incorporated the latter to provide access to the former.

I'm now looking at the candle pattern functions and to understand them better I'm spending a bit of time in the 'C' source for TA-Lib which is here:

TA-Lib has been pretty stable since 2005 or so and has been incorporated in a lot of places including commercial Forex platforms and now in Quantopian.

What do I do if I think I have found a problem with one of the candle functions? I don't want to make a fool of myself so I would like someone to check something with me.

My issue may be with CDLCLOSINGMARUBOZU which from it's name should be finding two candle patterns:

(i) "The closing black marubozu is a tall black candlestick with an upper shadow but no lower one". See:

(ii) "The closing white marubozu candlestick is a tall white candle with no upper shadow, but it does have a leg to stand on (a lower shadow)". See:

So we are talking about A) here:

When I run an algo with CDLCLOSINGMARUBOZU I get quite a lot of bullish signals and quite a lot of of bearish signals. But if I look at the OHLC prices for a bullish one I see this:

O = 9.8
H = 10.03
L = 9.76
C = 10.01

Now C > O so this candle has a real white body. H > C so there is an upper shadow. L < O so there is a lower shadow. So this is not a Closing White Marubozu or an Opening White Marubozu.

If I add some code:

        Indicator  = ztt.talib.CDLCLOSINGMARUBOZU(OpenPrices, HighPrices, LowPrices, ClosePrices)  
        if Indicator[-1:] == 100 and LowPrices[-1:] >= OpenPrices[-1:]:  
            context.PatternCount +=1  
            print Indicator  

Then my first match is:

O = 22.45
H = 24.49
L = 22.45
C = 24.45

which is a white candle with an upper shadow and no lower shadow. So this is an Opening White Marubozu!

If I change this to:

        Indicator  = ztt.talib.CDLCLOSINGMARUBOZU(OpenPrices, HighPrices, LowPrices, ClosePrices)  
        if Indicator[-1:] == 100 and HighPrices[-1:] <= ClosePrices[-1:]:  
            context.PatternCount +=1  
            print Indicator  

I get:

O = 105.56
H = 108.2
L = 105.32
C = 108.2

which is a real white body, no upper shadow and a lower shadow i.e. this is a Closing White Marubozu!

This is part of the 'C' source:

/* Proceed with the calculation for the requested range.
    * Must have:  
    * - long white (black) real body  
    * - no or very short upper (lower) shadow  
    * The meaning of "long" and "very short" is specified with TA_SetCandleSettings  
    * outInteger is positive (1 to 100) when white (bullish), negative (-1 to -100) when black (bearish)  
   outIdx = 0;  
        if( TA_REALBODY(i) > TA_CANDLEAVERAGE( BodyLong, BodyLongPeriodTotal, i ) &&             // long body  
              ( // white body and very short lower shadow  
                TA_CANDLECOLOR(i) == 1 &&  
                TA_UPPERSHADOW(i) < TA_CANDLEAVERAGE( ShadowVeryShort, ShadowVeryShortPeriodTotal, i )  
              ) ||  
              ( // black body and very short upper shadow  
                TA_CANDLECOLOR(i) == -1 &&  
                TA_LOWERSHADOW(i) < TA_CANDLEAVERAGE( ShadowVeryShort, ShadowVeryShortPeriodTotal, i )  
            ) )  
            outInteger[outIdx++] = TA_CANDLECOLOR(i) * 100;  
            outInteger[outIdx++] = 0;  
        /* add the current range and subtract the first range: this is done after the pattern recognition  
         * when avgPeriod is not 0, that means "compare with the previous candles" (it excludes the current candle)  
        BodyLongPeriodTotal += TA_CANDLERANGE( BodyLong, i ) - TA_CANDLERANGE( BodyLong, BodyLongTrailingIdx );  
        ShadowVeryShortPeriodTotal += TA_CANDLERANGE( ShadowVeryShort, i )  
                                    - TA_CANDLERANGE( ShadowVeryShort, ShadowVeryShortTrailingIdx );  
   } while( i <= endIdx );  

Have I got this all wrong?


Clone Algorithm
Backtest from to with initial capital
Total Returns
Max Drawdown
Benchmark Returns
Returns 1 Month 3 Month 6 Month 12 Month
Alpha 1 Month 3 Month 6 Month 12 Month
Beta 1 Month 3 Month 6 Month 12 Month
Sharpe 1 Month 3 Month 6 Month 12 Month
Sortino 1 Month 3 Month 6 Month 12 Month
Volatility 1 Month 3 Month 6 Month 12 Month
Max Drawdown 1 Month 3 Month 6 Month 12 Month
import numpy as np
import zipline.transforms.ta as ztt

def initialize(context):
    set_universe(universe.DollarVolumeUniverse(floor_percentile=90.0, ceiling_percentile=100.0))
    context.PatternCount = 0
def handle_data(context, data):
    Results = GetPrices(data)
    if Results is None:
    for Sid in data:
        if Sid.sid not in Results[0].columns:
        OpenPrices  = np.array(Results[0][Sid])
        HighPrices  = np.array(Results[1][Sid])
        LowPrices   = np.array(Results[2][Sid])
        ClosePrices = np.array(Results[3][Sid])
        Indicator  = ztt.talib.CDLCLOSINGMARUBOZU(OpenPrices, HighPrices, LowPrices, ClosePrices)
        if Indicator[-1:] == 100 and LowPrices[-1:] >= OpenPrices[-1:]:
            context.PatternCount +=1
            print Indicator
            print OpenPrices[-1:],HighPrices[-1:],LowPrices[-1:],ClosePrices[-1:]
    print context.PatternCount
@batch_transform(window_length=11, refresh_period=0)
def GetPrices(DataPanel):
    OpenPrices  = DataPanel['open_price']
    HighPrices  = DataPanel['high']
    LowPrices   = DataPanel['low']
    ClosePrices = DataPanel['close_price']
    return OpenPrices, HighPrices, LowPrices, ClosePrices

This backtest was created using an older version of the backtester. Please re-run this backtest to see results using the latest backtester. Learn more about the recent changes.
There was a runtime error.
1 response

I have examined the TA Lib source before although I can't say I completely understand all of it. It appears that CDLCLOSINGMARUBOZU follows two rules. The candle color and a "no or very short" shadow.

The algorithm does not follow a strict definition that the side with "no shadow" have a length of zero (i.e. Close == High or Close == Low). It appears there is no requirements for shadow on the other side of the candle. The algorithm can end up qualifying Closing White Marubozus where the upper shadow > lower shadow as long as TA_UPPERSHADOW < TA_CANDLEAVERAGE.

If we follow TA-Libs definition for a "no or very short" shadow. For your first example it is debateable whether it is a Closing White Marubozu or not depending on how one defines the required length of the lower shadow. For the second example the the lower shadow has a length of zero but there appears to be no test for this in CDLCLOSINGMARUBOZU so the algorthm qualifies it as a Closing White Marubozu anyways.