Skip to content

Commit

Permalink
feat: Add Chande Momentum Oscillator (CMO) (#370)
Browse files Browse the repository at this point in the history
  • Loading branch information
LeeDongGeon1996 committed Apr 7, 2024
1 parent ce944e6 commit ad9030c
Show file tree
Hide file tree
Showing 3 changed files with 102 additions and 0 deletions.
1 change: 1 addition & 0 deletions stock_indicators/indicators/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from .chandelier import (get_chandelier)
from .chop import (get_chop)
from .cmf import (get_cmf)
from .cmo import (get_cmo)
from .connors_rsi import (get_connors_rsi)
from .correlation import (get_correlation)
from .doji import (get_doji)
Expand Down
55 changes: 55 additions & 0 deletions stock_indicators/indicators/cmo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
from typing import Iterable, Optional, TypeVar

from stock_indicators._cslib import CsIndicator
from stock_indicators._cstypes import List as CsList
from stock_indicators.indicators.common.helpers import RemoveWarmupMixin
from stock_indicators.indicators.common.results import IndicatorResults, ResultBase
from stock_indicators.indicators.common.quote import Quote


def get_cmo(quotes: Iterable[Quote], lookback_periods: int):
"""Get CMO calculated.
The Chande Momentum Oscillator (CMO) is a momentum indicator
depicting the weighted percentof higher prices in financial markets.
Parameters:
`quotes` : Iterable[Quote]
Historical price quotes.
`lookback_periods` : int
Number of periods in the lookback window.
Returns:
`CMOResults[CMOResult]`
CMOResults is list of CMOResult with providing useful helper methods.
See more:
- [CMO Reference](https://python.stockindicators.dev/indicators/Cmo/#content)
- [Helper Methods](https://python.stockindicators.dev/utilities/#content)
"""
results = CsIndicator.GetCmo[Quote](CsList(Quote, quotes), lookback_periods)
return CMOResults(results, CMOResult)


class CMOResult(ResultBase):
"""
A wrapper class for a single unit of Chande Momentum Oscillator (CMO) results.
"""

@property
def cmo(self) -> Optional[float]:
return self._csdata.Cmo

@cmo.setter
def cmo(self, value):
self._csdata.Cmo = value


_T = TypeVar("_T", bound=CMOResult)
class CMOResults(RemoveWarmupMixin, IndicatorResults[_T]):
"""
A wrapper class for the list of Chande Momentum Oscillator (CMO) results.
It is exactly same with built-in `list` except for that it provides
some useful helper methods written in CSharp implementation.
"""
46 changes: 46 additions & 0 deletions tests/test_cmo.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import pytest
from stock_indicators import indicators

class TestCMO:
def test_standard(self, quotes):
results = indicators.get_cmo(quotes, 14)

assert 502 == len(results)
assert 488 == len(list(filter(lambda x: x.cmo is not None, results)))

r = results[13]
assert r.cmo is None

r = results[14]
assert 24.1081 == round(float(r.cmo), 4)

r = results[249]
assert 48.9614 == round(float(r.cmo), 4)

r = results[501]
assert -26.7502 == round(float(r.cmo), 4)

def test_bad_data(self, bad_quotes):
r = indicators.get_cmo(bad_quotes, 15)

assert 502 == len(r)

def test_no_quotes(self, quotes):
r = indicators.get_cmo([], 5)
assert 0 == len(r)

r = indicators.get_cmo(quotes[:1], 5)
assert 1 == len(r)

def test_removed(self, quotes):
results = indicators.get_cmo(quotes, 14).remove_warmup_periods()

assert 488 == len(results)

last = results.pop()
assert -26.7502 == round(float(last.cmo), 4)

def test_exceptions(self, quotes):
from System import ArgumentOutOfRangeException
with pytest.raises(ArgumentOutOfRangeException):
indicators.get_cmo(quotes, 0)

0 comments on commit ad9030c

Please sign in to comment.