/* 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 * ------------------------------------------------------------------- * MF Mario Fortier * CC Claude Code (AI assistant) * * Change history: * * MMDDYY BY Description * ------------------------------------------------------------------- * 112400 MF Template creation. * 052603 MF Adapt code to compile with .NET Managed C++ * 071026 MF,CC Fix #107. Guard the Fast-K division with TA_IS_ZERO, not an * exact `diff != 0.0`, so a machine-flat window yields 0 instead * of dividing a sub-epsilon residue into [0,100] noise (STOCHRSI). * 072026 MF,CC Fix #130. Never elect outSlowD as the K scratch buffer: %D's * in-place ma() destroyed the smoothed K before the final copy. * 082326 MF,CC Fix #253. Scale that guard to the window's own extremes: the * fixed band zeroed the whole output for any instrument quoted * small enough to fall under it. * 082726 MF,CC Fix #269. Answer a rejected %D ma() before the copy, not after: * the stale *outNBElement overran outSlowK by lookbackDSlow. * 090626 MF,CC Fix #390. Divide by the range, scale after: the hoisted * `(highest-lowest)/100.0` underflowed to 0.0 on a denormal * range that the guard still called "not flat". */ TA_LIB_API int TA_STOCH_Lookback( int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType ) { int retValue; if( (int)optInFastK_Period == TA_INTEGER_DEFAULT ) optInFastK_Period = 5; else if( (int)optInFastK_Period < 1 || (int)optInFastK_Period > 100000 ) return -1; if( (int)optInSlowK_Period == TA_INTEGER_DEFAULT ) optInSlowK_Period = 3; else if( (int)optInSlowK_Period < 1 || (int)optInSlowK_Period > 100000 ) return -1; if( (int)optInSlowK_MAType == TA_INTEGER_DEFAULT || optInSlowK_MAType == TA_MAType_DEFAULT ) optInSlowK_MAType = 0; else if( (int)optInSlowK_MAType < TA_MATYPE_MIN || (int)optInSlowK_MAType > TA_MATYPE_MAX ) return -1; if( (int)optInSlowD_Period == TA_INTEGER_DEFAULT ) optInSlowD_Period = 3; else if( (int)optInSlowD_Period < 1 || (int)optInSlowD_Period > 100000 ) return -1; if( (int)optInSlowD_MAType == TA_INTEGER_DEFAULT || optInSlowD_MAType == TA_MAType_DEFAULT ) optInSlowD_MAType = 0; else if( (int)optInSlowD_MAType < TA_MATYPE_MIN || (int)optInSlowD_MAType > TA_MATYPE_MAX ) return -1; /* Account for the initial data needed for Fast-K. */ retValue = optInFastK_Period - 1; /* Add the smoothing being done for %K slow */ retValue += TA_MA_Lookback(optInSlowK_Period,optInSlowK_MAType); /* Add the smoothing being done for %D slow. */ retValue += TA_MA_Lookback(optInSlowD_Period,optInSlowD_MAType); return retValue; } TA_LIB_API int TA_STOCH_DisplayShift( int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType, int outputIdx ) { if( TA_STOCH_Lookback( optInFastK_Period, optInSlowK_Period, optInSlowK_MAType, optInSlowD_Period, optInSlowD_MAType ) < 0 ) return INT_MIN; if( outputIdx < 0 || outputIdx >= 2 ) return INT_MIN; return 0; } TA_LIB_API TA_RetCode TA_STOCH( int startIdx, int endIdx, const double inHigh[], const double inLow[], const double inClose[], int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType, int *outBegIdx, int *outNBElement, double outSlowK[], double outSlowD[] ) { TA_RetCode retCode; double lowest; double highest; double tmp; double *tempBuffer; int outIdx; int lowestIdx; int highestIdx; int lookbackTotal; int lookbackK; int lookbackKSlow; int lookbackDSlow; int trailingIdx; int today; int i; int bufferIsAllocated; 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( (int)optInFastK_Period == TA_INTEGER_DEFAULT ) optInFastK_Period = 5; else if( (int)optInFastK_Period < 1 || (int)optInFastK_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowK_Period == TA_INTEGER_DEFAULT ) optInSlowK_Period = 3; else if( (int)optInSlowK_Period < 1 || (int)optInSlowK_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowK_MAType == TA_INTEGER_DEFAULT || optInSlowK_MAType == TA_MAType_DEFAULT ) optInSlowK_MAType = 0; else if( (int)optInSlowK_MAType < TA_MATYPE_MIN || (int)optInSlowK_MAType > TA_MATYPE_MAX ) return TA_BAD_PARAM; if( (int)optInSlowD_Period == TA_INTEGER_DEFAULT ) optInSlowD_Period = 3; else if( (int)optInSlowD_Period < 1 || (int)optInSlowD_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowD_MAType == TA_INTEGER_DEFAULT || optInSlowD_MAType == TA_MAType_DEFAULT ) optInSlowD_MAType = 0; else if( (int)optInSlowD_MAType < TA_MATYPE_MIN || (int)optInSlowD_MAType > TA_MATYPE_MAX ) 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( !outSlowK ) return TA_BAD_PARAM; if( !outSlowD ) return TA_BAD_PARAM; if( outSlowK == outSlowD ) return TA_BAD_PARAM; i = 0; /* With stochastic, there is a total of 4 different lines that * are defined: FASTK, FASTD, SLOWK and SLOWD. * * The D is the signal line usually drawn over its * corresponding K function. * * (Today's Close - LowestLow) * FASTK(Kperiod) = --------------------------- * 100 * (HighestHigh - LowestLow) * * FASTD(FastDperiod, MA type) = MA Smoothed FASTK over FastDperiod * * SLOWK(SlowKperiod, MA type) = MA Smoothed FASTK over SlowKperiod * * SLOWD(SlowDperiod, MA Type) = MA Smoothed SLOWK over SlowDperiod * * The HighestHigh and LowestLow are the extreme values among the * last 'Kperiod'. * * SLOWK and FASTD are equivalent when using the same period. * * The following shows how these four lines are made available in TA-LIB: * * TA_STOCH : Returns the SLOWK and SLOWD * TA_STOCHF : Returns the FASTK and FASTD * * The TA_STOCH function correspond to the more widely implemented version * found in many software/charting package. The TA_STOCHF is more rarely * used because its higher volatility cause often whipsaws. */ /* Identify the lookback needed. */ lookbackK = optInFastK_Period - 1; lookbackKSlow = TA_MA_Lookback(optInSlowK_Period,optInSlowK_MAType); lookbackDSlow = TA_MA_Lookback(optInSlowD_Period,optInSlowD_MAType); lookbackTotal = lookbackK + lookbackDSlow + lookbackKSlow; /* 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 ) { /* Succeed... but no data in the output. */ *outBegIdx= 0; *outNBElement= 0; return TA_SUCCESS; } /* Do the K calculation: * * Kt = 100 x ((Ct-Lt)/(Ht-Lt)) * * Kt is today stochastic * Ct is today closing price. * Lt is the lowest price of the last K Period (including today) * Ht is the highest price of the last K Period (including today) */ /* Proceed with the calculation for the requested range. * Note that this algorithm allows the input and * output to be the same buffer. */ outIdx = 0; /* Calculate just enough K for ending up with the caller * requested range. (The range of k must consider all * the lookback involve with the smoothing). */ trailingIdx = startIdx - lookbackTotal; today = trailingIdx + lookbackK; highestIdx = -1; lowestIdx = highestIdx; lowest = 0.0; highest = lowest; /* Allocate a temporary buffer large enough to * store the K. * * When outSlowK aliases a price input the caller buffer doubles as the * scratch, saving one allocation: the K writes trail the min/max window * reads, and the final memmove is overlap-safe. outSlowD must NOT be * elected: the %D ma() below would then run in place over the smoothed K * that the memmove into outSlowK still needs (issue #130). */ bufferIsAllocated = 0; if( outSlowK == inHigh || outSlowK == inLow || outSlowK == inClose ) { tempBuffer = outSlowK; } else { bufferIsAllocated = 1; tempBuffer = malloc((endIdx - today + 1) * sizeof(double)); if( !tempBuffer ) { *outBegIdx= 0; *outNBElement= 0; return TA_ALLOC_ERR; } } /* Do the K calculation */ while( today <= endIdx ) { /* Set the lowest low */ tmp = inLow[today]; if( lowestIdx < trailingIdx ) { lowestIdx = trailingIdx; lowest = inLow[lowestIdx]; i = lowestIdx; while( ++i <= today ) { tmp = inLow[i]; if( tmp < lowest ) { lowestIdx = i; lowest = tmp; } } } else if( tmp <= lowest ) { lowestIdx = today; lowest = tmp; } /* Set the highest high */ tmp = inHigh[today]; if( highestIdx < trailingIdx ) { highestIdx = trailingIdx; highest = inHigh[highestIdx]; i = highestIdx; while( ++i <= today ) { tmp = inHigh[i]; if( tmp > highest ) { highestIdx = i; highest = tmp; } } } else if( tmp >= highest ) { highestIdx = today; highest = tmp; } /* Divide by the range itself and scale after: the guard has to test the * very expression the division uses, or a scaling step can carry a * guarded-non-zero into a zero divisor. * * The band is the range against ITS OWN two extremes, not a fixed * constant: the range carries the quote unit, so a constant answers * "flat" for every window of an instrument quoted below it (issue #253). * It absorbs the machine-flat window an exact test would divide into * [0,100] noise (issue #107 / STOCHRSI). */ if( !TA_IS_ZERO_SCALED(highest - lowest, fabs(highest) + fabs(lowest)) ) { tempBuffer[outIdx++] = (inClose[today] - lowest) / (highest - lowest) * 100.0; } else { tempBuffer[outIdx++] = 0.0; } trailingIdx += 1; today += 1; } /* Un-smoothed K calculation completed. This K calculation is not returned * to the caller. It is always smoothed and then return. * Some documentation will refer to the smoothed version as being * "K-Slow", but often this end up to be shorten to "K". */ retCode = TA_MA(0,outIdx - 1,tempBuffer,optInSlowK_Period,optInSlowK_MAType,outBegIdx,outNBElement,tempBuffer); if( retCode != TA_SUCCESS || (int)*outNBElement == 0 ) { if( bufferIsAllocated ) { free(tempBuffer); } /* Something wrong happen? No further data? */ *outBegIdx= 0; *outNBElement= 0; return retCode; } /* Calculate the %D which is simply a moving average of * the already smoothed %K. */ retCode = TA_MA(0,(int)*outNBElement - 1,tempBuffer,optInSlowD_Period,optInSlowD_MAType,outBegIdx,outNBElement,outSlowD); if( retCode != TA_SUCCESS ) { /* Something wrong happen while processing %D? */ if( bufferIsAllocated ) { free(tempBuffer); } *outBegIdx= 0; *outNBElement= 0; return retCode; } /* Copy tempBuffer into the caller buffer. * (Calculation could not be done directly in the * caller buffer because more input data then the * requested range was needed for doing %D). */ /* memmove, not memcpy: tempBuffer aliases outSlowK when the caller buffer is * reused as scratch, so source and destination overlap (issue #94). */ memmove(outSlowK,&tempBuffer[lookbackDSlow],(int)*outNBElement * sizeof(double)); if( bufferIsAllocated ) { free(tempBuffer); } /* Note: Keep the outBegIdx relative to the * caller input before returning. */ *outBegIdx= startIdx; return TA_SUCCESS; } TA_RetCode TA_S_STOCH( int startIdx, int endIdx, const float inHigh[], const float inLow[], const float inClose[], int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType, int *outBegIdx, int *outNBElement, double outSlowK[], double outSlowD[] ) { TA_RetCode retCode; double lowest; double highest; double tmp; double *tempBuffer; int outIdx; int lowestIdx; int highestIdx; int lookbackTotal; int lookbackK; int lookbackKSlow; int lookbackDSlow; int trailingIdx; int today; int i; int bufferIsAllocated; 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( (int)optInFastK_Period == TA_INTEGER_DEFAULT ) optInFastK_Period = 5; else if( (int)optInFastK_Period < 1 || (int)optInFastK_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowK_Period == TA_INTEGER_DEFAULT ) optInSlowK_Period = 3; else if( (int)optInSlowK_Period < 1 || (int)optInSlowK_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowK_MAType == TA_INTEGER_DEFAULT || optInSlowK_MAType == TA_MAType_DEFAULT ) optInSlowK_MAType = 0; else if( (int)optInSlowK_MAType < TA_MATYPE_MIN || (int)optInSlowK_MAType > TA_MATYPE_MAX ) return TA_BAD_PARAM; if( (int)optInSlowD_Period == TA_INTEGER_DEFAULT ) optInSlowD_Period = 3; else if( (int)optInSlowD_Period < 1 || (int)optInSlowD_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowD_MAType == TA_INTEGER_DEFAULT || optInSlowD_MAType == TA_MAType_DEFAULT ) optInSlowD_MAType = 0; else if( (int)optInSlowD_MAType < TA_MATYPE_MIN || (int)optInSlowD_MAType > TA_MATYPE_MAX ) 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( !outSlowK ) return TA_BAD_PARAM; if( !outSlowD ) return TA_BAD_PARAM; if( outSlowK == outSlowD ) return TA_BAD_PARAM; i = 0; lookbackK = optInFastK_Period - 1; lookbackKSlow = TA_MA_Lookback(optInSlowK_Period,optInSlowK_MAType); lookbackDSlow = TA_MA_Lookback(optInSlowD_Period,optInSlowD_MAType); lookbackTotal = lookbackK + lookbackDSlow + lookbackKSlow; if( startIdx < lookbackTotal ) { startIdx = lookbackTotal; } if( startIdx > endIdx ) { *outBegIdx= 0; *outNBElement= 0; return TA_SUCCESS; } outIdx = 0; trailingIdx = startIdx - lookbackTotal; today = trailingIdx + lookbackK; highestIdx = -1; lowestIdx = highestIdx; lowest = 0.0; highest = lowest; bufferIsAllocated = 0; if( 0 || 0 || 0 ) { tempBuffer = outSlowK; } else { bufferIsAllocated = 1; tempBuffer = malloc((endIdx - today + 1) * sizeof(double)); if( !tempBuffer ) { *outBegIdx= 0; *outNBElement= 0; return TA_ALLOC_ERR; } } while( today <= endIdx ) { tmp = (double)inLow[today]; if( lowestIdx < trailingIdx ) { lowestIdx = trailingIdx; lowest = (double)inLow[lowestIdx]; i = lowestIdx; while( ++i <= today ) { tmp = (double)inLow[i]; if( tmp < lowest ) { lowestIdx = i; lowest = tmp; } } } else if( tmp <= lowest ) { lowestIdx = today; lowest = tmp; } tmp = (double)inHigh[today]; if( highestIdx < trailingIdx ) { highestIdx = trailingIdx; highest = (double)inHigh[highestIdx]; i = highestIdx; while( ++i <= today ) { tmp = (double)inHigh[i]; if( tmp > highest ) { highestIdx = i; highest = tmp; } } } else if( tmp >= highest ) { highestIdx = today; highest = tmp; } if( !TA_IS_ZERO_SCALED(highest - lowest, fabs(highest) + fabs(lowest)) ) { tempBuffer[outIdx++] = ((double)inClose[today] - lowest) / (highest - lowest) * 100.0; } else { tempBuffer[outIdx++] = 0.0; } trailingIdx += 1; today += 1; } retCode = TA_MA(0,outIdx - 1,tempBuffer,optInSlowK_Period,optInSlowK_MAType,outBegIdx,outNBElement,tempBuffer); if( retCode != TA_SUCCESS || (int)*outNBElement == 0 ) { if( bufferIsAllocated ) { free(tempBuffer); } *outBegIdx= 0; *outNBElement= 0; return retCode; } retCode = TA_MA(0,(int)*outNBElement - 1,tempBuffer,optInSlowD_Period,optInSlowD_MAType,outBegIdx,outNBElement,outSlowD); if( retCode != TA_SUCCESS ) { if( bufferIsAllocated ) { free(tempBuffer); } *outBegIdx= 0; *outNBElement= 0; return retCode; } memmove(outSlowK,&tempBuffer[lookbackDSlow],(int)*outNBElement * sizeof(double)); if( bufferIsAllocated ) { free(tempBuffer); } *outBegIdx= startIdx; return TA_SUCCESS; } /**** Streaming API *****/ struct TA_STOCH_Stream { /* The bars this handle has an output for (see TA_STOCH_OutRange). */ int outRangeBegIdx; int outRangeCount; /* The value(s) at the last bar the stream counted (see TA_STOCH_Value). */ double cur_outSlowK; double cur_outSlowD; int optInFastK_Period; int optInSlowK_Period; TA_MAType optInSlowK_MAType; int optInSlowD_Period; TA_MAType optInSlowD_MAType; double lowest; double highest; int lowestIdx; int highestIdx; int trailingIdx; int i; int today; int xCap; int xPhys; int xMask; double *x_inHigh; double *x_inLow; double *x_inClose; TA_MA_Stream *sub0; TA_MA_Stream *sub1; }; /* Private function, not in public API. */ static void TA_STOCH_ReleaseImpl( struct TA_STOCH_Stream *sp ) { if( !sp ) return; if( sp->x_inHigh ) TA_Free( sp->x_inHigh ); if( sp->x_inLow ) TA_Free( sp->x_inLow ); if( sp->x_inClose ) TA_Free( sp->x_inClose ); TA_Free( sp ); } /* Private function, not in public API. */ static TA_RetCode TA_STOCH_StepImpl( struct TA_STOCH_Stream *sp, double inHigh, double inLow, double inClose, double *outSlowK, double *outSlowD ) { double cur_tempBuffer = 0.0; double cur_outSlowD = 0.0; double tmp; sp->x_inHigh[sp->today & sp->xMask] = inHigh; sp->x_inLow[sp->today & sp->xMask] = inLow; sp->x_inClose[sp->today & sp->xMask] = inClose; /* Set the lowest low */ tmp = sp->x_inLow[sp->today & sp->xMask]; if( sp->lowestIdx < sp->trailingIdx ) { sp->lowestIdx = sp->trailingIdx; sp->lowest = sp->x_inLow[sp->lowestIdx & sp->xMask]; sp->i = sp->lowestIdx; while( ++sp->i <= sp->today ) { tmp = sp->x_inLow[sp->i & sp->xMask]; if( tmp < sp->lowest ) { sp->lowestIdx = sp->i; sp->lowest = tmp; } } } else if( tmp <= sp->lowest ) { sp->lowestIdx = sp->today; sp->lowest = tmp; } /* Set the highest high */ tmp = sp->x_inHigh[sp->today & sp->xMask]; if( sp->highestIdx < sp->trailingIdx ) { sp->highestIdx = sp->trailingIdx; sp->highest = sp->x_inHigh[sp->highestIdx & sp->xMask]; sp->i = sp->highestIdx; while( ++sp->i <= sp->today ) { tmp = sp->x_inHigh[sp->i & sp->xMask]; if( tmp > sp->highest ) { sp->highestIdx = sp->i; sp->highest = tmp; } } } else if( tmp >= sp->highest ) { sp->highestIdx = sp->today; sp->highest = tmp; } /* Divide by the range itself and scale after: the guard has to test the * very expression the division uses, or a scaling step can carry a * guarded-non-zero into a zero divisor. * * The band is the range against ITS OWN two extremes, not a fixed * constant: the range carries the quote unit, so a constant answers * "flat" for every window of an instrument quoted below it (issue #253). * It absorbs the machine-flat window an exact test would divide into * [0,100] noise (issue #107 / STOCHRSI). */ if( !TA_IS_ZERO_SCALED(sp->highest - sp->lowest, fabs(sp->highest) + fabs(sp->lowest)) ) { cur_tempBuffer = (sp->x_inClose[sp->today & sp->xMask] - sp->lowest) / (sp->highest - sp->lowest) * 100.0; } else { cur_tempBuffer = 0.0; } sp->trailingIdx += 1; sp->today += 1; /* Pipeline the new bar through the sub-streams (batch tail order). */ { TA_RetCode subRc = TA_MA_Update( sp->sub0, cur_tempBuffer, &cur_tempBuffer ); if( subRc != TA_SUCCESS ) return subRc; } { TA_RetCode subRc = TA_MA_Update( sp->sub1, cur_tempBuffer, &cur_outSlowD ); if( subRc != TA_SUCCESS ) return subRc; } *outSlowK = cur_tempBuffer; *outSlowD = cur_outSlowD; return TA_SUCCESS; } static TA_RetCode TA_STOCH_OpenImpl( struct TA_STOCH_Stream **stream, const double inHigh[], const double inLow[], const double inClose[], int startIdx, int historyLen, int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType, int *outBegIdx, int *outNBElement, double outSlowK[], double outSlowD[], int outStride ) { struct TA_STOCH_Stream *sp; int endIdx; int dummyBegIdx; int dummyNBElement; TA_RetCode subRc; double subOpenDummy; double *sc_outSlowK; double *sc_outSlowD; TA_MA_Stream *sub0; TA_MA_Stream *sub1; 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( !inHigh || !inLow || !inClose || !outSlowK || !outSlowD ) return TA_BAD_PARAM; if( (int)optInFastK_Period == TA_INTEGER_DEFAULT ) optInFastK_Period = 5; else if( (int)optInFastK_Period < 1 || (int)optInFastK_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowK_Period == TA_INTEGER_DEFAULT ) optInSlowK_Period = 3; else if( (int)optInSlowK_Period < 1 || (int)optInSlowK_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowK_MAType == TA_INTEGER_DEFAULT || optInSlowK_MAType == TA_MAType_DEFAULT ) optInSlowK_MAType = 0; else if( (int)optInSlowK_MAType < TA_MATYPE_MIN || (int)optInSlowK_MAType > TA_MATYPE_MAX ) return TA_BAD_PARAM; if( (int)optInSlowD_Period == TA_INTEGER_DEFAULT ) optInSlowD_Period = 3; else if( (int)optInSlowD_Period < 1 || (int)optInSlowD_Period > 100000 ) return TA_BAD_PARAM; if( (int)optInSlowD_MAType == TA_INTEGER_DEFAULT || optInSlowD_MAType == TA_MAType_DEFAULT ) optInSlowD_MAType = 0; else if( (int)optInSlowD_MAType < TA_MATYPE_MIN || (int)optInSlowD_MAType > TA_MATYPE_MAX ) return TA_BAD_PARAM; if( startIdx > historyLen - 1 ) { *outBegIdx = 0; *outNBElement = 0; return TA_INSUFFICIENT_HISTORY; } endIdx = historyLen - 1; dummyBegIdx = 0; dummyNBElement = 0; subRc = TA_SUCCESS; subOpenDummy = 0.0; sub0 = NULL; sub1 = NULL; if( outStride ) sc_outSlowK = outSlowK; else { sc_outSlowK = (double *)TA_Malloc( sizeof(double) * (size_t)historyLen ); if( !sc_outSlowK ) { return TA_ALLOC_ERR; } } if( outStride ) sc_outSlowD = outSlowD; else { sc_outSlowD = (double *)TA_Malloc( sizeof(double) * (size_t)historyLen ); if( !sc_outSlowD ) { TA_Free( sc_outSlowK ); return TA_ALLOC_ERR; } } { TA_RetCode retCode; double lowest; double highest; double tmp; double *tempBuffer; int outIdx; int lowestIdx; int highestIdx; int lookbackTotal; int lookbackK; int lookbackKSlow; int lookbackDSlow; int trailingIdx; int today; int i = 0; int bufferIsAllocated; /* With stochastic, there is a total of 4 different lines that * are defined: FASTK, FASTD, SLOWK and SLOWD. * * The D is the signal line usually drawn over its * corresponding K function. * * (Today's Close - LowestLow) * FASTK(Kperiod) = --------------------------- * 100 * (HighestHigh - LowestLow) * * FASTD(FastDperiod, MA type) = MA Smoothed FASTK over FastDperiod * * SLOWK(SlowKperiod, MA type) = MA Smoothed FASTK over SlowKperiod * * SLOWD(SlowDperiod, MA Type) = MA Smoothed SLOWK over SlowDperiod * * The HighestHigh and LowestLow are the extreme values among the * last 'Kperiod'. * * SLOWK and FASTD are equivalent when using the same period. * * The following shows how these four lines are made available in TA-LIB: * * TA_STOCH : Returns the SLOWK and SLOWD * TA_STOCHF : Returns the FASTK and FASTD * * The TA_STOCH function correspond to the more widely implemented version * found in many software/charting package. The TA_STOCHF is more rarely * used because its higher volatility cause often whipsaws. */ /* Identify the lookback needed. */ lookbackK = optInFastK_Period - 1; lookbackKSlow = TA_MA_Lookback(optInSlowK_Period,optInSlowK_MAType); lookbackDSlow = TA_MA_Lookback(optInSlowD_Period,optInSlowD_MAType); lookbackTotal = lookbackK + lookbackDSlow + lookbackKSlow; /* 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 ) { /* Succeed... but no data in the output. */ dummyBegIdx = 0; dummyNBElement = 0; *outBegIdx = 0; *outNBElement = 0; TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); return TA_INSUFFICIENT_HISTORY; } /* Do the K calculation: * * Kt = 100 x ((Ct-Lt)/(Ht-Lt)) * * Kt is today stochastic * Ct is today closing price. * Lt is the lowest price of the last K Period (including today) * Ht is the highest price of the last K Period (including today) */ /* Proceed with the calculation for the requested range. * Note that this algorithm allows the input and * output to be the same buffer. */ outIdx = 0; /* Calculate just enough K for ending up with the caller * requested range. (The range of k must consider all * the lookback involve with the smoothing). */ trailingIdx = startIdx - lookbackTotal; today = trailingIdx + lookbackK; highestIdx = -1; lowestIdx = highestIdx; lowest = 0.0; highest = lowest; /* Allocate a temporary buffer large enough to * store the K. * * When outSlowK aliases a price input the caller buffer doubles as the * scratch, saving one allocation: the K writes trail the min/max window * reads, and the final memmove is overlap-safe. outSlowD must NOT be * elected: the %D ma() below would then run in place over the smoothed K * that the memmove into outSlowK still needs (issue #130). */ bufferIsAllocated = 0; if( sc_outSlowK == inHigh || sc_outSlowK == inLow || sc_outSlowK == inClose ) { tempBuffer = sc_outSlowK; } else { bufferIsAllocated = 1; tempBuffer = malloc((endIdx - today + 1) * sizeof(double)); if( !tempBuffer ) { TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); return TA_ALLOC_ERR; } } /* Do the K calculation */ while( today <= endIdx ) { /* Set the lowest low */ tmp = inLow[today]; if( lowestIdx < trailingIdx ) { lowestIdx = trailingIdx; lowest = inLow[lowestIdx]; i = lowestIdx; while( ++i <= today ) { tmp = inLow[i]; if( tmp < lowest ) { lowestIdx = i; lowest = tmp; } } } else if( tmp <= lowest ) { lowestIdx = today; lowest = tmp; } /* Set the highest high */ tmp = inHigh[today]; if( highestIdx < trailingIdx ) { highestIdx = trailingIdx; highest = inHigh[highestIdx]; i = highestIdx; while( ++i <= today ) { tmp = inHigh[i]; if( tmp > highest ) { highestIdx = i; highest = tmp; } } } else if( tmp >= highest ) { highestIdx = today; highest = tmp; } /* Divide by the range itself and scale after: the guard has to test the * very expression the division uses, or a scaling step can carry a * guarded-non-zero into a zero divisor. * * The band is the range against ITS OWN two extremes, not a fixed * constant: the range carries the quote unit, so a constant answers * "flat" for every window of an instrument quoted below it (issue #253). * It absorbs the machine-flat window an exact test would divide into * [0,100] noise (issue #107 / STOCHRSI). */ if( !TA_IS_ZERO_SCALED(highest - lowest, fabs(highest) + fabs(lowest)) ) { tempBuffer[outIdx++] = (inClose[today] - lowest) / (highest - lowest) * 100.0; } else { tempBuffer[outIdx++] = 0.0; } trailingIdx += 1; today += 1; } /* Un-smoothed K calculation completed. This K calculation is not returned * to the caller. It is always smoothed and then return. * Some documentation will refer to the smoothed version as being * "K-Slow", but often this end up to be shorten to "K". */ /* Sub-stream 0: ma over `tempBuffer`, warmed from bar 0 up to the * sub-call's own startIdx (the seeding point). */ { subRc = TA_MA_OpenInternal( &sub0, tempBuffer, (0), (outIdx - 1) + 1, optInSlowK_Period, optInSlowK_MAType, &subOpenDummy ); if( subRc != TA_SUCCESS ) { if( bufferIsAllocated ) { free(tempBuffer); } TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); return subRc; } } retCode = TA_MA(0,outIdx - 1,tempBuffer,optInSlowK_Period,optInSlowK_MAType,&dummyBegIdx,&dummyNBElement,tempBuffer); if( retCode != TA_SUCCESS || (int)dummyNBElement == 0 ) { if( bufferIsAllocated ) { free(tempBuffer); } /* Something wrong happen? No further data? */ dummyBegIdx = 0; dummyNBElement = 0; TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); return retCode; } /* Calculate the %D which is simply a moving average of * the already smoothed %K. */ /* Sub-stream 1: ma over `tempBuffer`, warmed from bar 0 up to the * sub-call's own startIdx (the seeding point). */ { subRc = TA_MA_OpenAndFillInternal( &sub1, tempBuffer, (0), ((int)dummyNBElement - 1) + 1, optInSlowD_Period, optInSlowD_MAType, &dummyBegIdx, &dummyNBElement, sc_outSlowD ); if( subRc != TA_SUCCESS ) { if( bufferIsAllocated ) { free(tempBuffer); } TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); return subRc; } } retCode = subRc; if( retCode != TA_SUCCESS ) { /* Something wrong happen while processing %D? */ if( bufferIsAllocated ) { free(tempBuffer); } dummyBegIdx = 0; dummyNBElement = 0; TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); return retCode; } /* Copy tempBuffer into the caller buffer. * (Calculation could not be done directly in the * caller buffer because more input data then the * requested range was needed for doing %D). */ /* memmove, not memcpy: tempBuffer aliases outSlowK when the caller buffer is * reused as scratch, so source and destination overlap (issue #94). */ memmove(sc_outSlowK,&tempBuffer[lookbackDSlow],(int)dummyNBElement * sizeof(double)); if( bufferIsAllocated ) { free(tempBuffer); } /* Note: Keep the outBegIdx relative to the * caller input before returning. */ dummyBegIdx = startIdx; /* Capture the live producer state + sub handles. */ if( dummyNBElement < 1 ) { *outBegIdx = 0; *outNBElement = 0; TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); return TA_INSUFFICIENT_HISTORY; } sp = (struct TA_STOCH_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) { TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); return TA_ALLOC_ERR; } memset( sp, 0, sizeof(*sp) ); sp->optInFastK_Period = optInFastK_Period; sp->optInSlowK_Period = optInSlowK_Period; sp->optInSlowK_MAType = optInSlowK_MAType; sp->optInSlowD_Period = optInSlowD_Period; sp->optInSlowD_MAType = optInSlowD_MAType; sp->lowest = lowest; sp->highest = highest; sp->lowestIdx = lowestIdx; sp->highestIdx = highestIdx; sp->trailingIdx = trailingIdx; sp->i = i; sp->today = today; sp->xCap = (int)(today - trailingIdx) + 1; if( sp->xCap < 1 || sp->xCap > historyLen ) { TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); TA_STOCH_ReleaseImpl( sp ); return TA_INTERNAL_ERROR(385); } sp->xPhys = 1; while( sp->xPhys < sp->xCap ) sp->xPhys <<= 1; sp->xMask = sp->xPhys - 1; sp->x_inHigh = (double *)TA_Malloc( sizeof(double) * (size_t)sp->xPhys ); if( !sp->x_inHigh ) { TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); TA_STOCH_ReleaseImpl( sp ); return TA_ALLOC_ERR; } sp->x_inLow = (double *)TA_Malloc( sizeof(double) * (size_t)sp->xPhys ); if( !sp->x_inLow ) { TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); TA_STOCH_ReleaseImpl( sp ); return TA_ALLOC_ERR; } sp->x_inClose = (double *)TA_Malloc( sizeof(double) * (size_t)sp->xPhys ); if( !sp->x_inClose ) { TA_MA_Close( sub0 ); TA_MA_Close( sub1 ); if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); TA_STOCH_ReleaseImpl( sp ); return TA_ALLOC_ERR; } { int fillJ; for( fillJ = historyLen - sp->xCap; fillJ < historyLen; fillJ++ ) { sp->x_inHigh[fillJ & sp->xMask] = inHigh[fillJ]; sp->x_inLow[fillJ & sp->xMask] = inLow[fillJ]; sp->x_inClose[fillJ & sp->xMask] = inClose[fillJ]; } } sp->sub0 = sub0; sp->sub1 = sub1; *outBegIdx = dummyBegIdx; *outNBElement = dummyNBElement; if( !outStride ) outSlowK[0] = sc_outSlowK[dummyNBElement - 1]; if( !outStride ) outSlowD[0] = sc_outSlowD[dummyNBElement - 1]; if( !outStride ) TA_Free( sc_outSlowK ); if( !outStride ) TA_Free( sc_outSlowD ); sp->outRangeBegIdx = *outBegIdx; sp->outRangeCount = *outNBElement; sp->cur_outSlowK = outSlowK[(*outNBElement - 1) * outStride]; sp->cur_outSlowD = outSlowD[(*outNBElement - 1) * outStride]; *stream = sp; return TA_SUCCESS; } } /* Private function, not in public API. */ TA_RetCode TA_STOCH_OpenInternal( struct TA_STOCH_Stream **stream, const double inHigh[], const double inLow[], const double inClose[], int startIdx, int historyLen, int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType, double *outSlowK, double *outSlowD ) { TA_RetCode retCode; int dummyBegIdx = 0; int dummyNBElement = 0; double sink_outSlowK = 0.0; double sink_outSlowD = 0.0; retCode = TA_STOCH_OpenImpl( stream, inHigh, inLow, inClose, startIdx, historyLen, optInFastK_Period, optInSlowK_Period, optInSlowK_MAType, optInSlowD_Period, optInSlowD_MAType, &dummyBegIdx, &dummyNBElement, &sink_outSlowK, &sink_outSlowD, 0 ); if( retCode == TA_SUCCESS ) { *outSlowK = sink_outSlowK; *outSlowD = sink_outSlowD; } return retCode; } TA_LIB_API TA_RetCode TA_STOCH_Open( TA_STOCH_Stream **stream, const double inHigh[], const double inLow[], const double inClose[], int historyLen, int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType, double *outSlowK, double *outSlowD ) { 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( !inHigh || !inLow || !inClose || !outSlowK || !outSlowD ) return TA_BAD_PARAM; return TA_STOCH_OpenInternal( stream, inHigh, inLow, inClose, 0, historyLen, optInFastK_Period, optInSlowK_Period, optInSlowK_MAType, optInSlowD_Period, optInSlowD_MAType, outSlowK, outSlowD ); } TA_LIB_API TA_RetCode TA_STOCH_OpenAndFill( TA_STOCH_Stream **stream, const double inHigh[], const double inLow[], const double inClose[], int historyLen, int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType, int *outBegIdx, int *outNBElement, double outSlowK[], double outSlowD[] ) { 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( !inHigh || !inLow || !inClose || !outBegIdx || !outNBElement || !outSlowK || !outSlowD ) return TA_BAD_PARAM; if( (const void *)outSlowK == (const void *)inHigh || (const void *)outSlowK == (const void *)inLow || (const void *)outSlowK == (const void *)inClose || (const void *)outSlowD == (const void *)inHigh || (const void *)outSlowD == (const void *)inLow || (const void *)outSlowD == (const void *)inClose || (const void *)outSlowK == (const void *)outSlowD ) return TA_BAD_PARAM; return TA_STOCH_OpenAndFillInternal( stream, inHigh, inLow, inClose, 0, historyLen, optInFastK_Period, optInSlowK_Period, optInSlowK_MAType, optInSlowD_Period, optInSlowD_MAType, outBegIdx, outNBElement, outSlowK, outSlowD ); } /* Private function, not in public API. */ TA_RetCode TA_STOCH_OpenAndFillInternal( struct TA_STOCH_Stream **stream, const double inHigh[], const double inLow[], const double inClose[], int startIdx, int historyLen, int optInFastK_Period, int optInSlowK_Period, TA_MAType optInSlowK_MAType, int optInSlowD_Period, TA_MAType optInSlowD_MAType, int *outBegIdx, int *outNBElement, double outSlowK[], double outSlowD[] ) { return TA_STOCH_OpenImpl( stream, inHigh, inLow, inClose, startIdx, historyLen, optInFastK_Period, optInSlowK_Period, optInSlowK_MAType, optInSlowD_Period, optInSlowD_MAType, outBegIdx, outNBElement, outSlowK, outSlowD, 1 ); } TA_LIB_API TA_RetCode TA_STOCH_Update( TA_STOCH_Stream *stream, double inHigh, double inLow, double inClose, double *outSlowK, double *outSlowD ) { TA_RetCode retCode; if( !stream ) return TA_BAD_PARAM; if( stream->outRangeBegIdx + stream->outRangeCount > TA_INDEX_MAX ) return TA_OUT_OF_RANGE_END_INDEX; if( !outSlowK || !outSlowD ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inHigh ) || !TA_IS_FINITE( inLow ) || !TA_IS_FINITE( inClose ) ) return TA_BAD_PARAM; retCode = TA_STOCH_StepImpl( stream, inHigh, inLow, inClose, outSlowK, outSlowD ); if( retCode != TA_SUCCESS ) return retCode; stream->cur_outSlowK = *outSlowK; stream->cur_outSlowD = *outSlowD; stream->outRangeCount++; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_STOCH_Peek( const TA_STOCH_Stream *stream, double inHigh, double inLow, double inClose, double *outSlowK, double *outSlowD ) { const struct TA_STOCH_Stream *sp = stream; double cur_tempBuffer = 0.0; double cur_outSlowD = 0.0; double tmp; double highest; int highestIdx; int i; double lowest; int lowestIdx; double *x_inClose; double *x_inHigh; double *x_inLow; int pkSlot0 = -1; double pkVal0 = 0.0; int pkSlot1 = -1; double pkVal1 = 0.0; int pkSlot2 = -1; double pkVal2 = 0.0; if( !stream || !outSlowK || !outSlowD ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inHigh ) || !TA_IS_FINITE( inLow ) || !TA_IS_FINITE( inClose ) ) return TA_BAD_PARAM; highest = sp->highest; highestIdx = sp->highestIdx; i = sp->i; lowest = sp->lowest; lowestIdx = sp->lowestIdx; x_inClose = sp->x_inClose; x_inHigh = sp->x_inHigh; x_inLow = sp->x_inLow; pkSlot0 = sp->today & sp->xMask; pkVal0 = inHigh; pkSlot1 = sp->today & sp->xMask; pkVal1 = inLow; pkSlot2 = sp->today & sp->xMask; pkVal2 = inClose; /* Set the lowest low */ tmp = ((sp->today & sp->xMask) != pkSlot1) ? x_inLow[sp->today & sp->xMask] : pkVal1; if( lowestIdx < sp->trailingIdx ) { lowestIdx = sp->trailingIdx; lowest = ((lowestIdx & sp->xMask) != pkSlot1) ? x_inLow[lowestIdx & sp->xMask] : pkVal1; i = lowestIdx; while( ++i <= sp->today ) { tmp = ((i & sp->xMask) != pkSlot1) ? x_inLow[i & sp->xMask] : pkVal1; if( tmp < lowest ) { lowestIdx = i; lowest = tmp; } } } else if( tmp <= lowest ) { lowestIdx = sp->today; lowest = tmp; } /* Set the highest high */ tmp = ((sp->today & sp->xMask) != pkSlot0) ? x_inHigh[sp->today & sp->xMask] : pkVal0; if( highestIdx < sp->trailingIdx ) { highestIdx = sp->trailingIdx; highest = ((highestIdx & sp->xMask) != pkSlot0) ? x_inHigh[highestIdx & sp->xMask] : pkVal0; i = highestIdx; while( ++i <= sp->today ) { tmp = ((i & sp->xMask) != pkSlot0) ? x_inHigh[i & sp->xMask] : pkVal0; if( tmp > highest ) { highestIdx = i; highest = tmp; } } } else if( tmp >= highest ) { highestIdx = sp->today; highest = tmp; } /* Divide by the range itself and scale after: the guard has to test the * very expression the division uses, or a scaling step can carry a * guarded-non-zero into a zero divisor. * * The band is the range against ITS OWN two extremes, not a fixed * constant: the range carries the quote unit, so a constant answers * "flat" for every window of an instrument quoted below it (issue #253). * It absorbs the machine-flat window an exact test would divide into * [0,100] noise (issue #107 / STOCHRSI). */ if( !TA_IS_ZERO_SCALED(highest - lowest, fabs(highest) + fabs(lowest)) ) { cur_tempBuffer = ((((sp->today & sp->xMask) != pkSlot2) ? x_inClose[sp->today & sp->xMask] : pkVal2) - lowest) / (highest - lowest) * 100.0; } else { cur_tempBuffer = 0.0; } /* Pipeline the new bar through the sub-streams (batch tail order). */ { TA_RetCode subRc = TA_MA_Peek( (const TA_MA_Stream *)sp->sub0, cur_tempBuffer, &cur_tempBuffer ); if( subRc != TA_SUCCESS ) return subRc; } { TA_RetCode subRc = TA_MA_Peek( (const TA_MA_Stream *)sp->sub1, cur_tempBuffer, &cur_outSlowD ); if( subRc != TA_SUCCESS ) return subRc; } *outSlowK = cur_tempBuffer; *outSlowD = cur_outSlowD; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_STOCH_Close( TA_STOCH_Stream *stream ) { if( !stream ) return TA_SUCCESS; TA_MA_Close( stream->sub0 ); TA_MA_Close( stream->sub1 ); TA_STOCH_ReleaseImpl( stream ); return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_STOCH_Value( const TA_STOCH_Stream *stream, double *outSlowK, double *outSlowD ) { if( !stream || !outSlowK || !outSlowD ) return TA_BAD_PARAM; *outSlowK = stream->cur_outSlowK; *outSlowD = stream->cur_outSlowD; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_STOCH_OutRange( const TA_STOCH_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_STOCH_Advance( TA_STOCH_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_STOCH_Clone( const TA_STOCH_Stream *stream, TA_STOCH_Stream **clone ) { struct TA_STOCH_Stream *sp; if( !clone ) return TA_BAD_PARAM; *clone = NULL; if( !stream ) return TA_BAD_PARAM; sp = (struct TA_STOCH_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) return TA_ALLOC_ERR; *sp = *stream; sp->x_inHigh = NULL; sp->x_inLow = NULL; sp->x_inClose = NULL; sp->sub0 = NULL; sp->sub1 = NULL; if( stream->x_inHigh ) { size_t copyN = (size_t)(sp->xPhys); sp->x_inHigh = (double *)TA_Malloc( sizeof(double) * copyN ); if( !sp->x_inHigh ) { TA_STOCH_Close( sp ); return TA_ALLOC_ERR; } memcpy( sp->x_inHigh, stream->x_inHigh, sizeof(double) * copyN ); } if( stream->x_inLow ) { size_t copyN = (size_t)(sp->xPhys); sp->x_inLow = (double *)TA_Malloc( sizeof(double) * copyN ); if( !sp->x_inLow ) { TA_STOCH_Close( sp ); return TA_ALLOC_ERR; } memcpy( sp->x_inLow, stream->x_inLow, sizeof(double) * copyN ); } if( stream->x_inClose ) { size_t copyN = (size_t)(sp->xPhys); sp->x_inClose = (double *)TA_Malloc( sizeof(double) * copyN ); if( !sp->x_inClose ) { TA_STOCH_Close( sp ); return TA_ALLOC_ERR; } memcpy( sp->x_inClose, stream->x_inClose, sizeof(double) * copyN ); } if( stream->sub0 ) { TA_RetCode subRc = TA_MA_Clone( stream->sub0, &sp->sub0 ); if( subRc != TA_SUCCESS ) { TA_STOCH_Close( sp ); return subRc; } } if( stream->sub1 ) { TA_RetCode subRc = TA_MA_Clone( stream->sub1, &sp->sub1 ); if( subRc != TA_SUCCESS ) { TA_STOCH_Close( sp ); return subRc; } } *clone = sp; return TA_SUCCESS; }