Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Support for Decimal within ta-rs. #59

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ script:
# - cargo clippy -- -D warnings
- cargo test
- cargo test --features serde
- cargo test --features decimal --tests
- cargo package
6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,18 @@ travis-ci = { repository = "greyblake/ta-rs", branch = "master" }

[dependencies]
serde = { version = "1.0", features = ["derive"], optional = true }
rust_decimal = { version = "^1.25.0", optional = true, features = ["maths", "rand"] }

[dev-dependencies]
assert_approx_eq = "1.0.0"
csv = "1.1.0"
bencher = "0.1.5"
rand = "0.6.5"
rand = "0.8"
bincode = "1.3.1"

[features]
decimal = ["dep:rust_decimal"]

[profile.release]
lto = true

Expand Down
96 changes: 49 additions & 47 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,17 +9,18 @@

Technical analysis library for Rust.

* [Getting started](#getting-started)
* [Basic ideas](#basic-ideas)
* [List of indicators](#list-of-indicators)
* [Running benchmarks](#running-benchmarks)
* [Donations](#donations)
* [License](#license)
* [Contributors](#contributors)
- [Getting started](#getting-started)
- [Basic ideas](#basic-ideas)
- [List of indicators](#list-of-indicators)
- [Running benchmarks](#running-benchmarks)
- [Donations](#donations)
- [License](#license)
- [Contributors](#contributors)

## Getting started

Add to you `Cargo.toml`:

```
[dependencies]
ta = "0.4.0"
Expand Down Expand Up @@ -47,58 +48,60 @@ Check also the [documentation](https://docs.rs/ta).

A data item which represent a stock quote may implement the following traits:

* `Open`
* `High`
* `Low`
* `Close`
* `Volume`
- `Open`
- `High`
- `Low`
- `Close`
- `Volume`

It's not necessary to implement all of them, but it must be enough to fulfill requirements for a particular indicator.
You probably should prefer using `DataItem` unless you have reasons to implement your own structure.

Indicators typically implement the following traits:

* `Next<T>` (often `Next<f64>` and `Next<&DataItem>`) - to feed and get the next value
* `Reset` - to reset an indicator
* `Debug`
* `Display`
* `Default`
* `Clone`
- `Next<T>` (often `Next<f64>` and `Next<&DataItem>`) - to feed and get the next value
- `Reset` - to reset an indicator
- `Debug`
- `Display`
- `Default`
- `Clone`

## List of indicators

So far there are the following indicators available.

* Trend
* Exponential Moving Average (EMA)
* Simple Moving Average (SMA)
* Oscillators
* Relative Strength Index (RSI)
* Fast Stochastic
* Slow Stochastic
* Moving Average Convergence Divergence (MACD)
* Percentage Price Oscillator (PPO)
* Commodity Channel Index (CCI)
* Money Flow Index (MFI)
* Other
* Minimum
* Maximum
* True Range
* Standard Deviation (SD)
* Mean Absolute Deviation (MAD)
* Average True Range (AR)
* Efficiency Ratio (ER)
* Bollinger Bands (BB)
* Chandelier Exit (CE)
* Keltner Channel (KC)
* Rate of Change (ROC)
* On Balance Volume (OBV)

- Trend
- Exponential Moving Average (EMA)
- Simple Moving Average (SMA)
- Oscillators
- Relative Strength Index (RSI)
- Fast Stochastic
- Slow Stochastic
- Moving Average Convergence Divergence (MACD)
- Percentage Price Oscillator (PPO)
- Commodity Channel Index (CCI)
- Money Flow Index (MFI)
- Other
- Minimum
- Maximum
- True Range
- Standard Deviation (SD)
- Mean Absolute Deviation (MAD)
- Average True Range (AR)
- Efficiency Ratio (ER)
- Bollinger Bands (BB)
- Chandelier Exit (CE)
- Keltner Channel (KC)
- Rate of Change (ROC)
- On Balance Volume (OBV)

## Features

* `serde` - allows to serialize and deserialize indicators. NOTE: the backward compatibility of serialized
data with the future versions of ta is not guaranteed because internal implementation of the indicators is a subject to change.
- `decimal` - when enabled, uses `Decimal` objects from the [`rust_decimal`] crate instead of `f64`.
- `serde` - allows to serialize and deserialize indicators. NOTE: the backward compatibility of serialized
data with the future versions of ta is not guaranteed because internal implementation of the indicators is a subject to change.

[`rust_decimal`]: https://docs.rs/rust_decimal

## Running benchmarks

Expand All @@ -112,12 +115,10 @@ You can support the project by donating [NEAR tokens](https://near.org).

Our NEAR wallet address is `ta-rs.near`


## License

[MIT](https://github.com/greyblake/ta-rs/blob/master/LICENSE) © [Sergey Potapov](http://greyblake.com/)


## Contributors

- [greyblake](https://github.com/greyblake) Potapov Sergey - creator, maintainer.
Expand All @@ -129,3 +130,4 @@ Our NEAR wallet address is `ta-rs.near`
- [Devin Gunay](https://github.com/dgunay) - serde support
- [Youngchan Lee](https://github.com/edwardycl) - bugfix
- [tommady](https://github.com/tommady) - get rid of error-chain dependency
- [Luke Sneeringer](https://github.com/lukesneeringer) - Decimal implementation
12 changes: 6 additions & 6 deletions benches/indicators.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,18 +7,18 @@ use ta::indicators::{
PercentagePriceOscillator, RateOfChange, RelativeStrengthIndex, SimpleMovingAverage,
SlowStochastic, StandardDeviation, TrueRange, WeightedMovingAverage,
};
use ta::{DataItem, Next};
use ta::{lit, DataItem, Next};

const ITEMS_COUNT: usize = 5_000;

fn rand_data_item() -> DataItem {
let mut rng = rand::thread_rng();

let low = rng.gen_range(0.0, 500.0);
let high = rng.gen_range(500.0, 1000.0);
let open = rng.gen_range(low, high);
let close = rng.gen_range(low, high);
let volume = rng.gen_range(0.0, 10_000.0);
let low = rng.gen_range(lit!(0.0)..=lit!(500.0));
let high = rng.gen_range(lit!(500.0)..=lit!(1000.0));
let open = rng.gen_range(low..=high);
let close = rng.gen_range(low..=high);
let volume = rng.gen_range(lit!(0.0)..=lit!(10_000.0));

DataItem::builder()
.open(open)
Expand Down
19 changes: 12 additions & 7 deletions examples/custom_data_item.rs
Original file line number Diff line number Diff line change
@@ -1,30 +1,35 @@
use ta::indicators::TrueRange;
use ta::{Close, High, Low, Next};

#[cfg(feature = "decimal")]
type Num = rust_decimal::Decimal;
#[cfg(not(feature = "decimal"))]
type Num = f64;

// You can create your own data items.
// You may want it for different purposes, e.g.:
// - you data source don't have volume or other fields.
// - you want to skip validation to avoid performance penalty.
struct Item {
high: f64,
low: f64,
close: f64,
high: Num,
low: Num,
close: Num,
}

impl Low for Item {
fn low(&self) -> f64 {
fn low(&self) -> Num {
self.low
}
}

impl High for Item {
fn high(&self) -> f64 {
fn high(&self) -> Num {
self.high
}
}

impl Close for Item {
fn close(&self) -> f64 {
fn close(&self) -> Num {
self.close
}
}
Expand All @@ -34,7 +39,7 @@ fn main() {
let mut reader = csv::Reader::from_path("./examples/data/AMZN.csv").unwrap();

for record in reader.deserialize() {
let (date, _open, high, low, close, _volume): (String, f64, f64, f64, f64, f64) =
let (date, _open, high, low, close, _volume): (String, Num, Num, Num, Num, Num) =
record.unwrap();
let item = Item { high, low, close };
let val = tr.next(&item);
Expand Down
7 changes: 6 additions & 1 deletion examples/ema.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,17 @@ use ta::indicators::ExponentialMovingAverage as Ema;
use ta::DataItem;
use ta::Next;

#[cfg(feature = "decimal")]
type Num = rust_decimal::Decimal;
#[cfg(not(feature = "decimal"))]
type Num = f64;

fn main() {
let mut ema = Ema::new(9).unwrap();
let mut reader = csv::Reader::from_path("./examples/data/AMZN.csv").unwrap();

for record in reader.deserialize() {
let (date, open, high, low, close, volume): (String, f64, f64, f64, f64, f64) =
let (date, open, high, low, close, volume): (String, Num, Num, Num, Num, Num) =
record.unwrap();
let dt = DataItem::builder()
.open(open)
Expand Down
Loading