def csi(df, V, M, C):
    '''Commodity Selection Index(CSI)
    CSI suggest that the best commodities are:
        - high in directional movement (DMI indicator value)
        - high in volatility (Volatility Index value and ATR)
        - have reasonable margin requirements (relative to directional movement & volatility)
        - have reasonable commission rates 
    Usage:
        Trade with high CSI values
    CSI = ADXR*ATR(14)(V/sqrt(M)*1/(150+C))*100
    where:
        ADXR:Average Directional Movement Index Rating
        ATR(14):14-days Average True Range
        V:value of a 1 $ move(or the basic increment of ATR(14) in dollar)
        M:margin requirement in dollars
        C:commition in dollars
    Note: the result of the term 1/(150+C) must be carried to four decimal places
    ADXR = (ADT(today)+ADX(14 days ago))/2
    '''
    res = df[['high', 'low', 'close']].copy()
    res['ATR'] = atr(res, period=14)[['ATR']]
    res['ATR14'] = atr(res.shift(14), period=14)[['ATR']]
    res['ADXR'] = (res.ATR + res.ATR14) / 2
    res['CSI'] = res.ADXR * res.ATR * (V / np.sqrt(M) * 1 / (150 + C)) * 100
    return res[['CSI', 'ATR', 'ATR14', 'ADXR']]
def chandlier_exit(df):
    '''Chandlier Exit
    Make profits in the direction of trend development, 
    calculate the stock price at the time of exit
    uptrend = Highest High(in the past 22 days)-3*ATR(for 22 days)
    downtrend = Lowest Low(in the past 22 days)+3*ATR(for 22 days)
    '''
    res = df[['high', 'low', 'close']].copy()
    res['uptrend'] = (
        res[['high']].rolling(22).max()).high - 3 * (atr(df, period=22).ATR)
    res['downtrend'] = (
        res[['low']].rolling(22).min()).low + 3 * (atr(df, period=22).ATR)
    return res
def keltner_channels(df,n,halflife=14):
    '''Kelter Channels(Keltner Bands)
    The theory is that a movement that starts at one price band is likely to carry to the other.
    Go long when prices turn up at or below the lower band. Close your position 
    if price turns down near the upper band or crosses to below the moving average.
    Go short when price turns down at or above the upper band. Close your position 
    if price turns up near the lower band or crosses to above the moving average.
    
    Upper Band = Exponential MA of Closing Price + multiple of Average True Range
    Lower Band = Exponential MA of Closing Price - multiple of Average True Range
    '''
    res = df[['close','high','low']].copy()
    res['upper_band'] = ema(res['close'],halflife)+atr(res).ATR*n
    res['lower_band'] = ema(res['close'],halflife)-atr(res).ATR*n
    return res
def atr_trailing_stop_signals(df,n=3):
    '''ATR Trailing Stop Signals
    Usage:
        Signals are used for exits:
        Exit your long position (sell) when price crosses below the ATR trailing stop line.
        Exit your short position (buy) when price crosses above the ATR trailing stop line.
        they can also be used to signal entries — in conjunction with a trend filter.
    steps:
        Calculate Average True Range ("ATR")
        Multiply ATR by your selected multiple — in our case 3 x ATR
        In an up-trend, subtract 3 x ATR from Closing Price and plot the 
        result as the stop for the following day.
        If price closes below the ATR stop, add 3 x ATR to Closing Price — to track a Short trade
        Otherwise, continue subtracting 3 x ATR for each subsequent day until 
        price reverses below the ATR stop.
        We have also built in a ratchet mechanism so that ATR stops cannot move 
        lower during a Long trade nor rise during a Short trade.
        The HighLow option is a little different: 3xATR is subtracted from the 
        daily High during an up-trend and added to the daily Low during a down-trend.
    '''
    res = df[['close','high','low']].copy()
    res['ATR'] = atr(res).ATR
    res['stop'] = res.close-n*res.ATR
    res['trade'] = np.nan
    res['highoption'] = np.nan
    res['lowoption'] = np.nan
    for i in range(len(res.close)):
        while res.close.iloc[i]>=res.stop.iloc[i]:
            res.close.iloc[i] = res.close.iloc[i]-n*res.ATR.iloc[i]
        res.trade.iloc[i] = res.close.iloc[i]+n*res.ATR.iloc[i]
        res.highoption.iloc[i] = res.high.iloc[i]-n*res.ATR.iloc[i]
        res.lowoption.iloc[i] = res.low.iloc[i]+n*res.ATR.iloc[i]
    return res
