/* 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 * * * Change history: * * MMDDYY BY Description * ------------------------------------------------------------------- * 112400 MF Template creation. * 052603 MF Adapt code to compile with .NET Managed C++ * 080926 MF,CC Explicit no-smoothing copy at a period of 1. * 081026 MF,CC Fold the internal variant into EMA (issue #183). */ TA_NOINLINE TA_LIB_API int TA_EMA_Lookback( int optInTimePeriod ) { if( (int)optInTimePeriod == TA_INTEGER_DEFAULT ) optInTimePeriod = 30; else if( (int)optInTimePeriod < 1 || (int)optInTimePeriod > 100000 ) return -1; return optInTimePeriod - 1 + TA_GLOBALS_UNSTABLE(TA_FUNC_UNST_EMA,Ema,((optInTimePeriod > 1) ? (10 * optInTimePeriod + 1) / 2 : 0),((optInTimePeriod > 1) ? (19 * optInTimePeriod + 1) / 2 : 0)); } TA_LIB_API int TA_EMA_DisplayShift( int optInTimePeriod, int outputIdx ) { if( TA_EMA_Lookback( optInTimePeriod ) < 0 ) return INT_MIN; if( outputIdx < 0 || outputIdx >= 1 ) return INT_MIN; return 0; } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_EMA( int startIdx, int endIdx, const double inReal[], int optInTimePeriod, int *outBegIdx, int *outNBElement, double outReal[] ) { double emaBeta; double optInK_1; double tempReal; double prevMA; int i; int today; int outIdx; int lookbackTotal; 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)optInTimePeriod == TA_INTEGER_DEFAULT ) optInTimePeriod = 30; else if( (int)optInTimePeriod < 1 || (int)optInTimePeriod > 100000 ) return TA_BAD_PARAM; if( !inReal ) return TA_BAD_PARAM; if( !outBegIdx || !outNBElement ) return TA_BAD_PARAM; if( !outReal ) return TA_BAD_PARAM; /* Identify the minimum number of price bar needed * to calculate at least one output. */ lookbackTotal = TA_EMA_Lookback(optInTimePeriod); /* After the lookback call: a double live across a call is saved and * restored around every fma call of the loops below, one more instruction * per bar. * * emaBeta + optInK_1 must be exactly 1.0, or a flat input drifts off * its level. Each subtraction is exact only from an operand in * [0.5,1): at a period of 2 that is optInK_1, above it emaBeta. * * Above it emaBeta stays as the divide wrote it: the second subtraction * would not change a bit, and a register last written by a subtraction * costs each FMA reading it one more cycle on Intel P-cores. */ emaBeta = (double)(optInTimePeriod - 1) / (double)(optInTimePeriod + 1); optInK_1 = 1.0 - emaBeta; if( emaBeta < 0.5 ) { emaBeta = 1.0 - optInK_1; } /* 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; } /* No smoothing at period of 1: the output is a copy of the input * (same convention as TA_MA for every MAType). Explicit because the * recursion below, at a k of 1.0 and a beta of 0.0, does not keep the * sign of a -0.0 input. The unstable period still delays the first * output. */ if( optInTimePeriod == 1 ) { *outBegIdx= startIdx; outIdx = 0; today = startIdx; while( today <= endIdx ) { outReal[outIdx++] = inReal[today++]; } *outNBElement= outIdx; return TA_SUCCESS; } *outBegIdx= startIdx; /* Do the EMA calculation using tight loops. */ today = startIdx - lookbackTotal; i = optInTimePeriod; tempReal = 0.0; while( i-- > 0 ) { tempReal += inReal[today++]; } prevMA = tempReal / optInTimePeriod; while( today <= startIdx ) { prevMA = fma(emaBeta, prevMA, optInK_1 * inReal[today++]); } outReal[0] = prevMA; outIdx = 1; while( today <= endIdx ) { prevMA = fma(emaBeta, prevMA, optInK_1 * inReal[today++]); outReal[outIdx++] = prevMA; } *outNBElement= outIdx; return TA_SUCCESS; } TA_FMA_MULTIVERSION TA_RetCode TA_S_EMA( int startIdx, int endIdx, const float inReal[], int optInTimePeriod, int *outBegIdx, int *outNBElement, double outReal[] ) { double emaBeta; double optInK_1; double tempReal; double prevMA; int i; int today; int outIdx; int lookbackTotal; 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)optInTimePeriod == TA_INTEGER_DEFAULT ) optInTimePeriod = 30; else if( (int)optInTimePeriod < 1 || (int)optInTimePeriod > 100000 ) return TA_BAD_PARAM; if( !inReal ) return TA_BAD_PARAM; if( !outBegIdx || !outNBElement ) return TA_BAD_PARAM; if( !outReal ) return TA_BAD_PARAM; lookbackTotal = TA_EMA_Lookback(optInTimePeriod); emaBeta = (double)(optInTimePeriod - 1) / (double)(optInTimePeriod + 1); optInK_1 = 1.0 - emaBeta; if( emaBeta < 0.5 ) { emaBeta = 1.0 - optInK_1; } if( startIdx < lookbackTotal ) { startIdx = lookbackTotal; } if( startIdx > endIdx ) { *outBegIdx= 0; *outNBElement= 0; return TA_SUCCESS; } if( optInTimePeriod == 1 ) { *outBegIdx= startIdx; outIdx = 0; today = startIdx; while( today <= endIdx ) { outReal[outIdx++] = (double)inReal[today++]; } *outNBElement= outIdx; return TA_SUCCESS; } *outBegIdx= startIdx; today = startIdx - lookbackTotal; i = optInTimePeriod; tempReal = 0.0; while( i-- > 0 ) { tempReal += (double)inReal[today++]; } prevMA = tempReal / optInTimePeriod; while( today <= startIdx ) { prevMA = fma(emaBeta, prevMA, optInK_1 * (double)inReal[today++]); } outReal[0] = prevMA; outIdx = 1; while( today <= endIdx ) { prevMA = fma(emaBeta, prevMA, optInK_1 * (double)inReal[today++]); outReal[outIdx++] = prevMA; } *outNBElement= outIdx; return TA_SUCCESS; } /**** Streaming API *****/ struct TA_EMA_Stream { /* The bars this handle has an output for (see TA_EMA_OutRange). */ int outRangeBegIdx; int outRangeCount; /* The value(s) at the last bar the stream counted (see TA_EMA_Value). */ double cur_outReal; int optInTimePeriod; double emaBeta; double optInK_1; double pad_0; double prevMA; }; /* Private function, not in public API. */ static TA_FMA_STEP_INLINE void TA_EMA_StepImpl( struct TA_EMA_Stream *sp, double inReal, double *outReal ) { if( sp->optInTimePeriod == 1 ) { *outReal= inReal; sp->cur_outReal = *outReal; return; } sp->prevMA = fma(sp->emaBeta, sp->prevMA, sp->optInK_1 * inReal); *outReal= sp->prevMA; sp->cur_outReal = *outReal; } static TA_FMA_STEP_INLINE TA_RetCode TA_EMA_OpenImpl( struct TA_EMA_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, int *outBegIdx, int *outNBElement, double outReal[], int outStride ) { struct TA_EMA_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( !inReal || !outReal ) return TA_BAD_PARAM; if( (int)optInTimePeriod == TA_INTEGER_DEFAULT ) optInTimePeriod = 30; else if( (int)optInTimePeriod < 1 || (int)optInTimePeriod > 100000 ) return TA_BAD_PARAM; if( startIdx > historyLen - 1 ) { *outBegIdx = 0; *outNBElement = 0; return TA_INSUFFICIENT_HISTORY; } endIdx = historyLen - 1; if( optInTimePeriod == 1 ) { int fillLb = TA_EMA_Lookback( optInTimePeriod ); if( startIdx > fillLb ) fillLb = startIdx; if( historyLen < fillLb + 1 ) { *outBegIdx = 0; *outNBElement = 0; return TA_INSUFFICIENT_HISTORY; } sp = (struct TA_EMA_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) { return TA_ALLOC_ERR; } memset( sp, 0, sizeof(*sp) ); sp->optInTimePeriod = optInTimePeriod; { int fillIdx; *outBegIdx = fillLb; *outNBElement = historyLen - fillLb; if( outStride ) { for( fillIdx = 0; fillIdx < historyLen - fillLb; fillIdx++ ) { outReal[fillIdx] = inReal[fillLb + fillIdx]; } } else { outReal[0] = inReal[historyLen - 1]; } } sp->outRangeBegIdx = *outBegIdx; sp->outRangeCount = *outNBElement; sp->cur_outReal = outReal[(*outNBElement - 1) * outStride]; *stream = sp; return TA_SUCCESS; } { double emaBeta = 0.0; double optInK_1 = 0.0; double tempReal; double prevMA = 0.0; int i; int today; int outIdx; int lookbackTotal; /* Identify the minimum number of price bar needed * to calculate at least one output. */ lookbackTotal = TA_EMA_Lookback(optInTimePeriod); /* After the lookback call: a double live across a call is saved and * restored around every fma call of the loops below, one more instruction * per bar. * * emaBeta + optInK_1 must be exactly 1.0, or a flat input drifts off * its level. Each subtraction is exact only from an operand in * [0.5,1): at a period of 2 that is optInK_1, above it emaBeta. * * Above it emaBeta stays as the divide wrote it: the second subtraction * would not change a bit, and a register last written by a subtraction * costs each FMA reading it one more cycle on Intel P-cores. */ emaBeta = (double)(optInTimePeriod - 1) / (double)(optInTimePeriod + 1); optInK_1 = 1.0 - emaBeta; if( emaBeta < 0.5 ) { emaBeta = 1.0 - optInK_1; } /* 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; } *outBegIdx= startIdx; /* Do the EMA calculation using tight loops. */ today = startIdx - lookbackTotal; i = optInTimePeriod; tempReal = 0.0; while( i-- > 0 ) { tempReal += inReal[today++]; } prevMA = tempReal / optInTimePeriod; while( today <= startIdx ) { prevMA = fma(emaBeta, prevMA, optInK_1 * inReal[today++]); } outReal[0 * outStride] = prevMA; outIdx = 1; while( today <= endIdx ) { prevMA = fma(emaBeta, prevMA, optInK_1 * inReal[today++]); outReal[outIdx++ * outStride] = prevMA; } *outNBElement= outIdx; /* Capture the live batch state into the handle. */ sp = (struct TA_EMA_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) { return TA_ALLOC_ERR; } memset( sp, 0, sizeof(*sp) ); sp->optInTimePeriod = optInTimePeriod; sp->emaBeta = emaBeta; sp->optInK_1 = optInK_1; sp->prevMA = prevMA; sp->outRangeBegIdx = *outBegIdx; sp->outRangeCount = *outNBElement; sp->cur_outReal = outReal[(*outNBElement - 1) * outStride]; *stream = sp; return TA_SUCCESS; } } TA_FMA_OPEN_CLONE static TA_RetCode TA_EMA_OpenImplFma( struct TA_EMA_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, int *outBegIdx, int *outNBElement, double outReal[], int outStride ) { return TA_EMA_OpenImpl( stream, inReal, startIdx, historyLen, optInTimePeriod, outBegIdx, outNBElement, outReal, outStride ); } TA_FMA_OPEN_PLAIN static TA_RetCode TA_EMA_OpenImplPlain( struct TA_EMA_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, int *outBegIdx, int *outNBElement, double outReal[], int outStride ) { return TA_EMA_OpenImpl( stream, inReal, startIdx, historyLen, optInTimePeriod, outBegIdx, outNBElement, outReal, outStride ); } TA_FMA_OPEN_CLONE static TA_RetCode TA_EMA_OpenSinkFma( struct TA_EMA_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, double *outReal ) { TA_RetCode retCode; int dummyBegIdx = 0; int dummyNBElement = 0; double sink_outReal = 0.0; retCode = TA_EMA_OpenImpl( stream, inReal, startIdx, historyLen, optInTimePeriod, &dummyBegIdx, &dummyNBElement, &sink_outReal, 0 ); if( retCode == TA_SUCCESS ) { *outReal = sink_outReal; } return retCode; } /* Private function, not in public API. */ TA_RetCode TA_EMA_OpenInternal( struct TA_EMA_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, double *outReal ) { TA_RetCode retCode; int dummyBegIdx = 0; int dummyNBElement = 0; double sink_outReal = 0.0; retCode = TA_FMA_AVAILABLE ? TA_EMA_OpenImplFma( stream, inReal, startIdx, historyLen, optInTimePeriod, &dummyBegIdx, &dummyNBElement, &sink_outReal, 0 ) : TA_EMA_OpenImplPlain( stream, inReal, startIdx, historyLen, optInTimePeriod, &dummyBegIdx, &dummyNBElement, &sink_outReal, 0 ); if( retCode == TA_SUCCESS ) { *outReal = sink_outReal; } return retCode; } TA_LIB_API TA_RetCode TA_EMA_Open( TA_EMA_Stream **stream, const double inReal[], int historyLen, int optInTimePeriod, double *outReal ) { 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( !inReal || !outReal ) return TA_BAD_PARAM; return TA_FMA_AVAILABLE ? TA_EMA_OpenSinkFma( stream, inReal, 0, historyLen, optInTimePeriod, outReal ) : TA_EMA_OpenInternal( stream, inReal, 0, historyLen, optInTimePeriod, outReal ); } TA_LIB_API TA_RetCode TA_EMA_OpenAndFill( TA_EMA_Stream **stream, const double inReal[], int historyLen, int optInTimePeriod, int *outBegIdx, int *outNBElement, double outReal[] ) { 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( !inReal || !outBegIdx || !outNBElement || !outReal ) return TA_BAD_PARAM; if( (const void *)outReal == (const void *)inReal ) return TA_BAD_PARAM; return TA_EMA_OpenAndFillInternal( stream, inReal, 0, historyLen, optInTimePeriod, outBegIdx, outNBElement, outReal ); } /* Private function, not in public API. */ TA_RetCode TA_EMA_OpenAndFillInternal( struct TA_EMA_Stream **stream, const double inReal[], int startIdx, int historyLen, int optInTimePeriod, int *outBegIdx, int *outNBElement, double outReal[] ) { return TA_FMA_AVAILABLE ? TA_EMA_OpenImplFma( stream, inReal, startIdx, historyLen, optInTimePeriod, outBegIdx, outNBElement, outReal, 1 ) : TA_EMA_OpenImplPlain( stream, inReal, startIdx, historyLen, optInTimePeriod, outBegIdx, outNBElement, outReal, 1 ); } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_EMA_Update( TA_EMA_Stream *stream, double inReal, double *outReal ) { if( !stream ) return TA_BAD_PARAM; if( stream->outRangeBegIdx + stream->outRangeCount > TA_INDEX_MAX ) return TA_OUT_OF_RANGE_END_INDEX; if( !outReal ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inReal ) ) return TA_BAD_PARAM; TA_EMA_StepImpl( stream, inReal, outReal ); stream->outRangeCount++; return TA_SUCCESS; } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_EMA_Peek( const TA_EMA_Stream *stream, double inReal, double *outReal ) { const struct TA_EMA_Stream *sp = stream; double prevMA; if( !stream || !outReal ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inReal ) ) return TA_BAD_PARAM; prevMA = sp->prevMA; if( sp->optInTimePeriod == 1 ) { *outReal= inReal; return TA_SUCCESS; } prevMA = fma(sp->emaBeta, prevMA, sp->optInK_1 * inReal); *outReal= prevMA; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_EMA_Close( TA_EMA_Stream *stream ) { if( stream ) TA_Free( stream ); return TA_SUCCESS; } /* Private function, not in public API. */ void TA_EMA_StepTape( struct TA_EMA_Stream *sp, const double tape[], int tapeBase, int tapeMask, double inReal, double *outReal ) { (void)tape; (void)tapeBase; (void)tapeMask; TA_EMA_StepImpl( sp, inReal, outReal ); sp->outRangeCount++; } /* Private function, not in public API. */ void TA_EMA_PeekTape( const struct TA_EMA_Stream *sp, const double tape[], int tapeBase, int tapeMask, double inReal, double *outReal ) { (void)tape; (void)tapeBase; (void)tapeMask; (void)TA_EMA_Peek( sp, inReal, outReal ); } /* Private function, not in public API. */ int TA_EMA_TapeDetach( struct TA_EMA_Stream *sp ) { (void)sp; return 0; } TA_LIB_API TA_RetCode TA_EMA_Value( const TA_EMA_Stream *stream, double *outReal ) { if( !stream || !outReal ) return TA_BAD_PARAM; *outReal = stream->cur_outReal; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_EMA_OutRange( const TA_EMA_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_EMA_Advance( TA_EMA_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_EMA_Clone( const TA_EMA_Stream *stream, TA_EMA_Stream **clone ) { struct TA_EMA_Stream *sp; if( !clone ) return TA_BAD_PARAM; *clone = NULL; if( !stream ) return TA_BAD_PARAM; sp = (struct TA_EMA_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) return TA_ALLOC_ERR; *sp = *stream; *clone = sp; return TA_SUCCESS; }