GithubHelp home page GithubHelp logo

greyblake / ta-rs Goto Github PK

View Code? Open in Web Editor NEW
622.0 23.0 113.0 210 KB

Technical analysis library for Rust language

License: MIT License

Rust 100.00%
technical-analysis finance math library rust rust-lang financial financial-analysis finances stock-market

ta-rs's People

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ta-rs's Issues

Request Fractals Pattern Indicator

Add a fractals pattern indicator:
The Formulas for Fractals Are:
Bearish Fractal=
​High(N)>High(N−2) and
High(N)>High(N−1) and
High(N)>High(N+1) and
High(N)>High(N+2)

Bullish Fractal=
​Low(N)<Low(N−2) and
Low(N)<Low(N−1) and
Low(N)<Low(N+1) and
Low(N)<Low(N+2)

where:
N=High/low of the current price bar
N−2=High/low of price bar two periods
to the left of N
N−1=High/low of price bar one period
to the left of N
N+1=High/low of price bar one period
to the right of N
N+2=High/low of price bar two periods
to the right of N


Feature request: Hull moving average

It would be great, if you could add the HMA (Hull Moving Average) indicator.

This is the formula in PineScript:

length = input(9, minval=1)
src = input(close, title="Source")
hma = wma(2*wma(src, length/2)-wma(src, length), round(sqrt(length)))

Here is the website from Alan Hull, explaining the indicator. It's a moving average that is smoother than EMA and less lagging.

Why is a complete DataItem needed?

Just for example, OnBalanceVolume uses only close and volume. Yet I need to build a complete DataItem using the builder() method.
And this runs the validation if clause in DataItemBuiler::build() – no matter what.

It would be nice to be able to use the init struct pattern on DataItem to forego using a DataItemBuilder and allowing to skip the validation code.

In my case the data I feed into ta comes from tickers. So validation is not needed/correctness is guaranteed.

All that would be needed is adding a Default trait and a is_ok() method to DataItem and remove the validation code from DataItemBuiler::build(). That method would always just return a DataItem. Otherwise the method should be named build_and_validate() to express the hidden runtime penalty it entails.

As such TaError::DataItemIncomplete would not be needed any more since we acknowledge that some indicators work just fine with 'incomplete' DataItems and Data::Item::is_ok() could just return false if the check fails. Analogous what is now TaError::DataItemInvalid.

This suggestion would break the API/existing code of course.

The other option is to just add the Default trait and is_ok() methods to DataItem. This way new code using ta could use the aforementioned init struct pattern. A call to is_ok() can be wrapped in compile time flags to make sure it doesn't end up in release builds.

Does that make sense?

ATR value doesn't match expected values

When I compare the ATR values with tradingview and other technical analysis libraries like TA-lib (which uses the default ATR settings), I see that the values don't match at all.

Code:

let mut atr = ATR::new(14).unwrap();
    
