Skip to content

Commit

Permalink
[GH-143] Retrieve Chande Momentum Oscillator (#144)
Browse files Browse the repository at this point in the history
The Chande Momentum Oscillator (CMO) is a technical momentum
indicator developed by Tushar Chande.
https://www.investopedia.com/terms/c/chandemomentumoscillator.asp

```
CMO = 100 * ((sH - sL) / (sH + sL))
```

where:
* sH=the sum of higher closes over N periods
* sL=the sum of lower closes of N periods
  • Loading branch information
jealous authored Jun 16, 2023
1 parent 78306b6 commit f673957
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 3 deletions.
26 changes: 25 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
[![codecov](https://codecov.io/gh/jealous/stockstats/branch/master/graph/badge.svg?token=IFMD1pVJ7T)](https://codecov.io/gh/jealous/stockstats)
[![pypi](https://img.shields.io/pypi/v/stockstats.svg)](https://pypi.python.org/pypi/stockstats)

VERSION: 0.5.4
VERSION: 0.5.5

## Introduction

Expand Down Expand Up @@ -725,6 +725,30 @@ BOP = (close - open) / (high - low)
Example:
* `df['bop']` returns the Balance of Power

#### [Chande Momentum Oscillator] (https://www.investopedia.com/terms/c/chandemomentumoscillator.asp)

The Chande Momentum Oscillator (CMO) is a technical momentum
indicator developed by Tushar Chande.

The formula calculates the difference between the sum of recent
gains and the sum of recent losses and then divides the result
by the sum of all price movements over the same period.

The default window is 14.

Formular:
```
CMO = 100 * ((sH - sL) / (sH + sL))
```

where:
* sH=the sum of higher closes over N periods
* sL=the sum of lower closes of N periods

Examples:
* `df['cmo']` returns the CMO with a window of 14
* `df['cmo_5']` returns the CMO with a window of 5

## Issues

We use [Github Issues](https://github.com/jealous/stockstats/issues) to track
Expand Down
41 changes: 39 additions & 2 deletions stockstats.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ class StockDataFrame(pd.DataFrame):

WR = 14

CMO = 14

WAVE_TREND_1 = 10
WAVE_TREND_2 = 21

Expand Down Expand Up @@ -728,9 +730,9 @@ def _get_um_dm(self):
initialize up move and down move
"""
hd = self['high_delta']
hd = self._col_delta('high')
self['um'] = (hd + hd.abs()) / 2
ld = -self['low_delta']
ld = -self._col_delta('low')
self['dm'] = (ld + ld.abs()) / 2

def _get_pdm(self, windows):
Expand Down Expand Up @@ -1215,6 +1217,37 @@ def _get_bop(self):
divisor = self['high'] - self['low']
self['bop'] = dividend / divisor

def _get_cmo(self, window=None):
""" get Chande Momentum Oscillator
The Chande Momentum Oscillator (CMO) is a technical momentum
indicator developed by Tushar Chande.
https://www.investopedia.com/terms/c/chandemomentumoscillator.asp
CMO = 100 * ((sH - sL) / (sH + sL))
where:
* sH=the sum of higher closes over N periods
* sL=the sum of lower closes of N periods
"""
if window is None:
window = self.CMO
column_name = 'cmo'
else:
window = self.get_int_positive(window)
column_name = 'cmo_{}'.format(window)

close_diff = self['close'].diff()
up = close_diff.clip(lower=0)
down = close_diff.clip(upper=0).abs()
sum_up = self._mov_sum(up, window)
sum_down = self._mov_sum(down, window)
dividend = sum_up - sum_down
divisor = sum_up + sum_down
res = 100 * dividend / divisor
res.iloc[0] = 0
self[column_name] = res

def _get_kama(self, column, windows, fasts=None, slows=None):
""" get Kaufman's Adaptive Moving Average.
Implemented after
Expand Down Expand Up @@ -1298,6 +1331,9 @@ def _get_rate(self):
"""
self['rate'] = self['close'].pct_change() * 100

def _col_delta(self, col):
return self[col].diff()

def _get_delta(self, key):
key_to_delta = key.replace('_delta', '')
self[key] = self[key_to_delta].diff()
Expand Down Expand Up @@ -1395,6 +1431,7 @@ def handler(self):
('aroon',): self._get_aroon,
('ao',): self._get_ao,
('bop',): self._get_bop,
('cmo',): self._get_cmo,
}

def __init_not_exist_column(self, key):
Expand Down
13 changes: 13 additions & 0 deletions test.py
Original file line number Diff line number Diff line change
Expand Up @@ -687,6 +687,19 @@ def test_bop(self):
assert_that(bop[20110104], near_to(0.5))
assert_that(bop[20110106], near_to(-0.207))

def test_cmo(self):
stock = self.get_stock_30day()
cmo = stock['cmo']
assert_that(cmo[20110104], equal_to(0))
assert_that(cmo[20110126], near_to(7.023))
assert_that(cmo[20110127], near_to(-16.129))

cmo_14 = stock['cmo_14']
assert_that(cmo_14[20110126], near_to(7.023))

cmo_5 = stock['cmo_5']
assert_that(cmo_5[20110126], near_to(7.895))

def test_drop_column_inplace(self):
stock = self._supor[:20]
stock.columns.name = 'Luke'
Expand Down

0 comments on commit f673957

Please sign in to comment.