/* TA-LIB Copyright (c) 1999-2026, Mario Fortier * All rights reserved. * * Redistribution and use in source and binary forms, with or * without modification, are permitted provided that the following * conditions are met: * * - Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * * - Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in * the documentation and/or other materials provided with the * distribution. * * - Neither name of author nor the names of its contributors * may be used to endorse or promote products derived from this * software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ /* AUTO-GENERATED by ta_codegen — DO NOT EDIT. * Source of truth: ta_codegen/input// (regenerate: cd ta_codegen/generator && cargo run -- generate) */ #include #include #include "ta_func.h" #include "ta_utility.h" #include "ta_memory.h" #include "ta_func_stream_private.h" /* List of contributors: * * Initial Name/description * ------------------------------------------------------------------- * AC Angelo Ciceri * MF Mario Fortier * CC Claude Code (AI assistant) * * * Change history: * * MMDDYY BY Description * ------------------------------------------------------------------- * 120404 AC Creation * 100426 MF,CC Remove the outInteger comment, which misstated the values */ TA_LIB_API int TA_CDL3WHITESOLDIERS_Lookback( void ) { int BodyShort_avgPeriod = TA_Globals->candleSettings[TA_BodyShort].avgPeriod; int Far_avgPeriod = TA_Globals->candleSettings[TA_Far].avgPeriod; int Near_avgPeriod = TA_Globals->candleSettings[TA_Near].avgPeriod; int ShadowVeryShort_avgPeriod = TA_Globals->candleSettings[TA_ShadowVeryShort].avgPeriod; return max(max(ShadowVeryShort_avgPeriod,BodyShort_avgPeriod),max(Far_avgPeriod,Near_avgPeriod)) + 2; } TA_LIB_API int TA_CDL3WHITESOLDIERS_DisplayShift( int outputIdx ) { if( outputIdx < 0 || outputIdx >= 1 ) return INT_MIN; return 0; } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS( int startIdx, int endIdx, const double inOpen[], const double inHigh[], const double inLow[], const double inClose[], int *outBegIdx, int *outNBElement, int outInteger[] ) { double ShadowVeryShortPeriodTotal[3]; double NearPeriodTotal[3]; double FarPeriodTotal[3]; double BodyShortPeriodTotal; int i; int outIdx; int totIdx; int ShadowVeryShortTrailingIdx; int NearTrailingIdx; int FarTrailingIdx; int BodyShortTrailingIdx; int lookbackTotal; int BodyShort_avgPeriod = TA_Globals->candleSettings[TA_BodyShort].avgPeriod; int Far_avgPeriod = TA_Globals->candleSettings[TA_Far].avgPeriod; int Near_avgPeriod = TA_Globals->candleSettings[TA_Near].avgPeriod; int ShadowVeryShort_avgPeriod = TA_Globals->candleSettings[TA_ShadowVeryShort].avgPeriod; if( (startIdx < 0) || (startIdx > TA_INDEX_MAX) ) return TA_OUT_OF_RANGE_START_INDEX; if( (endIdx < 0) || (endIdx > TA_INDEX_MAX) || (endIdx < startIdx) ) return TA_OUT_OF_RANGE_END_INDEX; if( !inOpen ) return TA_BAD_PARAM; if( !inHigh ) return TA_BAD_PARAM; if( !inLow ) return TA_BAD_PARAM; if( !inClose ) return TA_BAD_PARAM; if( !outBegIdx || !outNBElement ) return TA_BAD_PARAM; if( !outInteger ) return TA_BAD_PARAM; /* Identify the minimum number of price bar needed * to calculate at least one output. */ lookbackTotal = TA_CDL3WHITESOLDIERS_Lookback(); /* Move up the start index if there is not * enough initial data. */ if( startIdx < lookbackTotal ) { startIdx = lookbackTotal; } /* Make sure there is still something to evaluate. */ if( startIdx > endIdx ) { *outBegIdx= 0; *outNBElement= 0; return TA_SUCCESS; } /* Do the calculation using tight loops. */ /* Add-up the initial period, except for the last value. */ ShadowVeryShortPeriodTotal[2] = 0; ShadowVeryShortPeriodTotal[1] = 0; ShadowVeryShortPeriodTotal[0] = 0; ShadowVeryShortTrailingIdx = startIdx - ShadowVeryShort_avgPeriod; NearPeriodTotal[2] = 0; NearPeriodTotal[1] = 0; NearPeriodTotal[0] = 0; NearTrailingIdx = startIdx - Near_avgPeriod; FarPeriodTotal[2] = 0; FarPeriodTotal[1] = 0; FarPeriodTotal[0] = 0; FarTrailingIdx = startIdx - Far_avgPeriod; BodyShortPeriodTotal = 0; BodyShortTrailingIdx = startIdx - BodyShort_avgPeriod; i = ShadowVeryShortTrailingIdx; while( i < startIdx ) { ShadowVeryShortPeriodTotal[2] = ShadowVeryShortPeriodTotal[2] + TA_CANDLERANGE(ShadowVeryShort,i - 2); ShadowVeryShortPeriodTotal[1] = ShadowVeryShortPeriodTotal[1] + TA_CANDLERANGE(ShadowVeryShort,i - 1); ShadowVeryShortPeriodTotal[0] = ShadowVeryShortPeriodTotal[0] + TA_CANDLERANGE(ShadowVeryShort,i); i += 1; } i = NearTrailingIdx; while( i < startIdx ) { NearPeriodTotal[2] = NearPeriodTotal[2] + TA_CANDLERANGE(Near,i - 2); NearPeriodTotal[1] = NearPeriodTotal[1] + TA_CANDLERANGE(Near,i - 1); i += 1; } i = FarTrailingIdx; while( i < startIdx ) { FarPeriodTotal[2] = FarPeriodTotal[2] + TA_CANDLERANGE(Far,i - 2); FarPeriodTotal[1] = FarPeriodTotal[1] + TA_CANDLERANGE(Far,i - 1); i += 1; } i = BodyShortTrailingIdx; while( i < startIdx ) { BodyShortPeriodTotal += TA_CANDLERANGE(BodyShort,i); i += 1; } i = startIdx; /* Proceed with the calculation for the requested range. * Must have: * - three white candlesticks with consecutively higher closes * - Greg Morris wants them to be long, Steve Nison doesn't; anyway they should not be short * - each candle opens within or near the previous white real body * - each candle must have no or very short upper shadow * - to differentiate this pattern from advance block, each candle must not be far shorter than the prior candle * The meanings of "not short", "very short shadow", "far" and "near" are specified with TA_SetCandleSettings; * here the 3 candles must be not short, if you want them to be long use TA_SetCandleSettings on BodyShort */ outIdx = 0; do { if( ((inClose[i - 2] >= inOpen[i - 2]) ? 1 : -1) == 1 && /* 1st white */ (inHigh[i - 2] - ((inClose[i - 2] >= inOpen[i - 2]) ? inClose[i - 2] : inOpen[i - 2])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[2],i - 2) && /* very short upper shadow */ ((inClose[i - 1] >= inOpen[i - 1]) ? 1 : -1) == 1 && /* 2nd white */ (inHigh[i - 1] - ((inClose[i - 1] >= inOpen[i - 1]) ? inClose[i - 1] : inOpen[i - 1])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[1],i - 1) && /* very short upper shadow */ ((inClose[i] >= inOpen[i]) ? 1 : -1) == 1 && /* 3rd white */ (inHigh[i] - ((inClose[i] >= inOpen[i]) ? inClose[i] : inOpen[i])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[0],i) && /* very short upper shadow */ inClose[i] > inClose[i - 1] && inClose[i - 1] > inClose[i - 2] && /* consecutive higher closes */ inOpen[i - 1] > inOpen[i - 2] && /* 2nd opens within/near 1st real body */ inOpen[i - 1] <= inClose[i - 2] + TA_CANDLEAVERAGE(Near,NearPeriodTotal[2],i - 2) && inOpen[i] > inOpen[i - 1] && /* 3rd opens within/near 2nd real body */ inOpen[i] <= inClose[i - 1] + TA_CANDLEAVERAGE(Near,NearPeriodTotal[1],i - 1) && fabs(inClose[i - 1] - inOpen[i - 1]) > fabs(inClose[i - 2] - inOpen[i - 2]) - TA_CANDLEAVERAGE(Far,FarPeriodTotal[2],i - 2) && /* 2nd not far shorter than 1st */ fabs(inClose[i] - inOpen[i]) > fabs(inClose[i - 1] - inOpen[i - 1]) - TA_CANDLEAVERAGE(Far,FarPeriodTotal[1],i - 1) && /* 3rd not far shorter than 2nd */ fabs(inClose[i] - inOpen[i]) > TA_CANDLEAVERAGE(BodyShort,BodyShortPeriodTotal,i) ) /* not short real body */ { outInteger[outIdx++] = 100; } else { 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) */ for( totIdx = 2; totIdx >= 0; totIdx -= 1 ) { ShadowVeryShortPeriodTotal[totIdx] = ShadowVeryShortPeriodTotal[totIdx] + (TA_CANDLERANGE(ShadowVeryShort,i - totIdx) - TA_CANDLERANGE(ShadowVeryShort,ShadowVeryShortTrailingIdx - totIdx)); } for( totIdx = 2; totIdx >= 1; totIdx -= 1 ) { FarPeriodTotal[totIdx] = FarPeriodTotal[totIdx] + (TA_CANDLERANGE(Far,i - totIdx) - TA_CANDLERANGE(Far,FarTrailingIdx - totIdx)); NearPeriodTotal[totIdx] = NearPeriodTotal[totIdx] + (TA_CANDLERANGE(Near,i - totIdx) - TA_CANDLERANGE(Near,NearTrailingIdx - totIdx)); } BodyShortPeriodTotal += TA_CANDLERANGE(BodyShort,i) - TA_CANDLERANGE(BodyShort,BodyShortTrailingIdx); i += 1; ShadowVeryShortTrailingIdx += 1; NearTrailingIdx += 1; FarTrailingIdx += 1; BodyShortTrailingIdx += 1; } while( i <= endIdx ); /* All done. Indicate the output limits and return. */ *outNBElement= outIdx; *outBegIdx= startIdx; return TA_SUCCESS; } TA_RetCode TA_S_CDL3WHITESOLDIERS( int startIdx, int endIdx, const float inOpen[], const float inHigh[], const float inLow[], const float inClose[], int *outBegIdx, int *outNBElement, int outInteger[] ) { double ShadowVeryShortPeriodTotal[3]; double NearPeriodTotal[3]; double FarPeriodTotal[3]; double BodyShortPeriodTotal; int i; int outIdx; int totIdx; int ShadowVeryShortTrailingIdx; int NearTrailingIdx; int FarTrailingIdx; int BodyShortTrailingIdx; int lookbackTotal; int BodyShort_avgPeriod = TA_Globals->candleSettings[TA_BodyShort].avgPeriod; int Far_avgPeriod = TA_Globals->candleSettings[TA_Far].avgPeriod; int Near_avgPeriod = TA_Globals->candleSettings[TA_Near].avgPeriod; int ShadowVeryShort_avgPeriod = TA_Globals->candleSettings[TA_ShadowVeryShort].avgPeriod; if( (startIdx < 0) || (startIdx > TA_INDEX_MAX) ) return TA_OUT_OF_RANGE_START_INDEX; if( (endIdx < 0) || (endIdx > TA_INDEX_MAX) || (endIdx < startIdx) ) return TA_OUT_OF_RANGE_END_INDEX; if( !inOpen ) return TA_BAD_PARAM; if( !inHigh ) return TA_BAD_PARAM; if( !inLow ) return TA_BAD_PARAM; if( !inClose ) return TA_BAD_PARAM; if( !outBegIdx || !outNBElement ) return TA_BAD_PARAM; if( !outInteger ) return TA_BAD_PARAM; lookbackTotal = TA_CDL3WHITESOLDIERS_Lookback(); if( startIdx < lookbackTotal ) { startIdx = lookbackTotal; } if( startIdx > endIdx ) { *outBegIdx= 0; *outNBElement= 0; return TA_SUCCESS; } ShadowVeryShortPeriodTotal[2] = 0; ShadowVeryShortPeriodTotal[1] = 0; ShadowVeryShortPeriodTotal[0] = 0; ShadowVeryShortTrailingIdx = startIdx - ShadowVeryShort_avgPeriod; NearPeriodTotal[2] = 0; NearPeriodTotal[1] = 0; NearPeriodTotal[0] = 0; NearTrailingIdx = startIdx - Near_avgPeriod; FarPeriodTotal[2] = 0; FarPeriodTotal[1] = 0; FarPeriodTotal[0] = 0; FarTrailingIdx = startIdx - Far_avgPeriod; BodyShortPeriodTotal = 0; BodyShortTrailingIdx = startIdx - BodyShort_avgPeriod; i = ShadowVeryShortTrailingIdx; while( i < startIdx ) { ShadowVeryShortPeriodTotal[2] = ShadowVeryShortPeriodTotal[2] + TA_CANDLERANGE(ShadowVeryShort,i - 2); ShadowVeryShortPeriodTotal[1] = ShadowVeryShortPeriodTotal[1] + TA_CANDLERANGE(ShadowVeryShort,i - 1); ShadowVeryShortPeriodTotal[0] = ShadowVeryShortPeriodTotal[0] + TA_CANDLERANGE(ShadowVeryShort,i); i += 1; } i = NearTrailingIdx; while( i < startIdx ) { NearPeriodTotal[2] = NearPeriodTotal[2] + TA_CANDLERANGE(Near,i - 2); NearPeriodTotal[1] = NearPeriodTotal[1] + TA_CANDLERANGE(Near,i - 1); i += 1; } i = FarTrailingIdx; while( i < startIdx ) { FarPeriodTotal[2] = FarPeriodTotal[2] + TA_CANDLERANGE(Far,i - 2); FarPeriodTotal[1] = FarPeriodTotal[1] + TA_CANDLERANGE(Far,i - 1); i += 1; } i = BodyShortTrailingIdx; while( i < startIdx ) { BodyShortPeriodTotal += TA_CANDLERANGE(BodyShort,i); i += 1; } i = startIdx; outIdx = 0; do { if( (((double)inClose[i - 2] >= (double)inOpen[i - 2]) ? 1 : -1) == 1 && ((double)inHigh[i - 2] - (((double)inClose[i - 2] >= (double)inOpen[i - 2]) ? (double)inClose[i - 2] : (double)inOpen[i - 2])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[2],i - 2) && (((double)inClose[i - 1] >= (double)inOpen[i - 1]) ? 1 : -1) == 1 && ((double)inHigh[i - 1] - (((double)inClose[i - 1] >= (double)inOpen[i - 1]) ? (double)inClose[i - 1] : (double)inOpen[i - 1])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[1],i - 1) && (((double)inClose[i] >= (double)inOpen[i]) ? 1 : -1) == 1 && ((double)inHigh[i] - (((double)inClose[i] >= (double)inOpen[i]) ? (double)inClose[i] : (double)inOpen[i])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[0],i) && (double)inClose[i] > (double)inClose[i - 1] && (double)inClose[i - 1] > (double)inClose[i - 2] && (double)inOpen[i - 1] > (double)inOpen[i - 2] && (double)inOpen[i - 1] <= (double)inClose[i - 2] + TA_CANDLEAVERAGE(Near,NearPeriodTotal[2],i - 2) && (double)inOpen[i] > (double)inOpen[i - 1] && (double)inOpen[i] <= (double)inClose[i - 1] + TA_CANDLEAVERAGE(Near,NearPeriodTotal[1],i - 1) && fabs((double)inClose[i - 1] - (double)inOpen[i - 1]) > fabs((double)inClose[i - 2] - (double)inOpen[i - 2]) - TA_CANDLEAVERAGE(Far,FarPeriodTotal[2],i - 2) && fabs((double)inClose[i] - (double)inOpen[i]) > fabs((double)inClose[i - 1] - (double)inOpen[i - 1]) - TA_CANDLEAVERAGE(Far,FarPeriodTotal[1],i - 1) && fabs((double)inClose[i] - (double)inOpen[i]) > TA_CANDLEAVERAGE(BodyShort,BodyShortPeriodTotal,i) ) { outInteger[outIdx++] = 100; } else { outInteger[outIdx++] = 0; } for( totIdx = 2; totIdx >= 0; totIdx -= 1 ) { ShadowVeryShortPeriodTotal[totIdx] = ShadowVeryShortPeriodTotal[totIdx] + (TA_CANDLERANGE(ShadowVeryShort,i - totIdx) - TA_CANDLERANGE(ShadowVeryShort,ShadowVeryShortTrailingIdx - totIdx)); } for( totIdx = 2; totIdx >= 1; totIdx -= 1 ) { FarPeriodTotal[totIdx] = FarPeriodTotal[totIdx] + (TA_CANDLERANGE(Far,i - totIdx) - TA_CANDLERANGE(Far,FarTrailingIdx - totIdx)); NearPeriodTotal[totIdx] = NearPeriodTotal[totIdx] + (TA_CANDLERANGE(Near,i - totIdx) - TA_CANDLERANGE(Near,NearTrailingIdx - totIdx)); } BodyShortPeriodTotal += TA_CANDLERANGE(BodyShort,i) - TA_CANDLERANGE(BodyShort,BodyShortTrailingIdx); i += 1; ShadowVeryShortTrailingIdx += 1; NearTrailingIdx += 1; FarTrailingIdx += 1; BodyShortTrailingIdx += 1; } while( i <= endIdx ); *outNBElement= outIdx; *outBegIdx= startIdx; return TA_SUCCESS; } /**** Streaming API *****/ struct TA_CDL3WHITESOLDIERS_Stream { /* The bars this handle has an output for (see TA_CDL3WHITESOLDIERS_OutRange). */ int outRangeBegIdx; int outRangeCount; /* The value(s) at the last bar the stream counted (see TA_CDL3WHITESOLDIERS_Value). */ int cur_outInteger; double ShadowVeryShortPeriodTotal[3]; double NearPeriodTotal[3]; double FarPeriodTotal[3]; double BodyShortPeriodTotal; double lag1_inOpen; double lag2_inOpen; double lag1_inHigh; double lag2_inHigh; double lag1_inLow; double lag2_inLow; double lag1_inClose; double lag2_inClose; int ringPos_BodyShortTrailingIdx; int ringCap_BodyShortTrailingIdx; double *ring_BodyShortTrailingIdx_derived; int ringPos_FarTrailingIdx; int ringCap_FarTrailingIdx; int ringLag_FarTrailingIdx; double *ring_FarTrailingIdx_derived; int ringPos_NearTrailingIdx; int ringCap_NearTrailingIdx; int ringLag_NearTrailingIdx; double *ring_NearTrailingIdx_derived; int ringPos_ShadowVeryShortTrailingIdx; int ringCap_ShadowVeryShortTrailingIdx; int ringLag_ShadowVeryShortTrailingIdx; double *ring_ShadowVeryShortTrailingIdx_derived; }; /* Private function, not in public API. */ static void TA_CDL3WHITESOLDIERS_ReleaseImpl( struct TA_CDL3WHITESOLDIERS_Stream *sp ) { if( !sp ) return; if( sp->ring_BodyShortTrailingIdx_derived ) TA_Free( sp->ring_BodyShortTrailingIdx_derived ); if( sp->ring_FarTrailingIdx_derived ) TA_Free( sp->ring_FarTrailingIdx_derived ); if( sp->ring_NearTrailingIdx_derived ) TA_Free( sp->ring_NearTrailingIdx_derived ); if( sp->ring_ShadowVeryShortTrailingIdx_derived ) TA_Free( sp->ring_ShadowVeryShortTrailingIdx_derived ); TA_Free( sp ); } /* Private function, not in public API. */ static void TA_CDL3WHITESOLDIERS_StepImpl( struct TA_CDL3WHITESOLDIERS_Stream *sp, double inOpen, double inHigh, double inLow, double inClose, int *outInteger ) { int totIdx; if( sp->ringCap_BodyShortTrailingIdx == 0 ) { sp->ring_BodyShortTrailingIdx_derived[0] = TA_STREAM_CANDLERANGE(BodyShort,inOpen,inHigh,inLow,inClose); } sp->ring_FarTrailingIdx_derived[sp->ringPos_FarTrailingIdx] = TA_STREAM_CANDLERANGE(Far,inOpen,inHigh,inLow,inClose); sp->ring_NearTrailingIdx_derived[sp->ringPos_NearTrailingIdx] = TA_STREAM_CANDLERANGE(Near,inOpen,inHigh,inLow,inClose); sp->ring_ShadowVeryShortTrailingIdx_derived[sp->ringPos_ShadowVeryShortTrailingIdx] = TA_STREAM_CANDLERANGE(ShadowVeryShort,inOpen,inHigh,inLow,inClose); if( ((sp->lag2_inClose >= sp->lag2_inOpen) ? 1 : -1) == 1 && /* 1st white */ (sp->lag2_inHigh - ((sp->lag2_inClose >= sp->lag2_inOpen) ? sp->lag2_inClose : sp->lag2_inOpen)) < TA_STREAM_CANDLEAVERAGE(ShadowVeryShort,sp->ShadowVeryShortPeriodTotal[2],sp->lag2_inOpen,sp->lag2_inHigh,sp->lag2_inLow,sp->lag2_inClose) && /* very short upper shadow */ ((sp->lag1_inClose >= sp->lag1_inOpen) ? 1 : -1) == 1 && /* 2nd white */ (sp->lag1_inHigh - ((sp->lag1_inClose >= sp->lag1_inOpen) ? sp->lag1_inClose : sp->lag1_inOpen)) < TA_STREAM_CANDLEAVERAGE(ShadowVeryShort,sp->ShadowVeryShortPeriodTotal[1],sp->lag1_inOpen,sp->lag1_inHigh,sp->lag1_inLow,sp->lag1_inClose) && /* very short upper shadow */ ((inClose >= inOpen) ? 1 : -1) == 1 && /* 3rd white */ (inHigh - ((inClose >= inOpen) ? inClose : inOpen)) < TA_STREAM_CANDLEAVERAGE(ShadowVeryShort,sp->ShadowVeryShortPeriodTotal[0],inOpen,inHigh,inLow,inClose) && /* very short upper shadow */ inClose > sp->lag1_inClose && sp->lag1_inClose > sp->lag2_inClose && /* consecutive higher closes */ sp->lag1_inOpen > sp->lag2_inOpen && /* 2nd opens within/near 1st real body */ sp->lag1_inOpen <= sp->lag2_inClose + TA_STREAM_CANDLEAVERAGE(Near,sp->NearPeriodTotal[2],sp->lag2_inOpen,sp->lag2_inHigh,sp->lag2_inLow,sp->lag2_inClose) && inOpen > sp->lag1_inOpen && /* 3rd opens within/near 2nd real body */ inOpen <= sp->lag1_inClose + TA_STREAM_CANDLEAVERAGE(Near,sp->NearPeriodTotal[1],sp->lag1_inOpen,sp->lag1_inHigh,sp->lag1_inLow,sp->lag1_inClose) && fabs(sp->lag1_inClose - sp->lag1_inOpen) > fabs(sp->lag2_inClose - sp->lag2_inOpen) - TA_STREAM_CANDLEAVERAGE(Far,sp->FarPeriodTotal[2],sp->lag2_inOpen,sp->lag2_inHigh,sp->lag2_inLow,sp->lag2_inClose) && /* 2nd not far shorter than 1st */ fabs(inClose - inOpen) > fabs(sp->lag1_inClose - sp->lag1_inOpen) - TA_STREAM_CANDLEAVERAGE(Far,sp->FarPeriodTotal[1],sp->lag1_inOpen,sp->lag1_inHigh,sp->lag1_inLow,sp->lag1_inClose) && /* 3rd not far shorter than 2nd */ fabs(inClose - inOpen) > TA_STREAM_CANDLEAVERAGE(BodyShort,sp->BodyShortPeriodTotal,inOpen,inHigh,inLow,inClose) ) /* not short real body */ { *outInteger= 100; } else { *outInteger= 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) */ for( totIdx = 2; totIdx >= 0; totIdx -= 1 ) { sp->ShadowVeryShortPeriodTotal[totIdx] = sp->ShadowVeryShortPeriodTotal[totIdx] + (sp->ring_ShadowVeryShortTrailingIdx_derived[(sp->ringPos_ShadowVeryShortTrailingIdx + sp->ringCap_ShadowVeryShortTrailingIdx - totIdx >= sp->ringCap_ShadowVeryShortTrailingIdx) ? sp->ringPos_ShadowVeryShortTrailingIdx + sp->ringCap_ShadowVeryShortTrailingIdx - totIdx - sp->ringCap_ShadowVeryShortTrailingIdx : sp->ringPos_ShadowVeryShortTrailingIdx + sp->ringCap_ShadowVeryShortTrailingIdx - totIdx] - sp->ring_ShadowVeryShortTrailingIdx_derived[(sp->ringPos_ShadowVeryShortTrailingIdx + sp->ringCap_ShadowVeryShortTrailingIdx - sp->ringLag_ShadowVeryShortTrailingIdx - totIdx) % sp->ringCap_ShadowVeryShortTrailingIdx]); } for( totIdx = 2; totIdx >= 1; totIdx -= 1 ) { sp->FarPeriodTotal[totIdx] = sp->FarPeriodTotal[totIdx] + (sp->ring_FarTrailingIdx_derived[(sp->ringPos_FarTrailingIdx + sp->ringCap_FarTrailingIdx - totIdx >= sp->ringCap_FarTrailingIdx) ? sp->ringPos_FarTrailingIdx + sp->ringCap_FarTrailingIdx - totIdx - sp->ringCap_FarTrailingIdx : sp->ringPos_FarTrailingIdx + sp->ringCap_FarTrailingIdx - totIdx] - sp->ring_FarTrailingIdx_derived[(sp->ringPos_FarTrailingIdx + sp->ringCap_FarTrailingIdx - sp->ringLag_FarTrailingIdx - totIdx) % sp->ringCap_FarTrailingIdx]); sp->NearPeriodTotal[totIdx] = sp->NearPeriodTotal[totIdx] + (sp->ring_NearTrailingIdx_derived[(sp->ringPos_NearTrailingIdx + sp->ringCap_NearTrailingIdx - totIdx >= sp->ringCap_NearTrailingIdx) ? sp->ringPos_NearTrailingIdx + sp->ringCap_NearTrailingIdx - totIdx - sp->ringCap_NearTrailingIdx : sp->ringPos_NearTrailingIdx + sp->ringCap_NearTrailingIdx - totIdx] - sp->ring_NearTrailingIdx_derived[(sp->ringPos_NearTrailingIdx + sp->ringCap_NearTrailingIdx - sp->ringLag_NearTrailingIdx - totIdx) % sp->ringCap_NearTrailingIdx]); } sp->BodyShortPeriodTotal += TA_STREAM_CANDLERANGE(BodyShort,inOpen,inHigh,inLow,inClose) - sp->ring_BodyShortTrailingIdx_derived[sp->ringPos_BodyShortTrailingIdx]; sp->cur_outInteger = *outInteger; sp->lag2_inOpen = sp->lag1_inOpen; sp->lag1_inOpen = inOpen; sp->lag2_inHigh = sp->lag1_inHigh; sp->lag1_inHigh = inHigh; sp->lag2_inLow = sp->lag1_inLow; sp->lag1_inLow = inLow; sp->lag2_inClose = sp->lag1_inClose; sp->lag1_inClose = inClose; sp->ring_BodyShortTrailingIdx_derived[sp->ringPos_BodyShortTrailingIdx] = TA_STREAM_CANDLERANGE(BodyShort,inOpen,inHigh,inLow,inClose); sp->ringPos_BodyShortTrailingIdx = sp->ringPos_BodyShortTrailingIdx + 1; if( sp->ringPos_BodyShortTrailingIdx >= sp->ringCap_BodyShortTrailingIdx ) { sp->ringPos_BodyShortTrailingIdx = 0; } sp->ringPos_FarTrailingIdx = sp->ringPos_FarTrailingIdx + 1; if( sp->ringPos_FarTrailingIdx >= sp->ringCap_FarTrailingIdx ) { sp->ringPos_FarTrailingIdx = 0; } sp->ringPos_NearTrailingIdx = sp->ringPos_NearTrailingIdx + 1; if( sp->ringPos_NearTrailingIdx >= sp->ringCap_NearTrailingIdx ) { sp->ringPos_NearTrailingIdx = 0; } sp->ringPos_ShadowVeryShortTrailingIdx = sp->ringPos_ShadowVeryShortTrailingIdx + 1; if( sp->ringPos_ShadowVeryShortTrailingIdx >= sp->ringCap_ShadowVeryShortTrailingIdx ) { sp->ringPos_ShadowVeryShortTrailingIdx = 0; } } static TA_RetCode TA_CDL3WHITESOLDIERS_OpenImpl( struct TA_CDL3WHITESOLDIERS_Stream **stream, const double inOpen[], const double inHigh[], const double inLow[], const double inClose[], int startIdx, int historyLen, int *outBegIdx, int *outNBElement, int outInteger[], int outStride ) { struct TA_CDL3WHITESOLDIERS_Stream *sp; int endIdx; if( !stream ) return TA_BAD_PARAM; *stream = NULL; if( historyLen < 1 ) return TA_OUT_OF_RANGE_START_INDEX; if( historyLen > TA_INDEX_MAX + 1 ) return TA_OUT_OF_RANGE_END_INDEX; if( !inOpen || !inHigh || !inLow || !inClose || !outInteger ) return TA_BAD_PARAM; if( startIdx > historyLen - 1 ) { *outBegIdx = 0; *outNBElement = 0; return TA_INSUFFICIENT_HISTORY; } endIdx = historyLen - 1; { int BodyShort_avgPeriod = TA_Globals->candleSettings[TA_BodyShort].avgPeriod; int Far_avgPeriod = TA_Globals->candleSettings[TA_Far].avgPeriod; int Near_avgPeriod = TA_Globals->candleSettings[TA_Near].avgPeriod; int ShadowVeryShort_avgPeriod = TA_Globals->candleSettings[TA_ShadowVeryShort].avgPeriod; double ShadowVeryShortPeriodTotal[3] = {0}; double NearPeriodTotal[3] = {0}; double FarPeriodTotal[3] = {0}; double BodyShortPeriodTotal = 0.0; int i; int outIdx; int totIdx; int ShadowVeryShortTrailingIdx; int NearTrailingIdx; int FarTrailingIdx; int BodyShortTrailingIdx; int lookbackTotal; /* Identify the minimum number of price bar needed * to calculate at least one output. */ lookbackTotal = TA_CDL3WHITESOLDIERS_Lookback(); /* Move up the start index if there is not * enough initial data. */ if( startIdx < lookbackTotal ) { startIdx = lookbackTotal; } /* Make sure there is still something to evaluate. */ if( startIdx > endIdx ) { *outBegIdx= 0; *outNBElement= 0; return TA_INSUFFICIENT_HISTORY; } /* Do the calculation using tight loops. */ /* Add-up the initial period, except for the last value. */ ShadowVeryShortPeriodTotal[2] = 0; ShadowVeryShortPeriodTotal[1] = 0; ShadowVeryShortPeriodTotal[0] = 0; ShadowVeryShortTrailingIdx = startIdx - ShadowVeryShort_avgPeriod; NearPeriodTotal[2] = 0; NearPeriodTotal[1] = 0; NearPeriodTotal[0] = 0; NearTrailingIdx = startIdx - Near_avgPeriod; FarPeriodTotal[2] = 0; FarPeriodTotal[1] = 0; FarPeriodTotal[0] = 0; FarTrailingIdx = startIdx - Far_avgPeriod; BodyShortPeriodTotal = 0; BodyShortTrailingIdx = startIdx - BodyShort_avgPeriod; i = ShadowVeryShortTrailingIdx; while( i < startIdx ) { ShadowVeryShortPeriodTotal[2] = ShadowVeryShortPeriodTotal[2] + TA_CANDLERANGE(ShadowVeryShort,i - 2); ShadowVeryShortPeriodTotal[1] = ShadowVeryShortPeriodTotal[1] + TA_CANDLERANGE(ShadowVeryShort,i - 1); ShadowVeryShortPeriodTotal[0] = ShadowVeryShortPeriodTotal[0] + TA_CANDLERANGE(ShadowVeryShort,i); i += 1; } i = NearTrailingIdx; while( i < startIdx ) { NearPeriodTotal[2] = NearPeriodTotal[2] + TA_CANDLERANGE(Near,i - 2); NearPeriodTotal[1] = NearPeriodTotal[1] + TA_CANDLERANGE(Near,i - 1); i += 1; } i = FarTrailingIdx; while( i < startIdx ) { FarPeriodTotal[2] = FarPeriodTotal[2] + TA_CANDLERANGE(Far,i - 2); FarPeriodTotal[1] = FarPeriodTotal[1] + TA_CANDLERANGE(Far,i - 1); i += 1; } i = BodyShortTrailingIdx; while( i < startIdx ) { BodyShortPeriodTotal += TA_CANDLERANGE(BodyShort,i); i += 1; } i = startIdx; /* Proceed with the calculation for the requested range. * Must have: * - three white candlesticks with consecutively higher closes * - Greg Morris wants them to be long, Steve Nison doesn't; anyway they should not be short * - each candle opens within or near the previous white real body * - each candle must have no or very short upper shadow * - to differentiate this pattern from advance block, each candle must not be far shorter than the prior candle * The meanings of "not short", "very short shadow", "far" and "near" are specified with TA_SetCandleSettings; * here the 3 candles must be not short, if you want them to be long use TA_SetCandleSettings on BodyShort */ outIdx = 0; do { if( ((inClose[i - 2] >= inOpen[i - 2]) ? 1 : -1) == 1 && /* 1st white */ (inHigh[i - 2] - ((inClose[i - 2] >= inOpen[i - 2]) ? inClose[i - 2] : inOpen[i - 2])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[2],i - 2) && /* very short upper shadow */ ((inClose[i - 1] >= inOpen[i - 1]) ? 1 : -1) == 1 && /* 2nd white */ (inHigh[i - 1] - ((inClose[i - 1] >= inOpen[i - 1]) ? inClose[i - 1] : inOpen[i - 1])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[1],i - 1) && /* very short upper shadow */ ((inClose[i] >= inOpen[i]) ? 1 : -1) == 1 && /* 3rd white */ (inHigh[i] - ((inClose[i] >= inOpen[i]) ? inClose[i] : inOpen[i])) < TA_CANDLEAVERAGE(ShadowVeryShort,ShadowVeryShortPeriodTotal[0],i) && /* very short upper shadow */ inClose[i] > inClose[i - 1] && inClose[i - 1] > inClose[i - 2] && /* consecutive higher closes */ inOpen[i - 1] > inOpen[i - 2] && /* 2nd opens within/near 1st real body */ inOpen[i - 1] <= inClose[i - 2] + TA_CANDLEAVERAGE(Near,NearPeriodTotal[2],i - 2) && inOpen[i] > inOpen[i - 1] && /* 3rd opens within/near 2nd real body */ inOpen[i] <= inClose[i - 1] + TA_CANDLEAVERAGE(Near,NearPeriodTotal[1],i - 1) && fabs(inClose[i - 1] - inOpen[i - 1]) > fabs(inClose[i - 2] - inOpen[i - 2]) - TA_CANDLEAVERAGE(Far,FarPeriodTotal[2],i - 2) && /* 2nd not far shorter than 1st */ fabs(inClose[i] - inOpen[i]) > fabs(inClose[i - 1] - inOpen[i - 1]) - TA_CANDLEAVERAGE(Far,FarPeriodTotal[1],i - 1) && /* 3rd not far shorter than 2nd */ fabs(inClose[i] - inOpen[i]) > TA_CANDLEAVERAGE(BodyShort,BodyShortPeriodTotal,i) ) /* not short real body */ { outInteger[outIdx++ * outStride] = 100; } else { outInteger[outIdx++ * outStride] = 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) */ for( totIdx = 2; totIdx >= 0; totIdx -= 1 ) { ShadowVeryShortPeriodTotal[totIdx] = ShadowVeryShortPeriodTotal[totIdx] + (TA_CANDLERANGE(ShadowVeryShort,i - totIdx) - TA_CANDLERANGE(ShadowVeryShort,ShadowVeryShortTrailingIdx - totIdx)); } for( totIdx = 2; totIdx >= 1; totIdx -= 1 ) { FarPeriodTotal[totIdx] = FarPeriodTotal[totIdx] + (TA_CANDLERANGE(Far,i - totIdx) - TA_CANDLERANGE(Far,FarTrailingIdx - totIdx)); NearPeriodTotal[totIdx] = NearPeriodTotal[totIdx] + (TA_CANDLERANGE(Near,i - totIdx) - TA_CANDLERANGE(Near,NearTrailingIdx - totIdx)); } BodyShortPeriodTotal += TA_CANDLERANGE(BodyShort,i) - TA_CANDLERANGE(BodyShort,BodyShortTrailingIdx); i += 1; ShadowVeryShortTrailingIdx += 1; NearTrailingIdx += 1; FarTrailingIdx += 1; BodyShortTrailingIdx += 1; } while( i <= endIdx ); /* All done. Indicate the output limits and return. */ *outNBElement= outIdx; *outBegIdx= startIdx; /* Capture the live batch state into the handle. */ sp = (struct TA_CDL3WHITESOLDIERS_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) { return TA_ALLOC_ERR; } memset( sp, 0, sizeof(*sp) ); memcpy( sp->ShadowVeryShortPeriodTotal, ShadowVeryShortPeriodTotal, sizeof( sp->ShadowVeryShortPeriodTotal ) ); memcpy( sp->NearPeriodTotal, NearPeriodTotal, sizeof( sp->NearPeriodTotal ) ); memcpy( sp->FarPeriodTotal, FarPeriodTotal, sizeof( sp->FarPeriodTotal ) ); sp->BodyShortPeriodTotal = BodyShortPeriodTotal; sp->ringCap_BodyShortTrailingIdx = (int)(i - BodyShortTrailingIdx); if( sp->ringCap_BodyShortTrailingIdx < 0 || sp->ringCap_BodyShortTrailingIdx > historyLen ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( sp ); return TA_INTERNAL_ERROR(203); } { size_t allocN = (size_t)(sp->ringCap_BodyShortTrailingIdx > 0 ? sp->ringCap_BodyShortTrailingIdx : 1); sp->ring_BodyShortTrailingIdx_derived = (double *)TA_Malloc( sizeof(double) * allocN ); if( !sp->ring_BodyShortTrailingIdx_derived ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( sp ); return TA_ALLOC_ERR; } { int fillJ; for( fillJ = historyLen - sp->ringCap_BodyShortTrailingIdx; fillJ < historyLen; fillJ++ ) sp->ring_BodyShortTrailingIdx_derived[fillJ - (historyLen - sp->ringCap_BodyShortTrailingIdx)] = TA_STREAM_CANDLERANGE(BodyShort,inOpen[fillJ],inHigh[fillJ],inLow[fillJ],inClose[fillJ]); } } sp->ringPos_BodyShortTrailingIdx = 0; sp->ringLag_FarTrailingIdx = (int)(i - FarTrailingIdx); sp->ringCap_FarTrailingIdx = sp->ringLag_FarTrailingIdx + 3; if( sp->ringLag_FarTrailingIdx < 0 || sp->ringCap_FarTrailingIdx > historyLen ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( sp ); return TA_INTERNAL_ERROR(204); } { size_t allocN = (size_t)(sp->ringCap_FarTrailingIdx > 0 ? sp->ringCap_FarTrailingIdx : 1); sp->ring_FarTrailingIdx_derived = (double *)TA_Malloc( sizeof(double) * allocN ); if( !sp->ring_FarTrailingIdx_derived ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( sp ); return TA_ALLOC_ERR; } { int fillJ; for( fillJ = historyLen - sp->ringCap_FarTrailingIdx; fillJ < historyLen; fillJ++ ) sp->ring_FarTrailingIdx_derived[fillJ % sp->ringCap_FarTrailingIdx] = TA_STREAM_CANDLERANGE(Far,inOpen[fillJ],inHigh[fillJ],inLow[fillJ],inClose[fillJ]); } } sp->ringPos_FarTrailingIdx = historyLen % sp->ringCap_FarTrailingIdx; sp->ringLag_NearTrailingIdx = (int)(i - NearTrailingIdx); sp->ringCap_NearTrailingIdx = sp->ringLag_NearTrailingIdx + 3; if( sp->ringLag_NearTrailingIdx < 0 || sp->ringCap_NearTrailingIdx > historyLen ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( sp ); return TA_INTERNAL_ERROR(205); } { size_t allocN = (size_t)(sp->ringCap_NearTrailingIdx > 0 ? sp->ringCap_NearTrailingIdx : 1); sp->ring_NearTrailingIdx_derived = (double *)TA_Malloc( sizeof(double) * allocN ); if( !sp->ring_NearTrailingIdx_derived ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( sp ); return TA_ALLOC_ERR; } { int fillJ; for( fillJ = historyLen - sp->ringCap_NearTrailingIdx; fillJ < historyLen; fillJ++ ) sp->ring_NearTrailingIdx_derived[fillJ % sp->ringCap_NearTrailingIdx] = TA_STREAM_CANDLERANGE(Near,inOpen[fillJ],inHigh[fillJ],inLow[fillJ],inClose[fillJ]); } } sp->ringPos_NearTrailingIdx = historyLen % sp->ringCap_NearTrailingIdx; sp->ringLag_ShadowVeryShortTrailingIdx = (int)(i - ShadowVeryShortTrailingIdx); sp->ringCap_ShadowVeryShortTrailingIdx = sp->ringLag_ShadowVeryShortTrailingIdx + 3; if( sp->ringLag_ShadowVeryShortTrailingIdx < 0 || sp->ringCap_ShadowVeryShortTrailingIdx > historyLen ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( sp ); return TA_INTERNAL_ERROR(206); } { size_t allocN = (size_t)(sp->ringCap_ShadowVeryShortTrailingIdx > 0 ? sp->ringCap_ShadowVeryShortTrailingIdx : 1); sp->ring_ShadowVeryShortTrailingIdx_derived = (double *)TA_Malloc( sizeof(double) * allocN ); if( !sp->ring_ShadowVeryShortTrailingIdx_derived ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( sp ); return TA_ALLOC_ERR; } { int fillJ; for( fillJ = historyLen - sp->ringCap_ShadowVeryShortTrailingIdx; fillJ < historyLen; fillJ++ ) sp->ring_ShadowVeryShortTrailingIdx_derived[fillJ % sp->ringCap_ShadowVeryShortTrailingIdx] = TA_STREAM_CANDLERANGE(ShadowVeryShort,inOpen[fillJ],inHigh[fillJ],inLow[fillJ],inClose[fillJ]); } } sp->ringPos_ShadowVeryShortTrailingIdx = historyLen % sp->ringCap_ShadowVeryShortTrailingIdx; sp->lag1_inOpen = inOpen[historyLen - 1]; sp->lag2_inOpen = inOpen[historyLen - 2]; sp->lag1_inHigh = inHigh[historyLen - 1]; sp->lag2_inHigh = inHigh[historyLen - 2]; sp->lag1_inLow = inLow[historyLen - 1]; sp->lag2_inLow = inLow[historyLen - 2]; sp->lag1_inClose = inClose[historyLen - 1]; sp->lag2_inClose = inClose[historyLen - 2]; sp->outRangeBegIdx = *outBegIdx; sp->outRangeCount = *outNBElement; sp->cur_outInteger = outInteger[(*outNBElement - 1) * outStride]; *stream = sp; return TA_SUCCESS; } } /* Private function, not in public API. */ TA_RetCode TA_CDL3WHITESOLDIERS_OpenInternal( struct TA_CDL3WHITESOLDIERS_Stream **stream, const double inOpen[], const double inHigh[], const double inLow[], const double inClose[], int startIdx, int historyLen, int *outInteger ) { TA_RetCode retCode; int dummyBegIdx = 0; int dummyNBElement = 0; int sink_outInteger = 0; retCode = TA_CDL3WHITESOLDIERS_OpenImpl( stream, inOpen, inHigh, inLow, inClose, startIdx, historyLen, &dummyBegIdx, &dummyNBElement, &sink_outInteger, 0 ); if( retCode == TA_SUCCESS ) { *outInteger = sink_outInteger; } return retCode; } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_Open( TA_CDL3WHITESOLDIERS_Stream **stream, const double inOpen[], const double inHigh[], const double inLow[], const double inClose[], int historyLen, int *outInteger ) { if( !stream ) return TA_BAD_PARAM; *stream = NULL; if( historyLen < 1 ) return TA_OUT_OF_RANGE_START_INDEX; if( historyLen > TA_INDEX_MAX + 1 ) return TA_OUT_OF_RANGE_END_INDEX; if( !inOpen || !inHigh || !inLow || !inClose || !outInteger ) return TA_BAD_PARAM; return TA_CDL3WHITESOLDIERS_OpenInternal( stream, inOpen, inHigh, inLow, inClose, 0, historyLen, outInteger ); } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_OpenAndFill( TA_CDL3WHITESOLDIERS_Stream **stream, const double inOpen[], const double inHigh[], const double inLow[], const double inClose[], int historyLen, int *outBegIdx, int *outNBElement, int outInteger[] ) { if( !stream ) return TA_BAD_PARAM; *stream = NULL; if( historyLen < 1 ) return TA_OUT_OF_RANGE_START_INDEX; if( historyLen > TA_INDEX_MAX + 1 ) return TA_OUT_OF_RANGE_END_INDEX; if( !inOpen || !inHigh || !inLow || !inClose || !outBegIdx || !outNBElement || !outInteger ) return TA_BAD_PARAM; if( (const void *)outInteger == (const void *)inOpen || (const void *)outInteger == (const void *)inHigh || (const void *)outInteger == (const void *)inLow || (const void *)outInteger == (const void *)inClose ) return TA_BAD_PARAM; return TA_CDL3WHITESOLDIERS_OpenAndFillInternal( stream, inOpen, inHigh, inLow, inClose, 0, historyLen, outBegIdx, outNBElement, outInteger ); } /* Private function, not in public API. */ TA_RetCode TA_CDL3WHITESOLDIERS_OpenAndFillInternal( struct TA_CDL3WHITESOLDIERS_Stream **stream, const double inOpen[], const double inHigh[], const double inLow[], const double inClose[], int startIdx, int historyLen, int *outBegIdx, int *outNBElement, int outInteger[] ) { return TA_CDL3WHITESOLDIERS_OpenImpl( stream, inOpen, inHigh, inLow, inClose, startIdx, historyLen, outBegIdx, outNBElement, outInteger, 1 ); } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_Update( TA_CDL3WHITESOLDIERS_Stream *stream, double inOpen, double inHigh, double inLow, double inClose, int *outInteger ) { if( !stream ) return TA_BAD_PARAM; if( stream->outRangeBegIdx + stream->outRangeCount > TA_INDEX_MAX ) return TA_OUT_OF_RANGE_END_INDEX; if( !outInteger ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inOpen ) || !TA_IS_FINITE( inHigh ) || !TA_IS_FINITE( inLow ) || !TA_IS_FINITE( inClose ) ) return TA_BAD_PARAM; TA_CDL3WHITESOLDIERS_StepImpl( stream, inOpen, inHigh, inLow, inClose, outInteger ); stream->outRangeCount++; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_Peek( const TA_CDL3WHITESOLDIERS_Stream *stream, double inOpen, double inHigh, double inLow, double inClose, int *outInteger ) { const struct TA_CDL3WHITESOLDIERS_Stream *sp = stream; if( !stream || !outInteger ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inOpen ) || !TA_IS_FINITE( inHigh ) || !TA_IS_FINITE( inLow ) || !TA_IS_FINITE( inClose ) ) return TA_BAD_PARAM; if( ((sp->lag2_inClose >= sp->lag2_inOpen) ? 1 : -1) == 1 && /* 1st white */ (sp->lag2_inHigh - ((sp->lag2_inClose >= sp->lag2_inOpen) ? sp->lag2_inClose : sp->lag2_inOpen)) < TA_STREAM_CANDLEAVERAGE(ShadowVeryShort,sp->ShadowVeryShortPeriodTotal[2],sp->lag2_inOpen,sp->lag2_inHigh,sp->lag2_inLow,sp->lag2_inClose) && /* very short upper shadow */ ((sp->lag1_inClose >= sp->lag1_inOpen) ? 1 : -1) == 1 && /* 2nd white */ (sp->lag1_inHigh - ((sp->lag1_inClose >= sp->lag1_inOpen) ? sp->lag1_inClose : sp->lag1_inOpen)) < TA_STREAM_CANDLEAVERAGE(ShadowVeryShort,sp->ShadowVeryShortPeriodTotal[1],sp->lag1_inOpen,sp->lag1_inHigh,sp->lag1_inLow,sp->lag1_inClose) && /* very short upper shadow */ ((inClose >= inOpen) ? 1 : -1) == 1 && /* 3rd white */ (inHigh - ((inClose >= inOpen) ? inClose : inOpen)) < TA_STREAM_CANDLEAVERAGE(ShadowVeryShort,sp->ShadowVeryShortPeriodTotal[0],inOpen,inHigh,inLow,inClose) && /* very short upper shadow */ inClose > sp->lag1_inClose && sp->lag1_inClose > sp->lag2_inClose && /* consecutive higher closes */ sp->lag1_inOpen > sp->lag2_inOpen && /* 2nd opens within/near 1st real body */ sp->lag1_inOpen <= sp->lag2_inClose + TA_STREAM_CANDLEAVERAGE(Near,sp->NearPeriodTotal[2],sp->lag2_inOpen,sp->lag2_inHigh,sp->lag2_inLow,sp->lag2_inClose) && inOpen > sp->lag1_inOpen && /* 3rd opens within/near 2nd real body */ inOpen <= sp->lag1_inClose + TA_STREAM_CANDLEAVERAGE(Near,sp->NearPeriodTotal[1],sp->lag1_inOpen,sp->lag1_inHigh,sp->lag1_inLow,sp->lag1_inClose) && fabs(sp->lag1_inClose - sp->lag1_inOpen) > fabs(sp->lag2_inClose - sp->lag2_inOpen) - TA_STREAM_CANDLEAVERAGE(Far,sp->FarPeriodTotal[2],sp->lag2_inOpen,sp->lag2_inHigh,sp->lag2_inLow,sp->lag2_inClose) && /* 2nd not far shorter than 1st */ fabs(inClose - inOpen) > fabs(sp->lag1_inClose - sp->lag1_inOpen) - TA_STREAM_CANDLEAVERAGE(Far,sp->FarPeriodTotal[1],sp->lag1_inOpen,sp->lag1_inHigh,sp->lag1_inLow,sp->lag1_inClose) && /* 3rd not far shorter than 2nd */ fabs(inClose - inOpen) > TA_STREAM_CANDLEAVERAGE(BodyShort,sp->BodyShortPeriodTotal,inOpen,inHigh,inLow,inClose) ) /* not short real body */ { *outInteger= 100; } else { *outInteger= 0; } return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_Close( TA_CDL3WHITESOLDIERS_Stream *stream ) { TA_CDL3WHITESOLDIERS_ReleaseImpl( stream ); return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_Value( const TA_CDL3WHITESOLDIERS_Stream *stream, int *outInteger ) { if( !stream || !outInteger ) return TA_BAD_PARAM; *outInteger = stream->cur_outInteger; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_OutRange( const TA_CDL3WHITESOLDIERS_Stream *stream, int *outBegIdx, int *outNBElement ) { if( !stream || !outBegIdx || !outNBElement ) return TA_BAD_PARAM; *outBegIdx = stream->outRangeBegIdx; *outNBElement = stream->outRangeCount; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_Advance( TA_CDL3WHITESOLDIERS_Stream *stream ) { if( !stream ) return TA_BAD_PARAM; if( stream->outRangeBegIdx + stream->outRangeCount > TA_INDEX_MAX ) return TA_OUT_OF_RANGE_END_INDEX; stream->outRangeCount++; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_CDL3WHITESOLDIERS_Clone( const TA_CDL3WHITESOLDIERS_Stream *stream, TA_CDL3WHITESOLDIERS_Stream **clone ) { struct TA_CDL3WHITESOLDIERS_Stream *sp; if( !clone ) return TA_BAD_PARAM; *clone = NULL; if( !stream ) return TA_BAD_PARAM; sp = (struct TA_CDL3WHITESOLDIERS_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) return TA_ALLOC_ERR; *sp = *stream; sp->ring_BodyShortTrailingIdx_derived = NULL; sp->ring_FarTrailingIdx_derived = NULL; sp->ring_NearTrailingIdx_derived = NULL; sp->ring_ShadowVeryShortTrailingIdx_derived = NULL; if( stream->ring_BodyShortTrailingIdx_derived ) { size_t copyN = (size_t)(sp->ringCap_BodyShortTrailingIdx > 0 ? sp->ringCap_BodyShortTrailingIdx : 1); sp->ring_BodyShortTrailingIdx_derived = (double *)TA_Malloc( sizeof(double) * copyN ); if( !sp->ring_BodyShortTrailingIdx_derived ) { TA_CDL3WHITESOLDIERS_Close( sp ); return TA_ALLOC_ERR; } memcpy( sp->ring_BodyShortTrailingIdx_derived, stream->ring_BodyShortTrailingIdx_derived, sizeof(double) * copyN ); } if( stream->ring_FarTrailingIdx_derived ) { size_t copyN = (size_t)(sp->ringCap_FarTrailingIdx > 0 ? sp->ringCap_FarTrailingIdx : 1); sp->ring_FarTrailingIdx_derived = (double *)TA_Malloc( sizeof(double) * copyN ); if( !sp->ring_FarTrailingIdx_derived ) { TA_CDL3WHITESOLDIERS_Close( sp ); return TA_ALLOC_ERR; } memcpy( sp->ring_FarTrailingIdx_derived, stream->ring_FarTrailingIdx_derived, sizeof(double) * copyN ); } if( stream->ring_NearTrailingIdx_derived ) { size_t copyN = (size_t)(sp->ringCap_NearTrailingIdx > 0 ? sp->ringCap_NearTrailingIdx : 1); sp->ring_NearTrailingIdx_derived = (double *)TA_Malloc( sizeof(double) * copyN ); if( !sp->ring_NearTrailingIdx_derived ) { TA_CDL3WHITESOLDIERS_Close( sp ); return TA_ALLOC_ERR; } memcpy( sp->ring_NearTrailingIdx_derived, stream->ring_NearTrailingIdx_derived, sizeof(double) * copyN ); } if( stream->ring_ShadowVeryShortTrailingIdx_derived ) { size_t copyN = (size_t)(sp->ringCap_ShadowVeryShortTrailingIdx > 0 ? sp->ringCap_ShadowVeryShortTrailingIdx : 1); sp->ring_ShadowVeryShortTrailingIdx_derived = (double *)TA_Malloc( sizeof(double) * copyN ); if( !sp->ring_ShadowVeryShortTrailingIdx_derived ) { TA_CDL3WHITESOLDIERS_Close( sp ); return TA_ALLOC_ERR; } memcpy( sp->ring_ShadowVeryShortTrailingIdx_derived, stream->ring_ShadowVeryShortTrailingIdx_derived, sizeof(double) * copyN ); } *clone = sp; return TA_SUCCESS; }