for record in reader.deserialize() {
    let (_timestamp, open, high, low, close, volume): (String, f64, f64, f64, f64, f64) = record.unwrap();

    let atr_val = atr.next(&dt);

    println!("o: {}, h: {}, l: {}", open, high, low, close);
    println!("atr: {:?}\n", atr_val);

Output TA-lib (and tradingview):

                       open    high     low   close        volume       atr
timestamp                                                                  
2021-10-04 13:00:00  303.78  304.78  303.28  304.45  4.729795e+05  3.275000
2021-10-04 13:30:00  304.47  306.73  303.88  306.52  7.521677e+05  3.244643
2021-10-04 14:00:00  306.52  309.34  305.63  308.54  1.235306e+06  3.277883
2021-10-04 14:30:00  308.54  309.22  295.05  298.66  2.537776e+06  4.055891
2021-10-04 15:00:00  298.66  299.28  290.72  298.69  4.477394e+06  4.377613
2021-10-04 15:30:00  298.69  302.13  298.32  300.61  9.675177e+05  4.337069

Output ta-rs:

o: 303.78, h: 304.78, l: 303.28, c: 304.45
atr: 3.284237282587478

o: 304.47, h: 306.73, l: 303.88, c: 306.52
atr: 3.226338978242484

o: 306.52, h: 309.34, l: 305.63, c: 308.54
atr: 3.2908271144768166

o: 308.54, h: 309.22, l: 295.05, c: 298.66
atr: 4.741383499213243

o: 298.66, h: 299.28, l: 290.72, c: 298.69
atr: 5.250532365984803

o: 298.69, h: 302.13, l: 298.32, c: 300.61
atr: 5.058461383853496

Add current value method to indicators

It would be very nice to have a method that would evaluate an indicator, and return it's current value. Current interface, that returns value only when new item is added is extremely clunky.

Second/subsequent iterations of `EfficiencyRatio` is `NaN` when price is stationary.

let mut ker = EfficiencyRatio::new(14)?;
println!("1  {}", ker.next(1.69));
println!("2  {}", ker.next(1.69));
println!("3  {}", ker.next(1.70));
println!("4  {}", ker.next(1.70));
println!("5  {}", ker.next(1.69));

Output:

1  1
2  NaN
3  1
4  1
5  0

If you keep passing 1.69, it subsequently outputs NaN.

ad hoc workaround

// https://github.com/greyblake/ta-rs/issues/73
struct FixEfficiencyRatio {
    er: EfficiencyRatio,
}
impl FixEfficiencyRatio {
    fn new(period: usize) -> ta::errors::Result<Self> {
        Ok(Self {
            er: EfficiencyRatio::new(period)?,
        })
    }
}
impl<T: Close> ta::Next<&T> for FixEfficiencyRatio {
    type Output = f64;
    fn next(&mut self, input: &T) -> f64 {
        match self.er.next(input) {
            v if v.is_nan() => 1.0,
            v => v,
        }
    }
}

KER implementation could be simplified with VecDeque.

Any interest in serde support?

A while back I forked this repo for a summer project and one of the things I added to my fork was serde support before I got busy with school and work. If I opened a PR for that would it be welcome? In its present state it may be a bit disruptive to the API since if I recall correctly I had to adjust one of the traits.

Add simple RollingWindow trait

Pretty much just

trait RollingWindow {
  fn length(&self) -> usize;
}

for all indicators. This won't hide the fact that all these indicators are essentially rolling window filters and need to be pre-fed with historical data with length (RollingWindow::length -1). For combined indicators this is of course the max rolling window length of all indicators (-1, of course).

I use this information in order to read the pre-feed for the window e.g. via ordinary rest calls and then turn to websockets based listening and async streaming. The first yielded value is then the current (in time) value for the given indicator.

Keeping track of number updates

I have a hard time understand how to interpret results that next generate before the full period. For exampel AverageTrueRange next will start emitting values at the first update, in my mind it should return an Optional where the first 13 updates would return None. Right now I have to keep track of the number of updates in a counter and wrap the ATR indicator. Am I wrong in my approach or how are you using it in actual systems?

DataItemIncomplete when trying to unwrap DataItem build

I tried to make a DataItem vector from a Candle vector. but when I do this:

DataItem::builder()
          .high(i.high.to_f64().unwrap())
          .low(i.low.to_f64().unwrap())
          .close(i.close.to_f64().unwrap())
          .volume(i.volume.to_f64().unwrap())
          .build().unwrap()

(i is a Candle in a for loop) it throws a DataItemIncomplete error.

Addition of volume based Indicators

As volume is the only other variable with price, addition of volume based indicators would be a good feature.
Can we add Money flow Indicator to start with?

Failing regression tests from original TA Lib

See subject.

I started doing this here. I only added tests for SMA and EMA. SMA works, i.e. the SMA of ta-rs matches the SMA output of TA Lib 1:1. But for EMA it fails.

There are two options: I am doing something wrong or the EMA implementation in ta-rs has a bug. I'm just opening this issue here to get some opinions.

The code I copied for data.rs is from test_data.c.

The actual tests are vastly simplified from the ones in TA Lib. I.e. I only run the iterator for the resp. indicator n times and compare the result with the expected one. See here.

Give to Typical Price his own structure ?

More and more indicators starts to use the typical price.
Does it have any sense to create a structure TypicalPrice which will implement Next?

We will (for example) be able to change:

impl<T: Close + High + Low> Next<&T> for CommodityChannelIndex {
    type Output = f64;

    fn next(&mut self, input: &T) -> Self::Output {
        let tp = (input.close() + input.high() + input.low()) / 3.0; // <--- before
        let sma = self.sma.next(tp);
        let mad = self.mad.next(input);

        if mad == 0.0 {
            return 0.0;
        }

        (tp - sma) / (mad * 0.015)
    }
}

to:

impl<T: Close + High + Low> Next<&T> for CommodityChannelIndex {
    type Output = f64;

    fn next(&mut self, input: &T) -> Self::Output {
        let tp = self.tp.next(input); // <--- after
        let sma = self.sma.next(tp);
        let mad = self.mad.next(input);

        if mad == 0.0 {
            return 0.0;
        }

        (tp - sma) / (mad * 0.015)
    }
}

Pros:

  • The crate provide a new indicator.
  • We exclude even more logic from our current indicators.
  • Let's imagine a future where our indicators can share a stack, so we do not allocate one deque: Box<[f64]> per indicator but only ones with the length of the longest period. Putting TP inside an indicator can offer even more optimization for our "group of indicators" like calculating it only ones and sharing his value across all the group.
    Idea: calling Next on the group run Calc for each indicator. Calc takes all the parameters and only apply the formula of the indicator (so TP, MAD(20), MAD(x), etc, cannot be processed twice):
fn calc(input: &CommodityChannelIndexInput) -> Self::Output {
    (input.tp - input.sma) / (input.mad * 0.015)
}

Cons:

  • This is a one-liner...

Would it be possible to limit the number of input values?

Hi,

Would it be possible to limit the number of input values? Instead of adding the value to the next iterator, would it be possible to only use for example 201 input values to calculate the Exponential Moving Average? This is because if you enter for example 500 input values into an ema with a period of 200 you get a totally different output as when you enter 201 input values.

Add DX/DMI & ADX

Requires DM-/DM+, DI-/DI+ first.

I started implementing all of them in my fork as I need them for the project I'm working on.
While they're all there now I haven't yet verified them and/or wrote tests.

I will update this issue and open a PR once that is done.

In the meantime I'm happy for feedback – just opening this issue so others can benefit/don't do work twice.

[help]Test used times.

could u do a test vs the ta-lib implement by cpp.
forgive me . i wanna test by myself but i dont know how to use this lib .
for example . bb indicator.

image
image

pls show a simple example like implement the function above. using the ta-rs lib. calculate the bb indicator.

How to use special inputs like ohlc4?

How can I use special inputs like ohlc4 and hlc3, when making use of the indicators?

use ta::indicators::SimpleMovingAverage as SMA;
use ta::DataItem;
use ta::Next;

fn main() {
    let mut sma = SMA::new(4).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) =
            record.unwrap();
        let dt = DataItem::builder()
            .open(open)
            .high(high)
            .low(low)
            .close(close)
            .volume(volume)
            .build()
            .unwrap();
            
        let ohlc4 = (open + high + low + close) / 4.0;
        
        let sma_val = sma.next(ohlc4);
        println!("{}: {} = {:2.2}", date, ema, sma_val);
    }
}