def choppiness(df,n):
    '''Choppiness Index
    Choppiness Index = 100 * Log10{Sum(TrueRange,n) / [Maximum(TrueHigh,n) - 
    Minimum(TrueLow,n)]} / Log10(n)
    '''
    res = df[['high','low','close']].copy()
    res['sumTR'] = atr(res)['TR'].rolling(n).sum()
    res['maxtruehigh'] = res['high'].rolling(n).max()
    res['mintruelow'] = res['low'].rolling(n).min()
    res['choppiness'] = 100* (res.sumTR/(res.maxtruehigh-res.mintruelow)).apply(math.log,10) \
                /math.log(n,10)
    return res
def asi(df, T=300):
    '''Accumulative Swing Indicator(ASI)
    ASI = 50*(Cy-C+0.5*(Cy-Oy)+0.25*(C-O))/R*(K/T)
    parameters:
        C = Today's closing price
        Cy = Yesterday's closing price
        Hy = Yesterday's highest price
        K = The greatest of: Hy - C and Ly - C
        L = Today's lowest price
        Ly = Yesterday's lowest price
        O = Today's opening price
        Oy = Yesterday's opening price
        R = This varies based on relationship between today's closing price 
            and yesterday's high and low prices
        T = the maximum price changing during trade session
    Usage:
        ASI has positive value — uptrend.
        ASI has negative value — downtrend.
        ASI trend line breakout — validates a breakout on the price chart
    '''
    #SI(i) = (50*(close[i-1]-close[i]+0.5*(close[i-1]-open[i-1])
    #       +0.25*(close[i]-open[i]))/R)*(K/T)
    #ASI(i) = SI(i-1)+SI(i)
    res = df[['close', 'open', 'high', 'low']].copy()
    res['temp1'] = -1 * res.close.diff()
    res['temp2'] = (res.close - res.open).shift()
    res['temp3'] = res.close - res.open
    #K=max((close[i-1]-close[i]),(low[i-1]-close[i]))
    res['K'] = np.nan
    for i in range(len(res) - 1):
        if res.close.iloc[i] >= res.low[i]:
            res.K.iloc[i + 1] = res.close.iloc[i] - res.close.iloc[i + 1]
        else:
            res.K.iloc[i + 1] = res.low.iloc[i] - res.close.iloc[i + 1]
    '''
     R = TR - 0.5*ER + 0.25*SH
     TR = atr(df,period=14)
     if(Close[i+1] >= Low[i] && Close[i+1] <= High[i]) 
           ER = 0; 
       else 
         {
           if(Close[i+1] > High[i]) 
               ER = MathAbs(High[i] - Close[i+1]);
           if(Close[i+1] < Low[i]) 
               ER = MathAbs(Low[i] - Close[i+1]);
         }

      SH = MathAbs(Close[i+1] - Open[i+1]);
      
     '''

    res['TR'] = atr(df)[['ATR']]
    res['ER'] = np.nan
    for i in range(len(res) - 1):
        if res.close.iloc[i+1]>=res.low.iloc[i] and \
            res.close.iloc[i+1]<=res.high.iloc[i]:
            res.ER.iloc[i] = 0
        else:
            if res.close.iloc[i + 1] > res.high.iloc[i]:
                res.ER.iloc[i] = abs(res.high.iloc[i] - res.close.iloc[i + 1])
            elif res.close.iloc[i + 1] < res.low.iloc[i]:
                res.ER.iloc[i] = abs(res.low.iloc[i] - res.close.iloc[i + 1])
    res['SH'] = abs(res.close.shift(-1) - res.open.shift(-1))
    res['R'] = res.TR - 0.5 * res.ER + 0.25 * res.SH
    res['SI'] = (50 * (res.temp1 + 0.5 * res.temp2 + 0.25 * res.temp3) /
                 res.R) * (res.K / T)
    res['ASI'] = np.nan
    for i in range(len(res) - 1):
        res.ASI.iloc[i + 1] = res.SI.iloc[i + 1] + res.SI.iloc[i]

    return res.ASI