/* 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 * CF Christo Fogelberg * * Change history: * * MMDDYY BY Description * ------------------------------------------------------------------- * 010802 MF Template creation. * 052603 MF Adapt code to compile with .NET Managed C++ * 122104 MF,CF Fix#1089506 for out-of-bound access to ep_temp. * 082726 MF,CC Answer a rejected minus_dm before reading ep_temp, not after: * the read was of an uninitialised local. */ TA_LIB_API int TA_SAR_Lookback( double optInAcceleration, double optInMaximum ) { if( optInAcceleration == TA_REAL_DEFAULT ) optInAcceleration = 0.02; else if( !(optInAcceleration >= 0e0 && optInAcceleration <= TA_REAL_MAX) ) return -1; if( optInMaximum == TA_REAL_DEFAULT ) optInMaximum = 0.2; else if( !(optInMaximum >= 0e0 && optInMaximum <= TA_REAL_MAX) ) return -1; /* SAR always sacrify one price bar to establish the * initial extreme price. */ return 1; } TA_LIB_API int TA_SAR_DisplayShift( double optInAcceleration, double optInMaximum, int outputIdx ) { if( TA_SAR_Lookback( optInAcceleration, optInMaximum ) < 0 ) return INT_MIN; if( outputIdx < 0 || outputIdx >= 1 ) return INT_MIN; return 0; } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_SAR( int startIdx, int endIdx, const double inHigh[], const double inLow[], double optInAcceleration, double optInMaximum, int *outBegIdx, int *outNBElement, double outReal[] ) { TA_RetCode retCode; int isLong; int todayIdx; int outIdx; int tempInt; double newHigh; double newLow; double prevHigh; double prevLow; double af; double ep; double sar; double ep_temp[1]; 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( optInAcceleration == TA_REAL_DEFAULT ) optInAcceleration = 0.02; else if( !(optInAcceleration >= 0e0 && optInAcceleration <= TA_REAL_MAX) ) return TA_BAD_PARAM; if( optInMaximum == TA_REAL_DEFAULT ) optInMaximum = 0.2; else if( !(optInMaximum >= 0e0 && optInMaximum <= TA_REAL_MAX) ) return TA_BAD_PARAM; if( !inHigh ) return TA_BAD_PARAM; if( !inLow ) return TA_BAD_PARAM; if( !outBegIdx || !outNBElement ) return TA_BAD_PARAM; if( !outReal ) return TA_BAD_PARAM; /* > 0 indicates long. == 0 indicates short */ /* Implementation of the SAR has been a little bit open to interpretation * since Wilder (the original author) did not define a precise algorithm * on how to bootstrap the algorithm. Take any existing software application * and you will see slight variation on how the algorithm was adapted. * * What is the initial trade direction? Long or short? * =================================================== * The interpretation of what should be the initial SAR values is * open to interpretation, particularly since the caller to the function * does not specify the initial direction of the trade. * * In TA-Lib, the following logic is used: * - Calculate +DM and -DM between the first and * second bar. The highest directional indication will * indicate the assumed direction of the trade for the second * price bar. * - In the case of a tie between +DM and -DM, * the direction is LONG by default. * * What is the initial "extreme point" and thus SAR? * ================================================= * The following shows how different people took different approach: * - Metastock use the first price bar high/low depending of * the direction. No SAR is calculated for the first price * bar. * - Tradestation use the closing price of the second bar. No * SAR are calculated for the first price bar. * - Wilder (the original author) use the SIP from the * previous trade (cannot be implement here since the * direction and length of the previous trade is unknonw). * - The Magazine TASC seems to follow Wilder approach which * is not practical here. * * TA-Lib "consume" the first price bar and use its high/low as the * initial SAR of the second price bar. I found that approach to be * the closest to Wilders idea of having the first entry day use * the previous extreme point, except that here the extreme point is * derived solely from the first price bar. I found the same approach * to be used by Metastock. */ /* Identify the minimum number of price bar needed * to calculate at least one output. * * Move up the start index if there is not * enough initial data. */ if( startIdx < 1 ) { startIdx = 1; } /* Make sure there is still something to evaluate. */ if( startIdx > endIdx ) { *outBegIdx= 0; *outNBElement= 0; return TA_SUCCESS; } /* Make sure the acceleration and maximum are coherent. * If not, correct the acceleration. */ af = optInAcceleration; if( af > optInMaximum ) { optInAcceleration = optInMaximum; af = optInAcceleration; } /* Identify if the initial direction is long or short. * (ep is just used as a temp buffer here, the name * of the parameter is not significant). */ retCode = TA_MINUS_DM(startIdx,startIdx,inHigh,inLow,1,&tempInt,&tempInt,ep_temp); if( retCode != TA_SUCCESS ) { *outBegIdx= 0; *outNBElement= 0; return retCode; } if( ep_temp[0] > 0 ) { isLong = 0; } else { isLong = 1; } *outBegIdx= startIdx; outIdx = 0; /* Write the first SAR. */ todayIdx = startIdx; newHigh = inHigh[todayIdx - 1]; newLow = inLow[todayIdx - 1]; if( isLong == 1 ) { ep = inHigh[todayIdx]; sar = newLow; } else { ep = inLow[todayIdx]; sar = newHigh; } /* Cheat on the newLow and newHigh for the * first iteration. */ newLow = inLow[todayIdx]; newHigh = inHigh[todayIdx]; while( todayIdx <= endIdx ) { prevLow = newLow; prevHigh = newHigh; newLow = inLow[todayIdx]; newHigh = inHigh[todayIdx]; todayIdx += 1; if( isLong == 1 ) { /* Switch to short if the low penetrates the SAR value. */ if( newLow <= sar ) { /* Switch and Overide the SAR with the ep */ isLong = 0; sar = ep; /* Make sure the overide SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } /* Output the overide SAR */ outReal[outIdx++] = sar; /* Adjust af and ep */ af = optInAcceleration; ep = newLow; /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } else { /* No switch */ /* Output the SAR (was calculated in the previous iteration) */ outReal[outIdx++] = sar; /* Adjust af and ep. */ if( newHigh > ep ) { ep = newHigh; af += optInAcceleration; if( af > optInMaximum ) { af = optInMaximum; } } /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } /* Switch to long if the high penetrates the SAR value. */ } else if( newHigh >= sar ) { /* Switch and Overide the SAR with the ep */ isLong = 1; sar = ep; /* Make sure the overide SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } /* Output the overide SAR */ outReal[outIdx++] = sar; /* Adjust af and ep */ af = optInAcceleration; ep = newHigh; /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } else { /* No switch */ /* Output the SAR (was calculated in the previous iteration) */ outReal[outIdx++] = sar; /* Adjust af and ep. */ if( newLow < ep ) { ep = newLow; af += optInAcceleration; if( af > optInMaximum ) { af = optInMaximum; } } /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } } *outNBElement= outIdx; return TA_SUCCESS; } TA_FMA_MULTIVERSION TA_RetCode TA_S_SAR( int startIdx, int endIdx, const float inHigh[], const float inLow[], double optInAcceleration, double optInMaximum, int *outBegIdx, int *outNBElement, double outReal[] ) { TA_RetCode retCode; int isLong; int todayIdx; int outIdx; int tempInt; double newHigh; double newLow; double prevHigh; double prevLow; double af; double ep; double sar; double ep_temp[1]; 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( optInAcceleration == TA_REAL_DEFAULT ) optInAcceleration = 0.02; else if( !(optInAcceleration >= 0e0 && optInAcceleration <= TA_REAL_MAX) ) return TA_BAD_PARAM; if( optInMaximum == TA_REAL_DEFAULT ) optInMaximum = 0.2; else if( !(optInMaximum >= 0e0 && optInMaximum <= TA_REAL_MAX) ) return TA_BAD_PARAM; if( !inHigh ) return TA_BAD_PARAM; if( !inLow ) return TA_BAD_PARAM; if( !outBegIdx || !outNBElement ) return TA_BAD_PARAM; if( !outReal ) return TA_BAD_PARAM; if( startIdx < 1 ) { startIdx = 1; } if( startIdx > endIdx ) { *outBegIdx= 0; *outNBElement= 0; return TA_SUCCESS; } af = optInAcceleration; if( af > optInMaximum ) { optInAcceleration = optInMaximum; af = optInAcceleration; } retCode = TA_S_MINUS_DM(startIdx,startIdx,inHigh,inLow,1,&tempInt,&tempInt,ep_temp); if( retCode != TA_SUCCESS ) { *outBegIdx= 0; *outNBElement= 0; return retCode; } if( ep_temp[0] > 0 ) { isLong = 0; } else { isLong = 1; } *outBegIdx= startIdx; outIdx = 0; todayIdx = startIdx; newHigh = (double)inHigh[todayIdx - 1]; newLow = (double)inLow[todayIdx - 1]; if( isLong == 1 ) { ep = (double)inHigh[todayIdx]; sar = newLow; } else { ep = (double)inLow[todayIdx]; sar = newHigh; } newLow = (double)inLow[todayIdx]; newHigh = (double)inHigh[todayIdx]; while( todayIdx <= endIdx ) { prevLow = newLow; prevHigh = newHigh; newLow = (double)inLow[todayIdx]; newHigh = (double)inHigh[todayIdx]; todayIdx += 1; if( isLong == 1 ) { if( newLow <= sar ) { isLong = 0; sar = ep; if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } outReal[outIdx++] = sar; af = optInAcceleration; ep = newLow; sar = fma(af, ep - sar, sar); if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } else { outReal[outIdx++] = sar; if( newHigh > ep ) { ep = newHigh; af += optInAcceleration; if( af > optInMaximum ) { af = optInMaximum; } } sar = fma(af, ep - sar, sar); if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } } else if( newHigh >= sar ) { isLong = 1; sar = ep; if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } outReal[outIdx++] = sar; af = optInAcceleration; ep = newHigh; sar = fma(af, ep - sar, sar); if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } else { outReal[outIdx++] = sar; if( newLow < ep ) { ep = newLow; af += optInAcceleration; if( af > optInMaximum ) { af = optInMaximum; } } sar = fma(af, ep - sar, sar); if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } } *outNBElement= outIdx; return TA_SUCCESS; } /**** Streaming API *****/ struct TA_SAR_Stream { /* The bars this handle has an output for (see TA_SAR_OutRange). */ int outRangeBegIdx; int outRangeCount; /* The value(s) at the last bar the stream counted (see TA_SAR_Value). */ double cur_outReal; double pad_0; double optInAcceleration; double optInMaximum; int isLong; double newHigh; double newLow; double af; double ep; double sar; }; /* Private function, not in public API. */ static TA_FMA_STEP_INLINE void TA_SAR_StepImpl( struct TA_SAR_Stream *sp, double inHigh, double inLow, double *outReal ) { double prevHigh; double prevLow; double newHigh; double newLow; double sar; newHigh = sp->newHigh; newLow = sp->newLow; sar = sp->sar; prevLow = newLow; prevHigh = newHigh; newLow = inLow; newHigh = inHigh; if( sp->isLong == 1 ) { /* Switch to short if the low penetrates the SAR value. */ if( newLow <= sar ) { /* Switch and Overide the SAR with the ep */ sp->isLong = 0; sar = sp->ep; /* Make sure the overide SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } /* Output the overide SAR */ *outReal= sar; /* Adjust af and ep */ sp->af = sp->optInAcceleration; sp->ep = newLow; /* Calculate the new SAR */ sar = fma(sp->af, sp->ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } else { /* No switch */ /* Output the SAR (was calculated in the previous iteration) */ *outReal= sar; /* Adjust af and ep. */ if( newHigh > sp->ep ) { sp->ep = newHigh; sp->af += sp->optInAcceleration; if( sp->af > sp->optInMaximum ) { sp->af = sp->optInMaximum; } } /* Calculate the new SAR */ sar = fma(sp->af, sp->ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } /* Switch to long if the high penetrates the SAR value. */ } else if( newHigh >= sar ) { /* Switch and Overide the SAR with the ep */ sp->isLong = 1; sar = sp->ep; /* Make sure the overide SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } /* Output the overide SAR */ *outReal= sar; /* Adjust af and ep */ sp->af = sp->optInAcceleration; sp->ep = newHigh; /* Calculate the new SAR */ sar = fma(sp->af, sp->ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } else { /* No switch */ /* Output the SAR (was calculated in the previous iteration) */ *outReal= sar; /* Adjust af and ep. */ if( newLow < sp->ep ) { sp->ep = newLow; sp->af += sp->optInAcceleration; if( sp->af > sp->optInMaximum ) { sp->af = sp->optInMaximum; } } /* Calculate the new SAR */ sar = fma(sp->af, sp->ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } sp->cur_outReal = *outReal; sp->newHigh = newHigh; sp->newLow = newLow; sp->sar = sar; } static TA_RetCode TA_SAR_OpenImpl( struct TA_SAR_Stream **stream, const double inHigh[], const double inLow[], int startIdx, int historyLen, double optInAcceleration, double optInMaximum, int *outBegIdx, int *outNBElement, double outReal[], int outStride ) { struct TA_SAR_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( !inHigh || !inLow || !outReal ) return TA_BAD_PARAM; if( optInAcceleration == TA_REAL_DEFAULT ) optInAcceleration = 0.02; else if( !(optInAcceleration >= 0e0 && optInAcceleration <= TA_REAL_MAX) ) return TA_BAD_PARAM; if( optInMaximum == TA_REAL_DEFAULT ) optInMaximum = 0.2; else if( !(optInMaximum >= 0e0 && optInMaximum <= TA_REAL_MAX) ) return TA_BAD_PARAM; if( startIdx > historyLen - 1 ) { *outBegIdx = 0; *outNBElement = 0; return TA_INSUFFICIENT_HISTORY; } endIdx = historyLen - 1; { TA_RetCode retCode; int isLong = 0; /* > 0 indicates long. == 0 indicates short */ int todayIdx; int outIdx; int tempInt; double newHigh = 0.0; double newLow = 0.0; double prevHigh; double prevLow; double af = 0.0; double ep = 0.0; double sar = 0.0; double ep_temp[1]; /* Implementation of the SAR has been a little bit open to interpretation * since Wilder (the original author) did not define a precise algorithm * on how to bootstrap the algorithm. Take any existing software application * and you will see slight variation on how the algorithm was adapted. * * What is the initial trade direction? Long or short? * =================================================== * The interpretation of what should be the initial SAR values is * open to interpretation, particularly since the caller to the function * does not specify the initial direction of the trade. * * In TA-Lib, the following logic is used: * - Calculate +DM and -DM between the first and * second bar. The highest directional indication will * indicate the assumed direction of the trade for the second * price bar. * - In the case of a tie between +DM and -DM, * the direction is LONG by default. * * What is the initial "extreme point" and thus SAR? * ================================================= * The following shows how different people took different approach: * - Metastock use the first price bar high/low depending of * the direction. No SAR is calculated for the first price * bar. * - Tradestation use the closing price of the second bar. No * SAR are calculated for the first price bar. * - Wilder (the original author) use the SIP from the * previous trade (cannot be implement here since the * direction and length of the previous trade is unknonw). * - The Magazine TASC seems to follow Wilder approach which * is not practical here. * * TA-Lib "consume" the first price bar and use its high/low as the * initial SAR of the second price bar. I found that approach to be * the closest to Wilders idea of having the first entry day use * the previous extreme point, except that here the extreme point is * derived solely from the first price bar. I found the same approach * to be used by Metastock. */ /* Identify the minimum number of price bar needed * to calculate at least one output. * * Move up the start index if there is not * enough initial data. */ if( startIdx < 1 ) { startIdx = 1; } /* Make sure there is still something to evaluate. */ if( startIdx > endIdx ) { *outBegIdx= 0; *outNBElement= 0; return TA_INSUFFICIENT_HISTORY; } /* Make sure the acceleration and maximum are coherent. * If not, correct the acceleration. */ af = optInAcceleration; if( af > optInMaximum ) { optInAcceleration = optInMaximum; af = optInAcceleration; } /* Identify if the initial direction is long or short. * (ep is just used as a temp buffer here, the name * of the parameter is not significant). */ retCode = TA_MINUS_DM(startIdx,startIdx,inHigh,inLow,1,&tempInt,&tempInt,ep_temp); if( retCode != TA_SUCCESS ) { *outBegIdx= 0; *outNBElement= 0; return retCode; } if( ep_temp[0] > 0 ) { isLong = 0; } else { isLong = 1; } *outBegIdx= startIdx; outIdx = 0; /* Write the first SAR. */ todayIdx = startIdx; newHigh = inHigh[todayIdx - 1]; newLow = inLow[todayIdx - 1]; if( isLong == 1 ) { ep = inHigh[todayIdx]; sar = newLow; } else { ep = inLow[todayIdx]; sar = newHigh; } /* Cheat on the newLow and newHigh for the * first iteration. */ newLow = inLow[todayIdx]; newHigh = inHigh[todayIdx]; while( todayIdx <= endIdx ) { prevLow = newLow; prevHigh = newHigh; newLow = inLow[todayIdx]; newHigh = inHigh[todayIdx]; todayIdx += 1; if( isLong == 1 ) { /* Switch to short if the low penetrates the SAR value. */ if( newLow <= sar ) { /* Switch and Overide the SAR with the ep */ isLong = 0; sar = ep; /* Make sure the overide SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } /* Output the overide SAR */ outReal[outIdx++ * outStride] = sar; /* Adjust af and ep */ af = optInAcceleration; ep = newLow; /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } else { /* No switch */ /* Output the SAR (was calculated in the previous iteration) */ outReal[outIdx++ * outStride] = sar; /* Adjust af and ep. */ if( newHigh > ep ) { ep = newHigh; af += optInAcceleration; if( af > optInMaximum ) { af = optInMaximum; } } /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } /* Switch to long if the high penetrates the SAR value. */ } else if( newHigh >= sar ) { /* Switch and Overide the SAR with the ep */ isLong = 1; sar = ep; /* Make sure the overide SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } /* Output the overide SAR */ outReal[outIdx++ * outStride] = sar; /* Adjust af and ep */ af = optInAcceleration; ep = newHigh; /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } else { /* No switch */ /* Output the SAR (was calculated in the previous iteration) */ outReal[outIdx++ * outStride] = sar; /* Adjust af and ep. */ if( newLow < ep ) { ep = newLow; af += optInAcceleration; if( af > optInMaximum ) { af = optInMaximum; } } /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } } *outNBElement= outIdx; /* Capture the live batch state into the handle. */ sp = (struct TA_SAR_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) { return TA_ALLOC_ERR; } memset( sp, 0, sizeof(*sp) ); sp->optInAcceleration = optInAcceleration; sp->optInMaximum = optInMaximum; sp->isLong = isLong; sp->newHigh = newHigh; sp->newLow = newLow; sp->af = af; sp->ep = ep; sp->sar = sar; sp->outRangeBegIdx = *outBegIdx; sp->outRangeCount = *outNBElement; sp->cur_outReal = outReal[(*outNBElement - 1) * outStride]; *stream = sp; return TA_SUCCESS; } } /* Private function, not in public API. */ TA_RetCode TA_SAR_OpenInternal( struct TA_SAR_Stream **stream, const double inHigh[], const double inLow[], int startIdx, int historyLen, double optInAcceleration, double optInMaximum, double *outReal ) { TA_RetCode retCode; int dummyBegIdx = 0; int dummyNBElement = 0; double sink_outReal = 0.0; retCode = TA_SAR_OpenImpl( stream, inHigh, inLow, startIdx, historyLen, optInAcceleration, optInMaximum, &dummyBegIdx, &dummyNBElement, &sink_outReal, 0 ); if( retCode == TA_SUCCESS ) { *outReal = sink_outReal; } return retCode; } TA_LIB_API TA_RetCode TA_SAR_Open( TA_SAR_Stream **stream, const double inHigh[], const double inLow[], int historyLen, double optInAcceleration, double optInMaximum, 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( !inHigh || !inLow || !outReal ) return TA_BAD_PARAM; return TA_SAR_OpenInternal( stream, inHigh, inLow, 0, historyLen, optInAcceleration, optInMaximum, outReal ); } TA_LIB_API TA_RetCode TA_SAR_OpenAndFill( TA_SAR_Stream **stream, const double inHigh[], const double inLow[], int historyLen, double optInAcceleration, double optInMaximum, 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( !inHigh || !inLow || !outBegIdx || !outNBElement || !outReal ) return TA_BAD_PARAM; if( (const void *)outReal == (const void *)inHigh || (const void *)outReal == (const void *)inLow ) return TA_BAD_PARAM; return TA_SAR_OpenAndFillInternal( stream, inHigh, inLow, 0, historyLen, optInAcceleration, optInMaximum, outBegIdx, outNBElement, outReal ); } /* Private function, not in public API. */ TA_RetCode TA_SAR_OpenAndFillInternal( struct TA_SAR_Stream **stream, const double inHigh[], const double inLow[], int startIdx, int historyLen, double optInAcceleration, double optInMaximum, int *outBegIdx, int *outNBElement, double outReal[] ) { return TA_SAR_OpenImpl( stream, inHigh, inLow, startIdx, historyLen, optInAcceleration, optInMaximum, outBegIdx, outNBElement, outReal, 1 ); } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_SAR_Update( TA_SAR_Stream *stream, double inHigh, double inLow, 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( inHigh ) || !TA_IS_FINITE( inLow ) ) return TA_BAD_PARAM; TA_SAR_StepImpl( stream, inHigh, inLow, outReal ); stream->outRangeCount++; return TA_SUCCESS; } TA_FMA_MULTIVERSION TA_LIB_API TA_RetCode TA_SAR_Peek( const TA_SAR_Stream *stream, double inHigh, double inLow, double *outReal ) { const struct TA_SAR_Stream *sp = stream; double prevHigh; double prevLow; double af; double ep; int isLong; double newHigh; double newLow; double sar; if( !stream || !outReal ) return TA_BAD_PARAM; if( !TA_IS_FINITE( inHigh ) || !TA_IS_FINITE( inLow ) ) return TA_BAD_PARAM; af = sp->af; ep = sp->ep; isLong = sp->isLong; newHigh = sp->newHigh; newLow = sp->newLow; sar = sp->sar; prevLow = newLow; prevHigh = newHigh; newLow = inLow; newHigh = inHigh; if( isLong == 1 ) { /* Switch to short if the low penetrates the SAR value. */ if( newLow <= sar ) { /* Switch and Overide the SAR with the ep */ isLong = 0; sar = ep; /* Make sure the overide SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } /* Output the overide SAR */ *outReal= sar; /* Adjust af and ep */ af = sp->optInAcceleration; ep = newLow; /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } else { /* No switch */ /* Output the SAR (was calculated in the previous iteration) */ *outReal= sar; /* Adjust af and ep. */ if( newHigh > ep ) { ep = newHigh; af += sp->optInAcceleration; if( af > sp->optInMaximum ) { af = sp->optInMaximum; } } /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } /* Switch to long if the high penetrates the SAR value. */ } else if( newHigh >= sar ) { /* Switch and Overide the SAR with the ep */ isLong = 1; sar = ep; /* Make sure the overide SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } /* Output the overide SAR */ *outReal= sar; /* Adjust af and ep */ af = sp->optInAcceleration; ep = newHigh; /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar > prevLow ) { sar = prevLow; } if( sar > newLow ) { sar = newLow; } } else { /* No switch */ /* Output the SAR (was calculated in the previous iteration) */ *outReal= sar; /* Adjust af and ep. */ if( newLow < ep ) { ep = newLow; af += sp->optInAcceleration; if( af > sp->optInMaximum ) { af = sp->optInMaximum; } } /* Calculate the new SAR */ sar = fma(af, ep - sar, sar); /* Make sure the new SAR is within * yesterday's and today's range. */ if( sar < prevHigh ) { sar = prevHigh; } if( sar < newHigh ) { sar = newHigh; } } return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_SAR_Close( TA_SAR_Stream *stream ) { if( stream ) TA_Free( stream ); return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_SAR_Value( const TA_SAR_Stream *stream, double *outReal ) { if( !stream || !outReal ) return TA_BAD_PARAM; *outReal = stream->cur_outReal; return TA_SUCCESS; } TA_LIB_API TA_RetCode TA_SAR_OutRange( const TA_SAR_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_SAR_Advance( TA_SAR_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_SAR_Clone( const TA_SAR_Stream *stream, TA_SAR_Stream **clone ) { struct TA_SAR_Stream *sp; if( !clone ) return TA_BAD_PARAM; *clone = NULL; if( !stream ) return TA_BAD_PARAM; sp = (struct TA_SAR_Stream *)TA_Malloc( sizeof(*sp) ); if( !sp ) return TA_ALLOC_ERR; *sp = *stream; *clone = sp; return TA_SUCCESS; }