Re: Differing CCI Outputs bta-lib vs Backtrader.Indicators
Backtrader's CCI indicator has a bug, not computing the average absolute deviation in the right way. This results in different CCI values compared to CCI values in TradingView or compared tp the CCI values using TA-Lib (in backtrader).
The bug actually resides in the class MeanDeviation(Indicator), in the deviations.py file. MeanDeviation is used only by CCI.
attached is a cci.py that does not use MeanDeviation but implements the required logic directly. Another approach would be fixing MeanDeviation in deviations.py, but It would be less efficient.
Does anybody know how to feed the change into github and backtrader?
Thanks
Avy
#!/usr/bin/env python
# -*- coding: utf-8; py-indent-offset:4 -*-
###############################################################################
#
# Copyright (C) 2015-2020 Daniel Rodriguez
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# Implementation (of required average absolute deviation, or mean absolute deviation (MAD))
# corrected on 2021-01-19 By Avy Strominger
#
###############################################################################
from __future__ import (absolute_import, division, print_function,
unicode_literals)
import backtrader
from backtrader import Indicator, Max
from backtrader.indicators import MovAv
class CommodityChannelIndex(Indicator):
'''
Introduced by Donald Lambert in 1980 to measure variations of the
"typical price" (see below) from its mean to identify extremes and
reversals
Formula:
- tp = typical_price = (high + low + close) / 3
- tpmean = MovingAverage(tp, period)
- deviation = tp - tpmean
- meanabsdev = MeanAbsoluteDeviation(tp) (or average absolute deviation)
- cci = deviation / (meanabsdev * factor)
See:
- https://en.wikipedia.org/wiki/Commodity_channel_index
'''
alias = ('CCI',)
lines = ('cci',)
params = (('period', 20),
('factor', 0.015),
('movav', MovAv.Simple),
('upperband', 100.0),
('lowerband', -100.0),)
def _plotlabel(self):
plabels = [self.p.period, self.p.factor]
plabels += [self.p.movav] * self.p.notdefault('movav')
return plabels
def _plotinit(self):
self.plotinfo.plotyhlines = [0.0, self.p.upperband, self.p.lowerband]
def __init__(self):
self.tp = (self.data.high + self.data.low + self.data.close) / 3.0
self.tpmean = self.p.movav(self.tp, period=self.p.period)
def next(self):
AvgAbsDev = 0
for i in range (int(-self.p.period+1), 1):
AvgAbsDev = AvgAbsDev + abs(self.tp[i] - self.tpmean[0])
AvgAbsDev = AvgAbsDev / self.p.period
self.cci[0] = (self.tp[0] - self.tpmean[0]) / (self.p.factor * AvgAbsDev)
super(CommodityChannelIndex, self).__init__()