When I try this code snippet I don't get the simple moving average of the ohlc4 (checked ohlc4 and is correct).

RSI value does NOT match expected values

Hello,

I've been using the RSI indicator provided by ta.
Using the RSI indicator, I compute some higher level indicators, their form is not important here.

I wanted to verify that I had implemented the indicator correctly, and so I wrote extensive test cases on real data lifted from TradingView.

This is when I noticed that the RSI value computed on TradingView does not match the RSI computed from this library.

I figured out the issues here:

  1. The EMAs internal to the ta RSI use a different alpha (k field), as 2 / (period - 1), whereas on TV, the RSI uses an EMA with alpha of 1.0 / period.
  2. The computation of the RSI on TV uses a form that handles division by zero differently, otherwise the expressions should be equivalent.

I am not sure which version of the RSI is the intended "official" RSI,
but in order to make the RSI implementations match I've had to change things inside the ta library.
If you have any information on this please let me know, I am a bit clueless.

My suggested change is that you provide a function on EMA and dependent indicators to set the alpha parameter used.

Add Bollinger Bands Indicator

I think that Bollinger Bands Indicator would be a nice to have feature.
I see implementation on this indicator as:

  • struct that will extend moving average struct by using it as part of self data struct,
  • will share common project trait,
  • next function will return tuple of (moving average: i32, upper_bound: i32, lower_bound: i32 ) or corresponding data struct,
  • new function will take (n: i32, k: i32) where n is buffer size and k is standard deviation multiplier that multiply itself by standard deviation to indicate upper and lower bound according to Bollinger Band formula,

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.