Skip to content

Commit

Permalink
Ported ChandeMomentumOscillator
Browse files Browse the repository at this point in the history
  • Loading branch information
Pushkarm029 committed Feb 20, 2024
1 parent 61debf2 commit 4425bda
Show file tree
Hide file tree
Showing 6 changed files with 241 additions and 0 deletions.
125 changes: 125 additions & 0 deletions nautilus_core/indicators/src/momentum/cmo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
// -------------------------------------------------------------------------------------------------
// Copyright (C) 2015-2023 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------

use std::fmt::Display;

use anyhow::Result;
use nautilus_model::data::{bar::Bar, quote::QuoteTick, trade::TradeTick};
use pyo3::prelude::*;

use crate::{
average::{MovingAverageFactory, MovingAverageType},
indicator::{Indicator, MovingAverage},
};

#[repr(C)]
#[derive(Debug)]
#[pyclass(module = "nautilus_trader.core.nautilus.pyo3.indicators")]
pub struct ChandeMomentumOscillator {
pub period: usize,
pub average_gain: Box<dyn MovingAverage + Send + 'static>,
pub average_loss: Box<dyn MovingAverage + Send + 'static>,
pub previous_close: f64,
pub value: f64,
pub count: usize,
pub is_initialized: bool,
has_inputs: bool,
}

impl Display for ChandeMomentumOscillator {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}({})", self.name(), self.period)
}
}

impl Indicator for ChandeMomentumOscillator {
fn name(&self) -> String {
stringify!(ChandeMomentumOscillator).to_string()
}

fn has_inputs(&self) -> bool {
self.has_inputs
}

fn is_initialized(&self) -> bool {
self.is_initialized
}

fn handle_quote_tick(&mut self, _tick: &QuoteTick) {
// Function body intentionally left blank.
}

fn handle_trade_tick(&mut self, _tick: &TradeTick) {
// Function body intentionally left blank.
}

fn handle_bar(&mut self, bar: &Bar) {
self.update_raw((&bar.close).into());
}

fn reset(&mut self) {
self.value = 0.0;
self.count = 0;
self.has_inputs = false;
self.is_initialized = false;
self.previous_close = 0.0;
}
}

impl ChandeMomentumOscillator {
pub fn new(period: usize) -> Result<Self> {
Ok(Self {
period,
average_gain: MovingAverageFactory::create(MovingAverageType::Wilder, period),
average_loss: MovingAverageFactory::create(MovingAverageType::Wilder, period),
previous_close: 0.0,
value: 0.0,
count: 0,
is_initialized: false,
has_inputs: false,
})
}

pub fn update_raw(&mut self, close: f64) {
if !self.has_inputs {
self.previous_close = close;
self.has_inputs = true;
}

let gain: f64 = close - self.previous_close;
if gain > 0.0 {
self.average_gain.update_raw(gain);
self.average_loss.update_raw(0.0);
} else if gain < 0.0 {
self.average_gain.update_raw(0.0);
self.average_loss.update_raw(-gain);
} else {
self.average_gain.update_raw(0.0);
self.average_loss.update_raw(0.0);
}

if !self.is_initialized
&& self.average_gain.is_initialized()
&& self.average_loss.is_initialized()
{
self.is_initialized = true;
}
if self.is_initialized {
self.value = 100.0 * (self.average_gain.value() - self.average_loss.value())
/ (self.average_gain.value() + self.average_loss.value());
}
self.previous_close = close;
}
}
1 change: 1 addition & 0 deletions nautilus_core/indicators/src/momentum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
// -------------------------------------------------------------------------------------------------

pub mod aroon;
pub mod cmo;
pub mod rsi;
1 change: 1 addition & 0 deletions nautilus_core/indicators/src/python/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,5 +33,6 @@ pub fn indicators(_: Python<'_>, m: &PyModule) -> PyResult<()> {
// momentum
m.add_class::<crate::momentum::rsi::RelativeStrengthIndex>()?;
m.add_class::<crate::momentum::aroon::AroonOscillator>()?;
m.add_class::<crate::momentum::cmo::ChandeMomentumOscillator>()?;
Ok(())
}
93 changes: 93 additions & 0 deletions nautilus_core/indicators/src/python/momentum/cmo.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// -------------------------------------------------------------------------------------------------
// Copyright (C) 2015-2024 Nautech Systems Pty Ltd. All rights reserved.
// https://nautechsystems.io
//
// Licensed under the GNU Lesser General Public License Version 3.0 (the "License");
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at https://www.gnu.org/licenses/lgpl-3.0.en.html
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// -------------------------------------------------------------------------------------------------

use nautilus_core::python::to_pyvalue_err;
use nautilus_model::data::{bar::Bar, quote::QuoteTick, trade::TradeTick};
use pyo3::prelude::*;

use crate::{indicator::Indicator, momentum::cmo::ChandeMomentumOscillator};

#[pymethods]
impl ChandeMomentumOscillator {
#[new]
pub fn py_new(period: usize) -> PyResult<Self> {
Self::new(period).map_err(to_pyvalue_err)
}

#[getter]
#[pyo3(name = "name")]
fn py_name(&self) -> String {
self.name()
}

#[getter]
#[pyo3(name = "period")]
fn py_period(&self) -> usize {
self.period
}

#[getter]
#[pyo3(name = "has_inputs")]
fn py_has_inputs(&self) -> bool {
self.has_inputs()
}

#[getter]
#[pyo3(name = "count")]
fn py_count(&self) -> usize {
self.count
}

#[getter]
#[pyo3(name = "value")]
fn py_value(&self) -> f64 {
self.value
}

#[getter]
#[pyo3(name = "initialized")]
fn py_initialized(&self) -> bool {
self.is_initialized
}

#[pyo3(name = "update_raw")]
fn py_update_raw(&mut self, close: f64) {
self.update_raw(close);
}

#[pyo3(name = "handle_quote_tick")]
fn py_handle_quote_tick(&mut self, _tick: &QuoteTick) {
// Function body intentionally left blank.
}

#[pyo3(name = "handle_trade_tick")]
fn py_handle_trade_tick(&mut self, _tick: &TradeTick) {
// Function body intentionally left blank.
}

#[pyo3(name = "handle_bar")]
fn py_handle_bar(&mut self, bar: &Bar) {
self.update_raw((&bar.close).into());
}

#[pyo3(name = "reset")]
fn py_reset(&mut self) {
self.reset()
}

fn __repr__(&self) -> String {
format!("ChandeMomentumOscillator({})", self.period)
}
}
1 change: 1 addition & 0 deletions nautilus_core/indicators/src/python/momentum/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,5 @@
// -------------------------------------------------------------------------------------------------

pub mod aroon;
pub mod cmo;
pub mod rsi;
20 changes: 20 additions & 0 deletions nautilus_trader/core/nautilus_pyo3.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -1940,6 +1940,26 @@ class AroonOscillator:
def handle_bar(self, bar: Bar) -> None: ...
def reset(self) -> None: ...

class ChandeMomentumOscillator:
def __init__(
self,
period: int,
) -> None: ...
@property
def name(self) -> str: ...
@property
def period(self) -> int: ...
@property
def count(self) -> int: ...
@property
def initialized(self) -> bool: ...
@property
def has_inputs(self) -> bool: ...
@property
def value(self) -> float: ...
def update_raw(self, close: float) -> None: ...
def handle_bar(self, bar: Bar) -> None: ...
def reset(self) -> None: ...

###################################################################################################
# Adapters
Expand Down

0 comments on commit 4425bda

Please sign in to comment.