├── run.sh ├── .DS_Store ├── src ├── .DS_Store ├── errors.rs ├── helpers.rs ├── qafetch.rs ├── lib.rs ├── main.rs ├── traits.rs ├── indicators │ ├── mod.rs │ ├── on_balance_volume.rs │ ├── efficiency_ratio.rs │ ├── minimum.rs │ ├── slow_stochastic.rs │ ├── maximum.rs │ ├── rate_of_change.rs │ ├── average_true_range.rs │ ├── simple_moving_average.rs │ ├── hhv.rs │ ├── standard_deviation.rs │ ├── bollinger_bands.rs │ ├── fast_stochastic.rs │ ├── relative_strength_index.rs │ ├── moving_average_convergence_divergence.rs │ ├── true_range.rs │ ├── exponential_moving_average.rs │ ├── llv.rs │ ├── moving_average.rs │ └── money_flow_index.rs ├── qadata.rs ├── test_helper.rs ├── transaction.rs ├── qaorder.rs ├── data_item.rs ├── qaindicator.rs └── qaperformance.rs ├── .gitignore ├── tests └── test.rs ├── cloudbuild.yaml ├── .github └── workflows │ └── rust.yml ├── examples ├── asynctest.rs ├── x1.rs ├── x2.rs ├── qadata.rs ├── t01b2.rs └── t01b2D.rs ├── debug ├── read_his.py └── t01b2.py ├── Cargo.toml ├── README.md └── benches └── indicators.rs /run.sh: -------------------------------------------------------------------------------- 1 | cargo run --example t01b2 < ./data/RBL8_15min_2019.csv --release -------------------------------------------------------------------------------- /.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yutiansut/qaaccount-rs/HEAD/.DS_Store -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yutiansut/qaaccount-rs/HEAD/src/.DS_Store -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | .idea/ 4 | .vscode/ 5 | ~/.quantaxis 6 | *.log 7 | *.csv 8 | *.txt -------------------------------------------------------------------------------- /tests/test.rs: -------------------------------------------------------------------------------- 1 | extern crate csv; 2 | extern crate quantaxis_rs; 3 | 4 | // TODO: implement some integration tests 5 | -------------------------------------------------------------------------------- /cloudbuild.yaml: -------------------------------------------------------------------------------- 1 | steps: 2 | - name: 'rust:1.41' 3 | entrypoint: cargo 4 | args: ['build', '--example', 't01b2', '--release', '-j', '20'] 5 | -------------------------------------------------------------------------------- /src/errors.rs: -------------------------------------------------------------------------------- 1 | error_chain! { 2 | errors { 3 | InvalidParameter { description("invalid parameter") } 4 | DataItemIncomplete { description("data item is incomplete") } 5 | DataItemInvalid { description("data item is invalid") } 6 | OrderInvalid { description("order is invalid") } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.github/workflows/rust.yml: -------------------------------------------------------------------------------- 1 | name: Rust 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | steps: 11 | - uses: actions/checkout@v2 12 | - name: Build 13 | run: cargo build --verbose 14 | - name: Run tests 15 | run: cargo test --verbose 16 | - name: Run Bench 17 | run: cargo bench --verbose 18 | -------------------------------------------------------------------------------- /src/helpers.rs: -------------------------------------------------------------------------------- 1 | /// Returns the largest of 3 given numbers. 2 | pub fn max3(a: f64, b: f64, c: f64) -> f64 { 3 | a.max(b).max(c) 4 | } 5 | 6 | #[cfg(test)] 7 | mod tests { 8 | use super::*; 9 | 10 | #[test] 11 | fn test_max3() { 12 | assert_eq!(max3(3.0, 2.0, 1.0), 3.0); 13 | assert_eq!(max3(2.0, 3.0, 1.0), 3.0); 14 | assert_eq!(max3(2.0, 1.0, 3.0), 3.0); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /src/qafetch.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error; 2 | use std::io; 3 | use std::process; 4 | use serde::{Deserialize, Serialize}; 5 | 6 | #[derive(Debug, Clone, Serialize, Deserialize)] 7 | pub struct BAR { 8 | pub code: String, 9 | pub datetime: String, 10 | pub open: f64, 11 | pub high: f64, 12 | pub low: f64, 13 | pub close: f64, 14 | pub volume: f64, 15 | } 16 | 17 | impl BAR { 18 | pub fn print(&self) { 19 | println!("{:#?} -{:#?} ", self.datetime, self.open) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate error_chain; 3 | 4 | pub use crate::data_item::DataItem; 5 | pub use crate::traits::*; 6 | 7 | pub mod indicators; 8 | pub mod market_preset; 9 | pub mod qaaccount; 10 | pub mod qadata; 11 | pub mod qafetch; 12 | pub mod qaindicator; 13 | pub mod qaorder; 14 | pub mod qaposition; 15 | pub mod transaction; 16 | pub mod qaperformance; 17 | pub mod trade_date; 18 | 19 | 20 | #[cfg(test)] 21 | #[macro_use] 22 | mod test_helper; 23 | 24 | mod helpers; 25 | 26 | pub mod errors; 27 | 28 | mod data_item; 29 | mod traits; 30 | -------------------------------------------------------------------------------- /examples/asynctest.rs: -------------------------------------------------------------------------------- 1 | use tokio::runtime::Builder; 2 | use tokio::sync::oneshot; 3 | use tokio::task; 4 | use std::rc::Rc; 5 | use std::cell::RefCell; 6 | use quantaxis_rs::qaaccount::QA_Account; 7 | use std::borrow::BorrowMut; 8 | use std::sync::{Arc, Mutex}; 9 | use tokio::runtime; 10 | 11 | 12 | async fn some_computation() -> String { 13 | "represents the result of the computation".to_string() 14 | } 15 | 16 | 17 | #[tokio::main] 18 | async fn main() { 19 | let code = "RB2005".to_string(); 20 | let mut acc = QA_Account::new("RustT01B2_RBL8", "test", "admin", 100000.0, false, "real"); 21 | 22 | 23 | 24 | let order = acc.send_order_async(&code, 10.0, "2020-01-20 22:10:00", 2, 3500.0, "BUY_OPEN").await; 25 | println!("{:#?}", order.unwrap()); 26 | 27 | 28 | } -------------------------------------------------------------------------------- /debug/read_his.py: -------------------------------------------------------------------------------- 1 | import QUANTAXIS as QA 2 | import pandas as pd 3 | 4 | user = QA.QA_User(username='admin', password='admin') 5 | port = user.new_portfolio('rust') 6 | debug_acc = port.new_account('Rust_T01B2_RB_2019_x4', market_type=QA.MARKET_TYPE.FUTURE_CN, init_cash=1000000) 7 | trade_his = pd.read_csv('../RustT01B2_RBL8.csv') 8 | for _, item in trade_his.iterrows(): 9 | res = debug_acc.receive_simpledeal(code=item['code'], trade_price=item['price'], 10 | trade_amount=abs(item['amount']), 11 | trade_towards=item['direction'], 12 | trade_time=item['datetime']) 13 | # print(debug_acc.cash_available) 14 | 15 | print(debug_acc.history_table) 16 | debug_acc.save() 17 | Risk = QA.QA_Risk(debug_acc) 18 | Risk.save() 19 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate ndarray; 2 | extern crate ndarray_csv; 3 | extern crate num_traits; 4 | extern crate serde; 5 | extern crate stopwatch; 6 | 7 | use std::error::Error; 8 | use std::io; 9 | use std::process; 10 | 11 | use csv::{ReaderBuilder, WriterBuilder}; 12 | use ndarray::{array, stack}; 13 | use ndarray::prelude::*; 14 | use serde_json; 15 | use stopwatch::Stopwatch; 16 | 17 | pub mod market_preset; 18 | pub mod qaaccount; 19 | pub mod qadata; 20 | pub mod qafetch; 21 | pub mod qaindicator; 22 | pub mod qaorder; 23 | pub mod qaposition; 24 | pub mod transaction; 25 | pub mod trade_date; 26 | 27 | pub struct QABacktest {} 28 | 29 | impl QABacktest { 30 | fn create() -> Self { 31 | let backtest = QABacktest {}; 32 | backtest 33 | } 34 | 35 | fn init(&mut self) {} 36 | 37 | fn on_bar(&mut self, bar: qafetch::BAR) {} 38 | 39 | fn run(&mut self) {} 40 | 41 | fn day_open(&mut self) {} 42 | 43 | fn day_close(&mut self) {} 44 | 45 | fn on_backtest_close(&mut self) {} 46 | } 47 | 48 | pub fn main() {} 49 | -------------------------------------------------------------------------------- /examples/x1.rs: -------------------------------------------------------------------------------- 1 | use tokio::runtime::Builder; 2 | use tokio::sync::oneshot; 3 | use tokio::task; 4 | use std::rc::Rc; 5 | use std::cell::RefCell; 6 | use quantaxis_rs::qaaccount::QA_Account; 7 | use std::borrow::BorrowMut; 8 | use std::sync::{Arc, Mutex}; 9 | use tokio::runtime; 10 | use std::ops::DerefMut; 11 | 12 | async fn some_computation() -> String { 13 | "represents the result of the computation".to_string() 14 | } 15 | 16 | 17 | #[tokio::main] 18 | async fn main() { 19 | let code = "RB2005".to_string(); 20 | let mut acc = QA_Account::new("RustT01B2_RBL8", "test", "admin", 100000.0, false, "real"); 21 | acc.init_h(&code); 22 | let ac:Arc> = Arc::new(Mutex::new(acc)); 23 | let mut ac1= ac.clone(); 24 | let join = task::spawn(async move { 25 | let mut acc_mut = ac1.lock().unwrap(); 26 | let order = acc_mut.send_order_async(&code, 10.0, "2020-01-20 22:10:00", 2, 3500.0, "BUY_OPEN"); 27 | // ac1.as_ref().borrow_mut().get_mut().unwrap(). 28 | println!("下单完成"); 29 | 30 | }); 31 | let order = join.await.unwrap(); 32 | 33 | //println!("{:#?}", acc.hold); 34 | println!("{:#?}", order); 35 | } -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "quantaxis-rs" 3 | version = "0.3.4" 4 | description = 'quantaxis in rust' 5 | authors = ["yutiansut "] 6 | edition = "2018" 7 | include = [ 8 | "src/**/*", 9 | "Cargo.toml", 10 | "README.md" 11 | ] 12 | readme = "README.md" 13 | categories = ["science", "algorithms"] 14 | homepage = "https://github.com/yutiansut" 15 | repository = "https://github.com/yutiansut/quantaxis-rs" 16 | documentation = "https://docs.rs/quantaxis-rs/" 17 | license = "MIT OR Apache-2.0" 18 | 19 | [dependencies] 20 | serde_json = "1.0.61" 21 | serde_derive = "1.0" 22 | serde = { version = "1.0", features = ["derive"] } # 序列化 23 | csv = "1.1.3" 24 | stopwatch = "0.0.7" 25 | num = "0.2.0" 26 | num-traits = "0.2.6" 27 | rayon = "1.1" # 多线程 28 | ndarray = "0.13.0" # ndarray 29 | ndarray-csv = "0.4" 30 | log = "0.4" 31 | error-chain = "0.12.1" 32 | regex = "1" 33 | uuid = { version = "0.8", features = ["serde", "v1", "v4"] } 34 | float-cmp = "0.4.0" 35 | bencher = "0.1.5" 36 | rand = "0.6.5" 37 | qifi-rs ="0.3.0" 38 | chrono = "0.4.11" 39 | tokio = { version = "0.2.18", features = ["full"] } 40 | tokio-util = { version = "0.3.0", features = ["full"] } 41 | [dev-dependencies] 42 | 43 | 44 | 45 | 46 | [[bench]] 47 | name = "indicators" 48 | path = "benches/indicators.rs" 49 | harness = false 50 | 51 | -------------------------------------------------------------------------------- /src/traits.rs: -------------------------------------------------------------------------------- 1 | // Indicator traits 2 | // 3 | 4 | /// Resets an indicator to the initial state. 5 | pub trait Reset { 6 | fn reset(&mut self); 7 | } 8 | 9 | /// Consumes a data item of type `T` and returns `Output`. 10 | /// 11 | /// Typically `T` can be `f64` or a struct similar to [DataItem](struct.DataItem.html), that implements 12 | /// traits necessary to calculate value of a particular indicator. 13 | /// 14 | /// In most cases `Output` is `f64`, but sometimes it can be different. For example for 15 | /// [MACD](indicators/struct.MovingAverageConvergenceDivergence.html) it is `(f64, f64, f64)` since 16 | /// MACD returns 3 values. 17 | /// 18 | pub trait Next { 19 | type Output; 20 | fn next(&mut self, input: T) -> Self::Output; 21 | } 22 | 23 | pub trait Update { 24 | type Output; 25 | fn update(&mut self, input: T) -> Self::Output; 26 | } 27 | 28 | 29 | /// Open price of a particular period. 30 | pub trait Open { 31 | fn open(&self) -> f64; 32 | } 33 | 34 | /// Close price of a particular period. 35 | pub trait Close { 36 | fn close(&self) -> f64; 37 | } 38 | 39 | /// Lowest price of a particular period. 40 | pub trait Low { 41 | fn low(&self) -> f64; 42 | } 43 | 44 | /// Highest price of a particular period. 45 | pub trait High { 46 | fn high(&self) -> f64; 47 | } 48 | 49 | /// Trading volume of a particular trading period. 50 | pub trait Volume { 51 | fn volume(&self) -> f64; 52 | } 53 | -------------------------------------------------------------------------------- /examples/x2.rs: -------------------------------------------------------------------------------- 1 | use std::borrow::BorrowMut; 2 | use std::cell::RefCell; 3 | use std::rc::Rc; 4 | use std::sync::{Arc, Mutex}; 5 | 6 | use tokio::runtime; 7 | use tokio::runtime::Builder; 8 | use tokio::sync::oneshot; 9 | use tokio::task; 10 | 11 | use quantaxis_rs::qaaccount::QA_Account; 12 | 13 | async fn some_computation() -> String { 14 | "represents the result of the computation".to_string() 15 | } 16 | 17 | 18 | #[tokio::main] 19 | async fn main() { 20 | let code = "RB2005".to_string(); 21 | let mut acc = QA_Account::new("RustT01B2_RBL8", "test", "admin", 100000.0, false, "real"); 22 | let mut acc2 = QA_Account::new("RustT01B2_RBL8", "test", "admin", 100000.0, false, "real"); 23 | let c = code.clone(); 24 | acc.init_h(&code); 25 | acc2.init_h(&code); 26 | let join = task::spawn(async { 27 | let order = acc.send_order_async(&code, 10.0, "2020-01-20 22:10:00", 2, 3500.0, "BUY_OPEN").await; 28 | acc.settle(); 29 | println!("ok2"); 30 | println!("order: {:?}", order); 31 | (acc, code) 32 | }); 33 | let join2 = task::spawn(async { 34 | let order = acc2.send_order_async(&c, 10.0, "2020-01-20 22:10:00", 2, 3500.0, "BUY_OPEN").await; 35 | acc2.settle(); 36 | println!("2"); 37 | println!("order: {:?}", order); 38 | (acc2, c) 39 | }); 40 | 41 | let (acc, code) = join.await.unwrap(); 42 | let (acc2, code2) = join2.await.unwrap(); 43 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # QUANTAXIS-RS 这是你没有见过的QUANTAXIS的全新版本 2 | 3 | 本项目已经迁移至 QUANTAXIS 主项目中 : github.com/quantaxis/quantaxis 4 | 5 | ![Rust](https://github.com/yutiansut/quantaxis-rs/workflows/Rust/badge.svg) 6 | 7 | ## 特性 8 | - 完整的quantaxis Account/Data/Indicator功能 支持快速的回测 9 | - 单票分钟线2年回测在500ms 10 | - 单指标计算在70ns 11 | - 完整的测试工具,对于每个函数都有完整对应的测试 12 | - 完整的benchmark工具链 13 | 14 | ## 兼容性 15 | 16 | - 兼容python版本的QUANTAXIS回测 17 | - 兼容基于QIFI协议的所有项目 18 | 19 | 20 | power by yutiansut/somewheve 21 | 22 | 2020 23 | 24 | 25 | ### update MARKET_PRESET 26 | ```python 27 | 28 | import QUANTAXIS as QA 29 | 30 | 31 | mp = QA.QAARP.MARKET_PRESET() 32 | for k,i in mp.table.items(): 33 | print(f"""market_preset.insert( 34 | "{k}".to_string(), 35 | CodePreset {{ 36 | name: "{i['name']}".to_string(), 37 | unit_table: {int(i['unit_table'])}, 38 | price_tick: {float(i['price_tick'])}, 39 | buy_frozen_coeff: {i['buy_frozen_coeff']}, 40 | sell_frozen_coeff:{i['sell_frozen_coeff']}, 41 | exchange: "{i['exchange']}".to_string(), 42 | commission_coeff_peramount: {float(i['commission_coeff_peramount'])}, 43 | commission_coeff_pervol:{float(i['commission_coeff_pervol'])}, 44 | commission_coeff_today_peramount: {float(i['commission_coeff_today_peramount'])}, 45 | commission_coeff_today_pervol: {float(i['commission_coeff_today_pervol'])}, 46 | }}, 47 | );""") 48 | 49 | ``` 50 | -------------------------------------------------------------------------------- /src/indicators/mod.rs: -------------------------------------------------------------------------------- 1 | mod exponential_moving_average; 2 | pub use self::exponential_moving_average::ExponentialMovingAverage; 3 | 4 | mod simple_moving_average; 5 | pub use self::simple_moving_average::SimpleMovingAverage; 6 | 7 | mod moving_average; 8 | pub use self::moving_average::MovingAverage; 9 | 10 | mod standard_deviation; 11 | pub use self::standard_deviation::StandardDeviation; 12 | 13 | mod relative_strength_index; 14 | pub use self::relative_strength_index::RelativeStrengthIndex; 15 | 16 | mod minimum; 17 | pub use self::minimum::Minimum; 18 | 19 | mod maximum; 20 | pub use self::maximum::Maximum; 21 | 22 | mod fast_stochastic; 23 | pub use self::fast_stochastic::FastStochastic; 24 | 25 | mod slow_stochastic; 26 | pub use self::slow_stochastic::SlowStochastic; 27 | 28 | mod true_range; 29 | pub use self::true_range::TrueRange; 30 | 31 | mod average_true_range; 32 | pub use self::average_true_range::AverageTrueRange; 33 | 34 | mod moving_average_convergence_divergence; 35 | pub use self::moving_average_convergence_divergence::MovingAverageConvergenceDivergence; 36 | 37 | mod efficiency_ratio; 38 | pub use self::efficiency_ratio::EfficiencyRatio; 39 | 40 | mod bollinger_bands; 41 | pub use self::bollinger_bands::{BollingerBands, BollingerBandsOutput}; 42 | 43 | mod rate_of_change; 44 | pub use self::rate_of_change::RateOfChange; 45 | 46 | mod money_flow_index; 47 | pub use self::money_flow_index::MoneyFlowIndex; 48 | 49 | mod on_balance_volume; 50 | pub use self::on_balance_volume::OnBalanceVolume; 51 | 52 | 53 | mod hhv; 54 | pub use self::hhv::HHV; 55 | 56 | mod llv; 57 | pub use self::llv::LLV; -------------------------------------------------------------------------------- /benches/indicators.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate bencher; 3 | extern crate quantaxis_rs; 4 | 5 | use bencher::Bencher; 6 | use rand::Rng; 7 | use quantaxis_rs::indicators::{ 8 | BollingerBands, EfficiencyRatio, ExponentialMovingAverage, FastStochastic, Maximum, Minimum, 9 | MoneyFlowIndex, MovingAverageConvergenceDivergence, OnBalanceVolume, RateOfChange, 10 | RelativeStrengthIndex, SimpleMovingAverage, SlowStochastic, StandardDeviation, TrueRange, 11 | }; 12 | use quantaxis_rs::DataItem; 13 | use quantaxis_rs::Next; 14 | 15 | const ITEMS_COUNT: usize = 5_000; 16 | 17 | fn rand_data_item() -> DataItem { 18 | let mut rng = rand::thread_rng(); 19 | 20 | let low = rng.gen_range(0.0, 500.0); 21 | let high = rng.gen_range(500.0, 1000.0); 22 | let open = rng.gen_range(low, high); 23 | let close = rng.gen_range(low, high); 24 | let volume = rng.gen_range(0.0, 10_000.0); 25 | 26 | DataItem::builder() 27 | .open(open) 28 | .high(high) 29 | .low(low) 30 | .close(close) 31 | .volume(volume) 32 | .build() 33 | .unwrap() 34 | } 35 | 36 | macro_rules! bench_indicators { 37 | ($($indicator:ident), *) => { 38 | $( 39 | fn $indicator(bench: &mut Bencher) { 40 | let items: Vec = (0..ITEMS_COUNT).map( |_| rand_data_item() ).collect(); 41 | let mut indicator = $indicator::default(); 42 | 43 | bench.iter(|| { 44 | for item in items.iter() { 45 | indicator.next(item); 46 | 47 | } 48 | }) 49 | } 50 | )* 51 | 52 | benchmark_group!(benches, $($indicator,)*); 53 | benchmark_main!(benches); 54 | } 55 | } 56 | 57 | bench_indicators!( 58 | SimpleMovingAverage, 59 | ExponentialMovingAverage, 60 | StandardDeviation, 61 | BollingerBands, 62 | EfficiencyRatio, 63 | FastStochastic, 64 | Maximum, 65 | Minimum, 66 | MovingAverageConvergenceDivergence, 67 | RateOfChange, 68 | RelativeStrengthIndex, 69 | SlowStochastic, 70 | TrueRange, 71 | MoneyFlowIndex, 72 | OnBalanceVolume 73 | ); 74 | 75 | -------------------------------------------------------------------------------- /examples/qadata.rs: -------------------------------------------------------------------------------- 1 | extern crate num_traits; 2 | #[macro_use] 3 | extern crate serde; 4 | 5 | use core::fmt::Debug; 6 | use core::ops::AddAssign; 7 | 8 | use num_traits::{cast::FromPrimitive, float::Float, identities::One, identities::Zero}; 9 | use num_traits::real::Real; 10 | use serde::{Deserialize, Serialize}; 11 | 12 | #[derive(Clone, Debug, Serialize, Deserialize)] 13 | pub struct Data { 14 | pub min: T, 15 | pub max: T, 16 | pub mean: T, 17 | 18 | /// 19 | pub std_dev: T, 20 | 21 | /// count 序列号 22 | #[serde(skip)] 23 | count: usize, 24 | 25 | /// Internal mean squared for algo 26 | #[serde(skip)] 27 | mean2: T, 28 | } 29 | 30 | impl Data 31 | where 32 | T: Float + Zero + One + AddAssign + FromPrimitive + PartialEq + Debug, 33 | { 34 | pub fn new() -> Data { 35 | Data { 36 | count: 0, 37 | min: T::zero(), 38 | max: T::zero(), 39 | mean: T::zero(), 40 | std_dev: T::zero(), 41 | mean2: T::zero(), 42 | } 43 | } 44 | 45 | pub fn update(&mut self, value: T) { 46 | // Track min and max 47 | if value > self.max || self.count == 0 { 48 | self.max = value; 49 | } 50 | if value < self.min || self.count == 0 { 51 | self.min = value; 52 | } 53 | 54 | self.count += 1; 55 | let count = T::from_usize(self.count).unwrap(); 56 | 57 | // Calculate mean 58 | let delta: T = value - self.mean; 59 | self.mean += delta / count; 60 | 61 | // Mean2 used internally for standard deviation calculation 62 | let delta2: T = value - self.mean; 63 | self.mean2 += delta * delta2; 64 | 65 | // Calculate standard deviation 66 | if self.count > 1 { 67 | self.std_dev = (self.mean2 / (count - T::one())).sqrt(); 68 | } 69 | } 70 | } 71 | 72 | fn main() { 73 | let mut s: Data = Data::new(); 74 | 75 | let vals: Vec = vec![1.0, 2.0, 3.0, 4.0, 5.0]; 76 | for v in &vals { 77 | s.update(*v); 78 | println!("update new vals {}", v); 79 | println!("max {:?}", s.max); 80 | println!("mean {:?}", s.mean); 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/qadata.rs: -------------------------------------------------------------------------------- 1 | use core::fmt::Debug; 2 | use core::ops::AddAssign; 3 | 4 | use ndarray::array; 5 | use num_traits::{cast::FromPrimitive, float::Float, identities::One, identities::Zero}; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | //use num_traits::real::Real; 9 | 10 | #[derive(Clone, Debug, Serialize, Deserialize)] 11 | pub struct Stats { 12 | pub min: T, 13 | pub max: T, 14 | /// Mean of sample set 15 | pub mean: T, 16 | /// Standard deviation of sample 17 | pub std_dev: T, 18 | 19 | /// Number of values collected 20 | #[serde(skip)] 21 | count: usize, 22 | 23 | /// Internal mean squared for algo 24 | #[serde(skip)] 25 | mean2: T, 26 | } 27 | 28 | impl Stats 29 | where 30 | T: Float + Zero + One + AddAssign + FromPrimitive + PartialEq + Debug, 31 | { 32 | /// Create a new rolling-stats object 33 | pub fn new() -> Stats { 34 | Stats { 35 | count: 0, 36 | min: T::zero(), 37 | max: T::zero(), 38 | mean: T::zero(), 39 | std_dev: T::zero(), 40 | mean2: T::zero(), 41 | } 42 | } 43 | 44 | /// Update the rolling-stats object 45 | pub fn update(&mut self, value: T) { 46 | // Track min and max 47 | if value > self.max || self.count == 0 { 48 | self.max = value; 49 | } 50 | if value < self.min || self.count == 0 { 51 | self.min = value; 52 | } 53 | 54 | // Increment counter 55 | self.count += 1; 56 | let count = T::from_usize(self.count).unwrap(); 57 | 58 | // Calculate mean 59 | let delta: T = value - self.mean; 60 | self.mean += delta / count; 61 | 62 | // Mean2 used internally for standard deviation calculation 63 | let delta2: T = value - self.mean; 64 | self.mean2 += delta * delta2; 65 | 66 | // Calculate standard deviation 67 | if self.count > 1 { 68 | self.std_dev = (self.mean2 / (count - T::one())).sqrt(); 69 | } 70 | } 71 | } 72 | 73 | fn main() { 74 | let mut s: Stats = Stats::new(); 75 | 76 | let vals: Vec = vec![1.0, 2.0, 3.0, 4.0, 5.0]; 77 | for v in &vals { 78 | s.update(*v); 79 | 80 | println!("{:?}", s.max); 81 | println!("{:?}", s.mean); 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/test_helper.rs: -------------------------------------------------------------------------------- 1 | use super::{Close, High, Low, Open, Volume}; 2 | 3 | #[derive(Debug, PartialEq)] 4 | pub struct Bar { 5 | open: f64, 6 | high: f64, 7 | low: f64, 8 | close: f64, 9 | volume: f64, 10 | } 11 | 12 | impl Bar { 13 | pub fn new() -> Self { 14 | Self { 15 | open: 0.0, 16 | close: 0.0, 17 | low: 0.0, 18 | high: 0.0, 19 | volume: 0.0, 20 | } 21 | } 22 | 23 | // pub fn open>(mut self, val :T ) -> Self { 24 | // self.open = val.into(); 25 | // self 26 | // } 27 | 28 | pub fn high>(mut self, val: T) -> Self { 29 | self.high = val.into(); 30 | self 31 | } 32 | 33 | pub fn low>(mut self, val: T) -> Self { 34 | self.low = val.into(); 35 | self 36 | } 37 | 38 | pub fn close>(mut self, val: T) -> Self { 39 | self.close = val.into(); 40 | self 41 | } 42 | 43 | pub fn volume(mut self, val: f64) -> Self { 44 | self.volume = val; 45 | self 46 | } 47 | } 48 | 49 | impl Open for Bar { 50 | fn open(&self) -> f64 { 51 | self.open 52 | } 53 | } 54 | 55 | impl Close for Bar { 56 | fn close(&self) -> f64 { 57 | self.close 58 | } 59 | } 60 | 61 | impl Low for Bar { 62 | fn low(&self) -> f64 { 63 | self.low 64 | } 65 | } 66 | 67 | impl High for Bar { 68 | fn high(&self) -> f64 { 69 | self.high 70 | } 71 | } 72 | 73 | impl Volume for Bar { 74 | fn volume(&self) -> f64 { 75 | self.volume 76 | } 77 | } 78 | 79 | pub fn round(num: f64) -> f64 { 80 | (num * 1000.0).round() / 1000.00 81 | } 82 | 83 | #[macro_export] 84 | macro_rules! test_indicator { 85 | ($i:tt) => { 86 | #[test] 87 | fn test_indicator() { 88 | let bar = Bar::new(); 89 | 90 | // ensure Default trait is implemented 91 | let mut indicator = $i::default(); 92 | 93 | // ensure Next is implemented 94 | let first_output = indicator.next(12.3); 95 | 96 | // ensure next accepts &DataItem as well 97 | indicator.next(&bar); 98 | 99 | // ensure Reset is implemented and works correctly 100 | indicator.reset(); 101 | assert_eq!(indicator.next(12.3), first_output); 102 | 103 | // ensure Display is implemented 104 | format!("{}", indicator); 105 | } 106 | }; 107 | } 108 | -------------------------------------------------------------------------------- /src/transaction.rs: -------------------------------------------------------------------------------- 1 | use chrono::{DateTime, Offset, TimeZone, Utc}; 2 | use qifi_rs::Trade; 3 | use serde::{Deserialize, Serialize}; 4 | use serde_json::to_string; 5 | 6 | #[derive(Debug, Clone, Deserialize, Serialize)] 7 | pub struct QATransaction { 8 | pub code: String, 9 | pub amount: f64, 10 | pub price: f64, 11 | pub datetime: String, 12 | pub order_id: String, 13 | pub trade_id: String, 14 | pub realorder_id: String, 15 | pub account_cookie: String, 16 | pub commission: f64, 17 | pub tax: f64, 18 | pub message: String, 19 | pub frozen: f64, 20 | pub direction: i32, 21 | } 22 | 23 | 24 | impl QATransaction { 25 | pub fn to_json(&self) -> String { 26 | let jdata = serde_json::to_string(&self).unwrap(); 27 | jdata 28 | } 29 | pub fn get_direction_or_offset(&mut self, towards: i32) -> (String, String) { 30 | let rt = match towards { 31 | 1 => (String::from("BUY"), String::from("OPEN")), 32 | 2 => (String::from("BUY"), String::from("OPEN")), 33 | 3 => (String::from("BUY"), String::from("CLOSE")), 34 | 4 => (String::from("BUY"), String::from("CLOSETODAY")), 35 | -1 => (String::from("SELL"), String::from("CLOSE")), 36 | -2 => (String::from("SELL"), String::from("OPEN")), 37 | -3 => (String::from("SELL"), String::from("CLOSE")), 38 | -4 => (String::from("SELL"), String::from("CLOSETODAY")), 39 | _ => (String::from(""), String::from("")), 40 | }; 41 | rt 42 | } 43 | 44 | 45 | pub fn to_qifitrade(&mut self) -> Trade { 46 | let (direction, offset) = self.get_direction_or_offset(self.direction); 47 | let td = Utc 48 | .datetime_from_str(self.datetime.as_ref(), "%Y-%m-%d %H:%M:%S") 49 | .unwrap() 50 | .timestamp_nanos() 51 | - 28800000000000; 52 | 53 | Trade { 54 | seqno: 0, 55 | user_id: self.account_cookie.clone(), 56 | trade_id: self.trade_id.clone(), 57 | exchange_id: "".to_string(), 58 | instrument_id: self.code.clone(), 59 | order_id: self.order_id.clone(), 60 | exchange_trade_id: self.realorder_id.clone(), 61 | direction, 62 | offset, 63 | volume: self.amount.clone(), 64 | price: self.price.clone(), 65 | trade_date_time: td, 66 | commission: self.commission.clone(), 67 | } 68 | } 69 | } 70 | 71 | #[cfg(test)] 72 | mod tests { 73 | use super::*; 74 | 75 | #[test] 76 | fn test_to_qifi() { 77 | let mut transaction = QATransaction { 78 | code: "".to_string(), 79 | amount: 0.0, 80 | price: 0.0, 81 | datetime: "2020-01-02 00:00:00".to_string(), 82 | order_id: "".to_string(), 83 | trade_id: "".to_string(), 84 | realorder_id: "".to_string(), 85 | account_cookie: "".to_string(), 86 | commission: 0.0, 87 | tax: 0.0, 88 | message: "".to_string(), 89 | frozen: 0.0, 90 | direction: 0, 91 | }; 92 | transaction.to_qifitrade(); 93 | } 94 | } 95 | 96 | -------------------------------------------------------------------------------- /src/qaorder.rs: -------------------------------------------------------------------------------- 1 | use uuid::Uuid; 2 | use serde::{Serialize, Deserialize}; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct QAOrder { 6 | pub account_cookie: String, 7 | pub user_id: String, 8 | pub instrument_id: String, 9 | pub towards: i32, 10 | pub exchange_id: String, 11 | pub order_time: String, 12 | pub volume: f64, 13 | pub price: f64, 14 | pub order_id: String, 15 | pub seqno: String, 16 | pub direction: String, 17 | pub offset: String, 18 | pub volume_orign: f64, 19 | pub price_type: String, 20 | pub limit_price: f64, 21 | pub time_condition: String, 22 | pub volume_condition: String, 23 | pub insert_date_time: String, 24 | pub exchange_order_id: String, 25 | pub status: i32, 26 | pub volume_left: f64, 27 | pub last_msg: String, 28 | } 29 | 30 | impl QAOrder { 31 | pub fn new( 32 | account: String, 33 | code: String, 34 | towards: i32, 35 | exchange_id: String, 36 | order_time: String, 37 | volume: f64, 38 | price: f64, 39 | order_id: String, 40 | ) -> Self { 41 | let mut direction = "BUY".to_string(); 42 | let mut offset = "OPEN".to_string(); 43 | 44 | match towards { 45 | 1 | 2 => {} 46 | -1 => { 47 | direction = "SELL".to_string(); 48 | } 49 | -2 => { 50 | direction = "SELL".to_string(); 51 | } 52 | 3 => { 53 | offset = "CLOSE".to_string(); 54 | } 55 | -3 => { 56 | direction = "SELL".to_string(); 57 | offset = "CLOSE".to_string(); 58 | } 59 | _ => {} 60 | } 61 | 62 | Self { 63 | account_cookie: account.clone(), 64 | user_id: account.clone(), 65 | instrument_id: code.clone(), 66 | towards, 67 | exchange_id, 68 | order_time, 69 | volume, 70 | price, 71 | order_id, 72 | seqno: "".to_string(), 73 | direction, 74 | offset, 75 | volume_orign: 0.0, 76 | price_type: "LIMIT".to_string(), 77 | limit_price: price, 78 | time_condition: "AND".to_string(), 79 | volume_condition: "GFD".to_string(), 80 | insert_date_time: "".to_string(), 81 | exchange_order_id: Uuid::new_v4().to_string(), 82 | status: 100, 83 | volume_left: volume, 84 | last_msg: "".to_string(), 85 | } 86 | } 87 | 88 | pub fn to_trade_order(&self) -> TradeOrder { 89 | TradeOrder{ 90 | aid: "insert_order".to_string(), 91 | user_id: self.account_cookie.clone(), 92 | order_id: self.order_id.clone(), 93 | exchange_id: self.exchange_id.clone(), 94 | instrument_id: self.instrument_id.clone(), 95 | direction: self.direction.clone(), 96 | offset: self.offset.clone(), 97 | volume: self.volume as i64, 98 | price_type: self.price_type.clone(), 99 | limit_price: self.price, 100 | volume_condition: self.volume_condition.clone(), 101 | time_condition: self.time_condition.clone() 102 | } 103 | } 104 | } 105 | 106 | 107 | #[derive(Serialize, Deserialize, Debug)] 108 | pub struct TradeOrder { 109 | pub aid: String, 110 | pub user_id: String, 111 | pub order_id: String, 112 | pub exchange_id: String, 113 | pub instrument_id: String, 114 | pub direction: String, 115 | pub offset: String, 116 | pub volume: i64, 117 | pub price_type: String, 118 | pub limit_price: f64, 119 | pub volume_condition: String, 120 | pub time_condition: String, 121 | } -------------------------------------------------------------------------------- /src/indicators/on_balance_volume.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::{Close, Next, Reset, Volume}; 4 | 5 | /// On Balance Volume (OBV). 6 | /// 7 | /// The OBV is an volume and price based oscillator which gives cumulative total volumes. 8 | /// OBV measures buying and selling pressure as a cumulative indicator, 9 | /// adding volume on up days and subtracting it on down days. 10 | /// 11 | /// # Formula 12 | /// 13 | /// If the closing price is above the prior close price then: 14 | /// Current OBV = Previous OBV + Current Volume 15 | /// 16 | /// If the closing price is below the prior close price then: 17 | /// Current OBV = Previous OBV - Current Volume 18 | /// 19 | /// If the closing prices equals the prior close price then: 20 | /// Current OBV = Previous OBV 21 | /// 22 | /// Where: 23 | /// 24 | /// obv - on the balance volume 25 | /// 26 | /// # Example 27 | /// 28 | /// ``` 29 | /// use quantaxis_rs::indicators::OnBalanceVolume; 30 | /// use quantaxis_rs::{Next, DataItem}; 31 | /// 32 | /// let mut obv = OnBalanceVolume::new(); 33 | /// 34 | /// let di1 = DataItem::builder() 35 | /// .high(3.0) 36 | /// .low(1.0) 37 | /// .close(2.0) 38 | /// .open(1.5) 39 | /// .volume(1000.0) 40 | /// .build().unwrap(); 41 | /// 42 | /// let di2 = DataItem::builder() 43 | /// .high(3.0) 44 | /// .low(1.0) 45 | /// .close(1.5) 46 | /// .open(1.5) 47 | /// .volume(300.0) 48 | /// .build().unwrap(); 49 | /// 50 | /// assert_eq!(obv.next(&di1), 1000.0); 51 | /// assert_eq!(obv.next(&di2), 700.0); 52 | /// ``` 53 | /// 54 | /// # Links 55 | /// 56 | /// * [On Balance Volume, Wikipedia](https://en.wikipedia.org/wiki/On-balance_volume) 57 | /// * [On Balance Volume, stockcharts](https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:on_balance_volume_obv) 58 | 59 | #[derive(Debug, Clone)] 60 | pub struct OnBalanceVolume { 61 | obv: f64, 62 | prev_close: f64, 63 | } 64 | 65 | impl OnBalanceVolume { 66 | pub fn new() -> Self { 67 | Self { 68 | obv: 0.0, 69 | prev_close: 0.0, 70 | } 71 | } 72 | } 73 | 74 | impl<'a, T: Close + Volume> Next<&'a T> for OnBalanceVolume { 75 | type Output = f64; 76 | 77 | fn next(&mut self, input: &'a T) -> f64 { 78 | if input.close() > self.prev_close { 79 | self.obv = self.obv + input.volume(); 80 | } else if input.close() < self.prev_close { 81 | self.obv = self.obv - input.volume(); 82 | } 83 | self.prev_close = input.close(); 84 | self.obv 85 | } 86 | } 87 | 88 | impl Default for OnBalanceVolume { 89 | fn default() -> Self { 90 | Self::new() 91 | } 92 | } 93 | 94 | impl fmt::Display for OnBalanceVolume { 95 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 96 | write!(f, "OBV") 97 | } 98 | } 99 | 100 | impl Reset for OnBalanceVolume { 101 | fn reset(&mut self) { 102 | self.obv = 0.0; 103 | self.prev_close = 0.0; 104 | } 105 | } 106 | 107 | #[cfg(test)] 108 | mod tests { 109 | use super::*; 110 | use crate::test_helper::*; 111 | 112 | #[test] 113 | fn test_next_bar() { 114 | let mut obv = OnBalanceVolume::new(); 115 | 116 | let bar1 = Bar::new().close(1.5).volume(1000.0); 117 | let bar2 = Bar::new().close(5).volume(5000.0); 118 | let bar3 = Bar::new().close(4).volume(9000.0); 119 | let bar4 = Bar::new().close(4).volume(4000.0); 120 | 121 | assert_eq!(obv.next(&bar1), 1000.0); 122 | 123 | //close > prev_close 124 | assert_eq!(obv.next(&bar2), 6000.0); 125 | 126 | // close < prev_close 127 | assert_eq!(obv.next(&bar3), -3000.0); 128 | 129 | // close == prev_close 130 | assert_eq!(obv.next(&bar4), -3000.0); 131 | } 132 | 133 | #[test] 134 | fn test_reset() { 135 | let mut obv = OnBalanceVolume::new(); 136 | 137 | let bar1 = Bar::new().close(1.5).volume(1000.0); 138 | let bar2 = Bar::new().close(4).volume(2000.0); 139 | let bar3 = Bar::new().close(8).volume(3000.0); 140 | 141 | assert_eq!(obv.next(&bar1), 1000.0); 142 | assert_eq!(obv.next(&bar2), 3000.0); 143 | assert_eq!(obv.next(&bar3), 6000.0); 144 | 145 | obv.reset(); 146 | 147 | assert_eq!(obv.next(&bar1), 1000.0); 148 | assert_eq!(obv.next(&bar2), 3000.0); 149 | assert_eq!(obv.next(&bar3), 6000.0); 150 | } 151 | 152 | #[test] 153 | fn test_default() { 154 | OnBalanceVolume::default(); 155 | } 156 | 157 | #[test] 158 | fn test_display() { 159 | let obv = OnBalanceVolume::new(); 160 | assert_eq!(format!("{}", obv), "OBV"); 161 | } 162 | 163 | } 164 | -------------------------------------------------------------------------------- /src/data_item.rs: -------------------------------------------------------------------------------- 1 | use crate::errors::*; 2 | use crate::traits::{Close, High, Low, Open, Volume}; 3 | 4 | #[derive(Debug, Clone)] 5 | pub struct DataItem { 6 | open: f64, 7 | high: f64, 8 | low: f64, 9 | close: f64, 10 | volume: f64, 11 | } 12 | 13 | impl DataItem { 14 | pub fn builder() -> DataItemBuilder { 15 | DataItemBuilder::new() 16 | } 17 | } 18 | 19 | impl Open for DataItem { 20 | fn open(&self) -> f64 { 21 | self.open 22 | } 23 | } 24 | 25 | impl High for DataItem { 26 | fn high(&self) -> f64 { 27 | self.high 28 | } 29 | } 30 | 31 | impl Low for DataItem { 32 | fn low(&self) -> f64 { 33 | self.low 34 | } 35 | } 36 | 37 | impl Close for DataItem { 38 | fn close(&self) -> f64 { 39 | self.close 40 | } 41 | } 42 | 43 | impl Volume for DataItem { 44 | fn volume(&self) -> f64 { 45 | self.volume 46 | } 47 | } 48 | 49 | pub struct DataItemBuilder { 50 | open: Option, 51 | high: Option, 52 | low: Option, 53 | close: Option, 54 | volume: Option, 55 | } 56 | 57 | impl DataItemBuilder { 58 | pub fn new() -> Self { 59 | Self { 60 | open: None, 61 | high: None, 62 | low: None, 63 | close: None, 64 | volume: None, 65 | } 66 | } 67 | 68 | pub fn open(mut self, val: f64) -> Self { 69 | self.open = Some(val); 70 | self 71 | } 72 | 73 | pub fn high(mut self, val: f64) -> Self { 74 | self.high = Some(val); 75 | self 76 | } 77 | 78 | pub fn low(mut self, val: f64) -> Self { 79 | self.low = Some(val); 80 | self 81 | } 82 | 83 | pub fn close(mut self, val: f64) -> Self { 84 | self.close = Some(val); 85 | self 86 | } 87 | 88 | pub fn volume(mut self, val: f64) -> Self { 89 | self.volume = Some(val); 90 | self 91 | } 92 | 93 | pub fn build(self) -> Result { 94 | if let (Some(open), Some(high), Some(low), Some(close), Some(volume)) = 95 | (self.open, self.high, self.low, self.close, self.volume) 96 | { 97 | // validate 98 | if low <= open 99 | && low <= close 100 | && low <= high 101 | && high >= open 102 | && high >= close 103 | && volume >= 0.0 104 | && low >= 0.0 105 | { 106 | let item = DataItem { 107 | open, 108 | high, 109 | low, 110 | close, 111 | volume, 112 | }; 113 | Ok(item) 114 | } else { 115 | Err(Error::from_kind(ErrorKind::DataItemInvalid)) 116 | } 117 | } else { 118 | Err(Error::from_kind(ErrorKind::DataItemIncomplete)) 119 | } 120 | } 121 | } 122 | 123 | #[cfg(test)] 124 | mod tests { 125 | use super::*; 126 | 127 | #[test] 128 | fn test_builder() { 129 | fn assert_valid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) { 130 | let result = DataItem::builder() 131 | .open(open) 132 | .high(high) 133 | .low(low) 134 | .close(close) 135 | .volume(volume) 136 | .build(); 137 | 138 | assert!(result.is_ok()); 139 | println!("{:#?}", result.unwrap()); 140 | } 141 | 142 | fn assert_invalid((open, high, low, close, volume): (f64, f64, f64, f64, f64)) { 143 | let result = DataItem::builder() 144 | .open(open) 145 | .high(high) 146 | .low(low) 147 | .close(close) 148 | .volume(volume) 149 | .build(); 150 | assert!(result.is_err()); 151 | } 152 | 153 | let valid_records = vec![ 154 | // open, high, low , close, volume 155 | (20.0, 25.0, 15.0, 21.0, 7500.0), 156 | (10.0, 10.0, 10.0, 10.0, 10.0), 157 | (0.0, 0.0, 0.0, 0.0, 0.0), 158 | ]; 159 | for record in valid_records { 160 | assert_valid(record) 161 | } 162 | 163 | let invalid_records = vec![ 164 | // open, high, low , close, volume 165 | (-1.0, 25.0, 15.0, 21.0, 7500.0), 166 | (20.0, -1.0, 15.0, 21.0, 7500.0), 167 | (20.0, 25.0, -1.0, 21.0, 7500.0), 168 | (20.0, 25.0, 15.0, -1.0, 7500.0), 169 | (20.0, 25.0, 15.0, 21.0, -1.0), 170 | (14.9, 25.0, 15.0, 21.0, 7500.0), 171 | (25.1, 25.0, 15.0, 21.0, 7500.0), 172 | (20.0, 25.0, 15.0, 14.9, 7500.0), 173 | (20.0, 25.0, 15.0, 25.1, 7500.0), 174 | (20.0, 15.0, 25.0, 21.0, 7500.0), 175 | ]; 176 | for record in invalid_records { 177 | assert_invalid(record) 178 | } 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/indicators/efficiency_ratio.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::fmt; 3 | 4 | use crate::errors::*; 5 | use crate::traits::{Close, Next, Reset}; 6 | 7 | /// Kaufman's Efficiency Ratio (ER). 8 | /// 9 | /// It is calculated by dividing the price change over a period by the absolute sum of the price movements that occurred to achieve that change. 10 | /// The resulting ratio ranges between 0.0 and 1.0 with higher values representing a more efficient or trending market. 11 | /// 12 | /// # Parameters 13 | /// 14 | /// * _length_ - number of periods (integer greater than 0) 15 | /// 16 | /// # Example 17 | /// 18 | /// ``` 19 | /// use quantaxis_rs::indicators::EfficiencyRatio; 20 | /// use quantaxis_rs::Next; 21 | /// 22 | /// let mut er = EfficiencyRatio::new(4).unwrap(); 23 | /// assert_eq!(er.next(10.0), 1.0); 24 | /// assert_eq!(er.next(13.0), 1.0); 25 | /// assert_eq!(er.next(12.0), 0.5); 26 | /// assert_eq!(er.next(13.0), 0.6); 27 | /// assert_eq!(er.next(18.0), 0.8); 28 | /// assert_eq!(er.next(19.0), 0.75); 29 | /// ``` 30 | 31 | pub struct EfficiencyRatio { 32 | length: u32, 33 | prices: VecDeque, 34 | } 35 | 36 | impl EfficiencyRatio { 37 | pub fn new(length: u32) -> Result { 38 | if length == 0 { 39 | Err(Error::from_kind(ErrorKind::InvalidParameter)) 40 | } else { 41 | let indicator = Self { 42 | length: length, 43 | prices: VecDeque::with_capacity(length as usize + 1), 44 | }; 45 | Ok(indicator) 46 | } 47 | } 48 | } 49 | 50 | impl Next for EfficiencyRatio { 51 | type Output = f64; 52 | 53 | fn next(&mut self, input: f64) -> f64 { 54 | self.prices.push_back(input); 55 | 56 | if self.prices.len() <= 2 { 57 | return 1.0; 58 | } 59 | 60 | let first = self.prices[0]; 61 | 62 | // Calculate volatility 63 | let volatility = self 64 | .prices 65 | .iter() 66 | .skip(1) 67 | .fold((first, 0.0), |(prev, sum), &val| { 68 | (val, sum + (prev - val).abs()) 69 | }) 70 | .1; 71 | 72 | // Calculate direction 73 | let last_index = self.prices.len() - 1; 74 | let direction = (first - self.prices[last_index]).abs(); 75 | 76 | // Get rid of the first element 77 | if self.prices.len() > (self.length as usize) { 78 | self.prices.pop_front(); 79 | } 80 | 81 | // Return actual efficiency ratio 82 | direction / volatility 83 | } 84 | } 85 | 86 | impl<'a, T: Close> Next<&'a T> for EfficiencyRatio { 87 | type Output = f64; 88 | 89 | fn next(&mut self, input: &'a T) -> f64 { 90 | self.next(input.close()) 91 | } 92 | } 93 | 94 | impl Reset for EfficiencyRatio { 95 | fn reset(&mut self) { 96 | self.prices.clear(); 97 | } 98 | } 99 | 100 | impl Default for EfficiencyRatio { 101 | fn default() -> Self { 102 | Self::new(14).unwrap() 103 | } 104 | } 105 | 106 | impl fmt::Display for EfficiencyRatio { 107 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 108 | write!(f, "ER({})", self.length) 109 | } 110 | } 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | use super::*; 115 | use crate::test_helper::*; 116 | macro_rules! test_indicator { 117 | ($i:tt) => { 118 | #[test] 119 | fn test_indicator() { 120 | let bar = Bar::new(); 121 | 122 | // ensure Default trait is implemented 123 | let mut indicator = $i::default(); 124 | 125 | // ensure Next is implemented 126 | let first_output = indicator.next(12.3); 127 | 128 | // ensure next accepts &DataItem as well 129 | indicator.next(&bar); 130 | 131 | // ensure Reset is implemented and works correctly 132 | indicator.reset(); 133 | assert_eq!(indicator.next(12.3), first_output); 134 | 135 | // ensure Display is implemented 136 | format!("{}", indicator); 137 | } 138 | }; 139 | } 140 | test_indicator!(EfficiencyRatio); 141 | 142 | #[test] 143 | fn test_new() { 144 | assert!(EfficiencyRatio::new(0).is_err()); 145 | assert!(EfficiencyRatio::new(1).is_ok()); 146 | } 147 | 148 | #[test] 149 | fn test_next_f64() { 150 | let mut er = EfficiencyRatio::new(3).unwrap(); 151 | 152 | assert_eq!(round(er.next(3.0)), 1.0); 153 | assert_eq!(round(er.next(5.0)), 1.0); 154 | assert_eq!(round(er.next(2.0)), 0.2); 155 | assert_eq!(round(er.next(3.0)), 0.0); 156 | assert_eq!(round(er.next(1.0)), 0.667); 157 | assert_eq!(round(er.next(3.0)), 0.2); 158 | assert_eq!(round(er.next(4.0)), 0.2); 159 | assert_eq!(round(er.next(6.0)), 1.0); 160 | 161 | er.reset(); 162 | assert_eq!(round(er.next(3.0)), 1.0); 163 | assert_eq!(round(er.next(5.0)), 1.0); 164 | assert_eq!(round(er.next(2.0)), 0.2); 165 | assert_eq!(round(er.next(3.0)), 0.0); 166 | } 167 | 168 | #[test] 169 | fn test_display() { 170 | let er = EfficiencyRatio::new(17).unwrap(); 171 | assert_eq!(format!("{}", er), "ER(17)"); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/indicators/minimum.rs: -------------------------------------------------------------------------------- 1 | use std::f64::INFINITY; 2 | use std::fmt; 3 | 4 | use crate::errors::*; 5 | use crate::{Low, Next, Reset}; 6 | 7 | /// Returns the lowest value in a given time frame. 8 | /// 9 | /// # Parameters 10 | /// 11 | /// * _n_ - size of the time frame (integer greater than 0). Default value is 14. 12 | /// 13 | /// # Example 14 | /// 15 | /// ``` 16 | /// use quantaxis_rs::indicators::Minimum; 17 | /// use quantaxis_rs::Next; 18 | /// 19 | /// let mut min = Minimum::new(3).unwrap(); 20 | /// assert_eq!(min.next(10.0), 10.0); 21 | /// assert_eq!(min.next(11.0), 10.0); 22 | /// assert_eq!(min.next(12.0), 10.0); 23 | /// assert_eq!(min.next(13.0), 11.0); 24 | /// ``` 25 | #[derive(Debug, Clone)] 26 | pub struct Minimum { 27 | n: usize, 28 | vec: Vec, 29 | min_index: usize, 30 | cur_index: usize, 31 | } 32 | 33 | impl Minimum { 34 | pub fn new(n: u32) -> Result { 35 | let n = n as usize; 36 | 37 | if n <= 0 { 38 | return Err(Error::from_kind(ErrorKind::InvalidParameter)); 39 | } 40 | 41 | let indicator = Self { 42 | n: n, 43 | vec: vec![INFINITY; n], 44 | min_index: 0, 45 | cur_index: 0, 46 | }; 47 | 48 | Ok(indicator) 49 | } 50 | 51 | fn find_min_index(&self) -> usize { 52 | let mut min = ::std::f64::INFINITY; 53 | let mut index: usize = 0; 54 | 55 | for (i, &val) in self.vec.iter().enumerate() { 56 | if val < min { 57 | min = val; 58 | index = i; 59 | } 60 | } 61 | 62 | index 63 | } 64 | } 65 | 66 | impl Next for Minimum { 67 | type Output = f64; 68 | 69 | fn next(&mut self, input: f64) -> Self::Output { 70 | self.cur_index = (self.cur_index + 1) % (self.n as usize); 71 | self.vec[self.cur_index] = input; 72 | 73 | if input < self.vec[self.min_index] { 74 | self.min_index = self.cur_index; 75 | } else if self.min_index == self.cur_index { 76 | self.min_index = self.find_min_index(); 77 | } 78 | 79 | self.vec[self.min_index] 80 | } 81 | } 82 | 83 | impl<'a, T: Low> Next<&'a T> for Minimum { 84 | type Output = f64; 85 | 86 | fn next(&mut self, input: &'a T) -> Self::Output { 87 | self.next(input.low()) 88 | } 89 | } 90 | 91 | impl Reset for Minimum { 92 | fn reset(&mut self) { 93 | for i in 0..self.n { 94 | self.vec[i] = INFINITY; 95 | } 96 | } 97 | } 98 | 99 | impl Default for Minimum { 100 | fn default() -> Self { 101 | Self::new(14).unwrap() 102 | } 103 | } 104 | 105 | impl fmt::Display for Minimum { 106 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 107 | write!(f, "MIN({})", self.n) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use super::*; 114 | use crate::test_helper::*; 115 | macro_rules! test_indicator { 116 | ($i:tt) => { 117 | #[test] 118 | fn test_indicator() { 119 | let bar = Bar::new(); 120 | 121 | // ensure Default trait is implemented 122 | let mut indicator = $i::default(); 123 | 124 | // ensure Next is implemented 125 | let first_output = indicator.next(12.3); 126 | 127 | // ensure next accepts &DataItem as well 128 | indicator.next(&bar); 129 | 130 | // ensure Reset is implemented and works correctly 131 | indicator.reset(); 132 | assert_eq!(indicator.next(12.3), first_output); 133 | 134 | // ensure Display is implemented 135 | format!("{}", indicator); 136 | } 137 | }; 138 | } 139 | test_indicator!(Minimum); 140 | 141 | #[test] 142 | fn test_new() { 143 | assert!(Minimum::new(0).is_err()); 144 | assert!(Minimum::new(1).is_ok()); 145 | } 146 | 147 | #[test] 148 | fn test_next() { 149 | let mut min = Minimum::new(3).unwrap(); 150 | 151 | assert_eq!(min.next(4.0), 4.0); 152 | assert_eq!(min.next(1.2), 1.2); 153 | assert_eq!(min.next(5.0), 1.2); 154 | assert_eq!(min.next(3.0), 1.2); 155 | assert_eq!(min.next(4.0), 3.0); 156 | assert_eq!(min.next(6.0), 3.0); 157 | assert_eq!(min.next(7.0), 4.0); 158 | assert_eq!(min.next(8.0), 6.0); 159 | assert_eq!(min.next(-9.0), -9.0); 160 | assert_eq!(min.next(0.0), -9.0); 161 | } 162 | 163 | #[test] 164 | fn test_next_with_bars() { 165 | fn bar(low: f64) -> Bar { 166 | Bar::new().low(low) 167 | } 168 | 169 | let mut min = Minimum::new(3).unwrap(); 170 | 171 | assert_eq!(min.next(&bar(4.0)), 4.0); 172 | assert_eq!(min.next(&bar(4.0)), 4.0); 173 | assert_eq!(min.next(&bar(1.2)), 1.2); 174 | assert_eq!(min.next(&bar(5.0)), 1.2); 175 | } 176 | 177 | #[test] 178 | fn test_reset() { 179 | let mut min = Minimum::new(10).unwrap(); 180 | 181 | assert_eq!(min.next(5.0), 5.0); 182 | assert_eq!(min.next(7.0), 5.0); 183 | 184 | min.reset(); 185 | assert_eq!(min.next(8.0), 8.0); 186 | } 187 | 188 | #[test] 189 | fn test_default() { 190 | Minimum::default(); 191 | } 192 | 193 | #[test] 194 | fn test_display() { 195 | let indicator = Minimum::new(10).unwrap(); 196 | assert_eq!(format!("{}", indicator), "MIN(10)"); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/indicators/slow_stochastic.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::Result; 4 | use crate::indicators::{ExponentialMovingAverage, FastStochastic}; 5 | use crate::{Close, High, Low, Next, Reset}; 6 | 7 | /// Slow stochastic oscillator. 8 | /// 9 | /// Basically it is a fast stochastic oscillator smoothed with exponential moving average. 10 | /// 11 | /// # Parameters 12 | /// 13 | /// * _stochastic_n_ - number of periods for fast stochastic (integer greater than 0). Default is 14. 14 | /// *_ema_n_ - length for EMA (integer greater than 0). Default is 3. 15 | /// 16 | /// # Example 17 | /// 18 | /// ``` 19 | /// use quantaxis_rs::indicators::SlowStochastic; 20 | /// use quantaxis_rs::Next; 21 | /// 22 | /// let mut stoch = SlowStochastic::new(3, 2).unwrap(); 23 | /// assert_eq!(stoch.next(10.0), 50.0); 24 | /// assert_eq!(stoch.next(50.0).round(), 83.0); 25 | /// assert_eq!(stoch.next(50.0).round(), 94.0); 26 | /// assert_eq!(stoch.next(30.0).round(), 31.0); 27 | /// assert_eq!(stoch.next(55.0).round(), 77.0); 28 | /// ``` 29 | #[derive(Clone, Debug)] 30 | pub struct SlowStochastic { 31 | fast_stochastic: FastStochastic, 32 | ema: ExponentialMovingAverage, 33 | } 34 | 35 | impl SlowStochastic { 36 | pub fn new(stochastic_n: u32, ema_n: u32) -> Result { 37 | let indicator = Self { 38 | fast_stochastic: FastStochastic::new(stochastic_n)?, 39 | ema: ExponentialMovingAverage::new(ema_n)?, 40 | }; 41 | Ok(indicator) 42 | } 43 | } 44 | 45 | impl Next for SlowStochastic { 46 | type Output = f64; 47 | 48 | fn next(&mut self, input: f64) -> Self::Output { 49 | self.ema.next(self.fast_stochastic.next(input)) 50 | } 51 | } 52 | 53 | impl<'a, T: High + Low + Close> Next<&'a T> for SlowStochastic { 54 | type Output = f64; 55 | 56 | fn next(&mut self, input: &'a T) -> Self::Output { 57 | self.ema.next(self.fast_stochastic.next(input)) 58 | } 59 | } 60 | 61 | impl Reset for SlowStochastic { 62 | fn reset(&mut self) { 63 | self.fast_stochastic.reset(); 64 | self.ema.reset(); 65 | } 66 | } 67 | 68 | impl Default for SlowStochastic { 69 | fn default() -> Self { 70 | Self::new(14, 3).unwrap() 71 | } 72 | } 73 | 74 | impl fmt::Display for SlowStochastic { 75 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 76 | write!( 77 | f, 78 | "SLOW_STOCH({}, {})", 79 | self.fast_stochastic.length(), 80 | self.ema.length() 81 | ) 82 | } 83 | } 84 | 85 | #[cfg(test)] 86 | mod tests { 87 | use super::*; 88 | use crate::test_helper::*; 89 | macro_rules! test_indicator { 90 | ($i:tt) => { 91 | #[test] 92 | fn test_indicator() { 93 | let bar = Bar::new(); 94 | 95 | // ensure Default trait is implemented 96 | let mut indicator = $i::default(); 97 | 98 | // ensure Next is implemented 99 | let first_output = indicator.next(12.3); 100 | 101 | // ensure next accepts &DataItem as well 102 | indicator.next(&bar); 103 | 104 | // ensure Reset is implemented and works correctly 105 | indicator.reset(); 106 | assert_eq!(indicator.next(12.3), first_output); 107 | 108 | // ensure Display is implemented 109 | format!("{}", indicator); 110 | } 111 | }; 112 | } 113 | test_indicator!(SlowStochastic); 114 | 115 | #[test] 116 | fn test_new() { 117 | assert!(SlowStochastic::new(0, 1).is_err()); 118 | assert!(SlowStochastic::new(1, 0).is_err()); 119 | assert!(SlowStochastic::new(1, 1).is_ok()); 120 | } 121 | 122 | #[test] 123 | fn test_next_with_f64() { 124 | let mut stoch = SlowStochastic::new(3, 2).unwrap(); 125 | assert_eq!(stoch.next(10.0), 50.0); 126 | assert_eq!(stoch.next(50.0).round(), 83.0); 127 | assert_eq!(stoch.next(50.0).round(), 94.0); 128 | assert_eq!(stoch.next(30.0).round(), 31.0); 129 | assert_eq!(stoch.next(55.0).round(), 77.0); 130 | } 131 | 132 | #[test] 133 | fn test_next_with_bars() { 134 | let test_data = vec![ 135 | // high, low , close, expected 136 | (30.0, 10.0, 25.0, 75.0), 137 | (20.0, 20.0, 20.0, 58.0), 138 | (40.0, 20.0, 16.0, 33.0), 139 | (35.0, 15.0, 19.0, 22.0), 140 | (30.0, 20.0, 25.0, 34.0), 141 | (35.0, 25.0, 30.0, 61.0), 142 | ]; 143 | 144 | let mut stoch = SlowStochastic::new(3, 2).unwrap(); 145 | 146 | for (high, low, close, expected) in test_data { 147 | let input_bar = Bar::new().high(high).low(low).close(close); 148 | assert_eq!(stoch.next(&input_bar).round(), expected); 149 | } 150 | } 151 | 152 | #[test] 153 | fn test_reset() { 154 | let mut stoch = SlowStochastic::new(3, 2).unwrap(); 155 | assert_eq!(stoch.next(10.0), 50.0); 156 | assert_eq!(stoch.next(50.0).round(), 83.0); 157 | assert_eq!(stoch.next(50.0).round(), 94.0); 158 | 159 | stoch.reset(); 160 | assert_eq!(stoch.next(10.0), 50.0); 161 | } 162 | 163 | #[test] 164 | fn test_default() { 165 | SlowStochastic::default(); 166 | } 167 | 168 | #[test] 169 | fn test_display() { 170 | let indicator = SlowStochastic::new(10, 2).unwrap(); 171 | assert_eq!(format!("{}", indicator), "SLOW_STOCH(10, 2)"); 172 | } 173 | } 174 | -------------------------------------------------------------------------------- /src/indicators/maximum.rs: -------------------------------------------------------------------------------- 1 | use std::f64::INFINITY; 2 | use std::fmt; 3 | 4 | use crate::{High, Next, Reset}; 5 | use crate::errors::*; 6 | 7 | /// Returns the highest value in a given time frame. 8 | /// 9 | /// # Parameters 10 | /// 11 | /// * _n_ - size of the time frame (integer greater than 0). Default value is 14. 12 | /// 13 | /// # Example 14 | /// 15 | /// ``` 16 | /// use quantaxis_rs::indicators::Maximum; 17 | /// use quantaxis_rs::Next; 18 | /// 19 | /// let mut max = Maximum::new(3).unwrap(); 20 | /// assert_eq!(max.next(7.0), 7.0); 21 | /// assert_eq!(max.next(5.0), 7.0); 22 | /// assert_eq!(max.next(4.0), 7.0); 23 | /// assert_eq!(max.next(4.0), 5.0); 24 | /// assert_eq!(max.next(8.0), 8.0); 25 | /// ``` 26 | #[derive(Debug, Clone)] 27 | pub struct Maximum { 28 | n: usize, 29 | vec: Vec, 30 | max_index: usize, 31 | cur_index: usize, 32 | } 33 | 34 | impl Maximum { 35 | pub fn new(n: u32) -> Result { 36 | let n = n as usize; 37 | 38 | if n == 0 { 39 | return Err(Error::from_kind(ErrorKind::InvalidParameter)); 40 | } 41 | 42 | let indicator = Self { 43 | n: n, 44 | vec: vec![-INFINITY; n], 45 | max_index: 0, 46 | cur_index: 0, 47 | }; 48 | Ok(indicator) 49 | } 50 | 51 | fn find_max_index(&self) -> usize { 52 | let mut max = -INFINITY; 53 | let mut index: usize = 0; 54 | 55 | for (i, &val) in self.vec.iter().enumerate() { 56 | if val > max { 57 | max = val; 58 | index = i; 59 | } 60 | } 61 | 62 | index 63 | } 64 | } 65 | 66 | impl Next for Maximum { 67 | type Output = f64; 68 | 69 | fn next(&mut self, input: f64) -> Self::Output { 70 | self.cur_index = (self.cur_index + 1) % (self.n as usize); 71 | self.vec[self.cur_index] = input; 72 | 73 | if input > self.vec[self.max_index] { 74 | self.max_index = self.cur_index; 75 | } else if self.max_index == self.cur_index { 76 | self.max_index = self.find_max_index(); 77 | } 78 | 79 | self.vec[self.max_index] 80 | } 81 | } 82 | 83 | impl<'a, T: High> Next<&'a T> for Maximum { 84 | type Output = f64; 85 | 86 | fn next(&mut self, input: &'a T) -> Self::Output { 87 | self.next(input.high()) 88 | } 89 | } 90 | 91 | impl Reset for Maximum { 92 | fn reset(&mut self) { 93 | for i in 0..self.n { 94 | self.vec[i] = -INFINITY; 95 | } 96 | } 97 | } 98 | 99 | impl Default for Maximum { 100 | fn default() -> Self { 101 | Self::new(14).unwrap() 102 | } 103 | } 104 | 105 | impl fmt::Display for Maximum { 106 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 107 | write!(f, "MAX({})", self.n) 108 | } 109 | } 110 | 111 | #[cfg(test)] 112 | mod tests { 113 | use crate::test_helper::*; 114 | 115 | use super::*; 116 | macro_rules! test_indicator { 117 | ($i:tt) => { 118 | #[test] 119 | fn test_indicator() { 120 | let bar = Bar::new(); 121 | 122 | // ensure Default trait is implemented 123 | let mut indicator = $i::default(); 124 | 125 | // ensure Next is implemented 126 | let first_output = indicator.next(12.3); 127 | 128 | // ensure next accepts &DataItem as well 129 | indicator.next(&bar); 130 | 131 | // ensure Reset is implemented and works correctly 132 | indicator.reset(); 133 | assert_eq!(indicator.next(12.3), first_output); 134 | 135 | // ensure Display is implemented 136 | format!("{}", indicator); 137 | } 138 | }; 139 | } 140 | test_indicator!(Maximum); 141 | 142 | #[test] 143 | fn test_new() { 144 | assert!(Maximum::new(0).is_err()); 145 | assert!(Maximum::new(1).is_ok()); 146 | } 147 | 148 | #[test] 149 | fn test_next() { 150 | let mut max = Maximum::new(3).unwrap(); 151 | 152 | assert_eq!(max.next(4.0), 4.0); 153 | assert_eq!(max.next(1.2), 4.0); 154 | assert_eq!(max.next(5.0), 5.0); 155 | assert_eq!(max.next(3.0), 5.0); 156 | assert_eq!(max.next(4.0), 5.0); 157 | assert_eq!(max.next(0.0), 4.0); 158 | assert_eq!(max.next(-1.0), 4.0); 159 | assert_eq!(max.next(-2.0), 0.0); 160 | assert_eq!(max.next(-1.5), -1.0); 161 | } 162 | 163 | #[test] 164 | fn test_next_with_bars() { 165 | fn bar(high: f64) -> Bar { 166 | Bar::new().high(high) 167 | } 168 | 169 | let mut max = Maximum::new(2).unwrap(); 170 | 171 | assert_eq!(max.next(&bar(1.1)), 1.1); 172 | assert_eq!(max.next(&bar(4.0)), 4.0); 173 | assert_eq!(max.next(&bar(3.5)), 4.0); 174 | assert_eq!(max.next(&bar(2.0)), 3.5); 175 | } 176 | 177 | #[test] 178 | fn test_reset() { 179 | let mut max = Maximum::new(100).unwrap(); 180 | assert_eq!(max.next(4.0), 4.0); 181 | assert_eq!(max.next(10.0), 10.0); 182 | assert_eq!(max.next(4.0), 10.0); 183 | 184 | max.reset(); 185 | assert_eq!(max.next(4.0), 4.0); 186 | } 187 | 188 | #[test] 189 | fn test_default() { 190 | Maximum::default(); 191 | } 192 | 193 | #[test] 194 | fn test_display() { 195 | let indicator = Maximum::new(7).unwrap(); 196 | assert_eq!(format!("{}", indicator), "MAX(7)"); 197 | } 198 | } 199 | -------------------------------------------------------------------------------- /src/indicators/rate_of_change.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::fmt; 3 | 4 | use crate::errors::*; 5 | use crate::traits::{Close, Next, Reset}; 6 | 7 | /// Rate of Change (ROC) 8 | /// 9 | /// # Formula 10 | /// 11 | /// ROC = (Pricet - Pricet-n) / Pricet-n * 100 12 | /// 13 | /// Where: 14 | /// 15 | /// * ROC - current value of Rate of Change indicator 16 | /// * Pt - price at the moment 17 | /// * Pt-n - price _n_ periods ago 18 | /// 19 | /// # Parameters 20 | /// 21 | /// * _length_ - number of periods (_n_), integer greater than 0 22 | /// 23 | /// # Example 24 | /// 25 | /// ``` 26 | /// use quantaxis_rs::indicators::RateOfChange; 27 | /// use quantaxis_rs::Next; 28 | /// 29 | /// let mut roc = RateOfChange::new(2).unwrap(); 30 | /// assert_eq!(roc.next(10.0), 0.0); // 0 31 | /// assert_eq!(roc.next(9.7).round(), -3.0); // (9.7 - 10) / 10 * 100 = -3 32 | /// assert_eq!(roc.next(20.0).round(), 100.0); // (20 - 10) / 10 * 100 = 100 33 | /// assert_eq!(roc.next(20.0).round(), 106.0); // (20 - 9.7) / 9.7 * 100 = 106 34 | /// ``` 35 | /// 36 | /// # Links 37 | /// 38 | /// * [Rate of Change, Wikipedia](https://en.wikipedia.org/wiki/Momentum_(technical_analysis)) 39 | /// 40 | #[derive(Debug, Clone)] 41 | pub struct RateOfChange { 42 | length: u32, 43 | prices: VecDeque, 44 | } 45 | 46 | impl RateOfChange { 47 | pub fn new(length: u32) -> Result { 48 | match length { 49 | 0 => Err(Error::from_kind(ErrorKind::InvalidParameter)), 50 | _ => { 51 | let indicator = Self { 52 | length: length, 53 | prices: VecDeque::with_capacity(length as usize + 1), 54 | }; 55 | Ok(indicator) 56 | } 57 | } 58 | } 59 | } 60 | 61 | impl Next for RateOfChange { 62 | type Output = f64; 63 | 64 | fn next(&mut self, input: f64) -> f64 { 65 | self.prices.push_back(input); 66 | 67 | if self.prices.len() == 1 { 68 | return 0.0; 69 | } 70 | 71 | let initial_price = if self.prices.len() > (self.length as usize) { 72 | // unwrap is safe, because the check above. 73 | // At this moment there must be at least 2 items in self.prices 74 | self.prices.pop_front().unwrap() 75 | } else { 76 | self.prices[0] 77 | }; 78 | 79 | (input - initial_price) / initial_price * 100.0 80 | } 81 | } 82 | 83 | impl<'a, T: Close> Next<&'a T> for RateOfChange { 84 | type Output = f64; 85 | 86 | fn next(&mut self, input: &'a T) -> f64 { 87 | self.next(input.close()) 88 | } 89 | } 90 | 91 | impl Default for RateOfChange { 92 | fn default() -> Self { 93 | Self::new(9).unwrap() 94 | } 95 | } 96 | 97 | impl fmt::Display for RateOfChange { 98 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 99 | write!(f, "ROC({})", self.length) 100 | } 101 | } 102 | 103 | impl Reset for RateOfChange { 104 | fn reset(&mut self) { 105 | self.prices.clear(); 106 | } 107 | } 108 | 109 | #[cfg(test)] 110 | mod tests { 111 | use super::*; 112 | use crate::test_helper::*; 113 | macro_rules! test_indicator { 114 | ($i:tt) => { 115 | #[test] 116 | fn test_indicator() { 117 | let bar = Bar::new(); 118 | 119 | // ensure Default trait is implemented 120 | let mut indicator = $i::default(); 121 | 122 | // ensure Next is implemented 123 | let first_output = indicator.next(12.3); 124 | 125 | // ensure next accepts &DataItem as well 126 | indicator.next(&bar); 127 | 128 | // ensure Reset is implemented and works correctly 129 | indicator.reset(); 130 | assert_eq!(indicator.next(12.3), first_output); 131 | 132 | // ensure Display is implemented 133 | format!("{}", indicator); 134 | } 135 | }; 136 | } 137 | test_indicator!(RateOfChange); 138 | 139 | #[test] 140 | fn test_new() { 141 | assert!(RateOfChange::new(0).is_err()); 142 | assert!(RateOfChange::new(1).is_ok()); 143 | assert!(RateOfChange::new(100_000).is_ok()); 144 | } 145 | 146 | #[test] 147 | fn test_next_f64() { 148 | let mut roc = RateOfChange::new(3).unwrap(); 149 | 150 | assert_eq!(round(roc.next(10.0)), 0.0); 151 | assert_eq!(round(roc.next(10.4)), 4.0); 152 | assert_eq!(round(roc.next(10.57)), 5.7); 153 | assert_eq!(round(roc.next(10.8)), 8.0); 154 | assert_eq!(round(roc.next(10.9)), 4.808); 155 | assert_eq!(round(roc.next(10.0)), -5.393); 156 | } 157 | 158 | #[test] 159 | fn test_next_bar() { 160 | fn bar(close: f64) -> Bar { 161 | Bar::new().close(close) 162 | } 163 | 164 | let mut roc = RateOfChange::new(3).unwrap(); 165 | 166 | assert_eq!(round(roc.next(&bar(10.0))), 0.0); 167 | assert_eq!(round(roc.next(&bar(10.4))), 4.0); 168 | assert_eq!(round(roc.next(&bar(10.57))), 5.7); 169 | } 170 | 171 | #[test] 172 | fn test_reset() { 173 | let mut roc = RateOfChange::new(3).unwrap(); 174 | 175 | roc.next(12.3); 176 | roc.next(15.0); 177 | 178 | roc.reset(); 179 | 180 | assert_eq!(round(roc.next(10.0)), 0.0); 181 | assert_eq!(round(roc.next(10.4)), 4.0); 182 | assert_eq!(round(roc.next(10.57)), 5.7); 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /src/indicators/average_true_range.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::*; 4 | use crate::indicators::{MovingAverage, TrueRange}; 5 | use crate::{Close, High, Low, Next, Reset, Update}; 6 | use std::f64::INFINITY; 7 | 8 | /// Average true range (ATR). 9 | /// 10 | /// A technical analysis volatility indicator, originally developed by J. Welles Wilder. 11 | /// The average true range is an N-day smoothed moving average of the true range values. 12 | /// This implementation uses exponential moving average. 13 | /// 14 | /// # Formula 15 | /// 16 | /// ATR(length)t = EMA(length) of TRt 17 | /// 18 | /// Where: 19 | /// 20 | /// * _EMA(n)_ - [exponential moving average](struct.ExponentialMovingAverage.html) with smoothing period _length_ 21 | /// * _TRt_ - [true range](struct.TrueRange.html) for period _t_ 22 | /// 23 | /// # Parameters 24 | /// 25 | /// * _length_ - smoothing period of EMA (integer greater than 0) 26 | /// 27 | /// } 28 | #[derive(Debug, Clone)] 29 | pub struct AverageTrueRange { 30 | true_range: TrueRange, 31 | ma: MovingAverage, 32 | length: usize, 33 | pub cached: Vec 34 | } 35 | 36 | impl AverageTrueRange { 37 | pub fn new(length: u32) -> Result { 38 | let indicator = Self { 39 | true_range: TrueRange::new(), 40 | ma: MovingAverage::new(length)?, 41 | length: length as usize, 42 | cached: vec![-INFINITY; length as usize] 43 | }; 44 | Ok(indicator) 45 | } 46 | } 47 | 48 | impl Next for AverageTrueRange { 49 | type Output = f64; 50 | 51 | fn next(&mut self, input: f64) -> Self::Output { 52 | let res = self.ma.next(self.true_range.next(input)); 53 | self.cached.push(res); 54 | self.cached.remove(0); 55 | res 56 | } 57 | } 58 | impl Update for AverageTrueRange { 59 | type Output = f64; 60 | 61 | fn update(&mut self, input: f64) -> Self::Output { 62 | let res = self.ma.update(self.true_range.update(input)); 63 | let x = self.cached.last_mut().unwrap(); 64 | *x = res; 65 | res 66 | } 67 | } 68 | 69 | impl<'a, T: High + Low + Close> Next<&'a T> for AverageTrueRange { 70 | type Output = f64; 71 | 72 | fn next(&mut self, input: &'a T) -> Self::Output { 73 | let res = self.ma.next(self.true_range.next(input)); 74 | self.cached.push(res); 75 | self.cached.remove(0); 76 | res 77 | } 78 | } 79 | 80 | impl<'a, T: High + Low + Close> Update<&'a T> for AverageTrueRange { 81 | type Output = f64; 82 | 83 | fn update(&mut self, input: &'a T) -> Self::Output { 84 | let res = self.ma.update(self.true_range.update(input)); 85 | let x = self.cached.last_mut().unwrap(); 86 | *x = res; 87 | res 88 | } 89 | } 90 | 91 | impl Reset for AverageTrueRange { 92 | fn reset(&mut self) { 93 | self.true_range.reset(); 94 | self.ma.reset(); 95 | } 96 | } 97 | 98 | impl Default for AverageTrueRange { 99 | fn default() -> Self { 100 | Self::new(14).unwrap() 101 | } 102 | } 103 | 104 | impl fmt::Display for AverageTrueRange { 105 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 106 | write!(f, "ATR({})", self.ma.n) 107 | } 108 | } 109 | 110 | #[cfg(test)] 111 | mod tests { 112 | use super::*; 113 | use crate::test_helper::*; 114 | macro_rules! test_indicator { 115 | ($i:tt) => { 116 | #[test] 117 | fn test_indicator() { 118 | let bar = Bar::new(); 119 | 120 | // ensure Default trait is implemented 121 | let mut indicator = $i::default(); 122 | 123 | // ensure Next is implemented 124 | let first_output = indicator.next(12.3); 125 | 126 | // ensure next accepts &DataItem as well 127 | indicator.next(&bar); 128 | 129 | // ensure Reset is implemented and works correctly 130 | indicator.reset(); 131 | assert_eq!(indicator.next(12.3), first_output); 132 | 133 | // ensure Display is implemented 134 | format!("{}", indicator); 135 | } 136 | }; 137 | } 138 | test_indicator!(AverageTrueRange); 139 | 140 | #[test] 141 | fn test_new() { 142 | assert!(AverageTrueRange::new(0).is_err()); 143 | assert!(AverageTrueRange::new(1).is_ok()); 144 | } 145 | #[test] 146 | fn test_next() { 147 | let mut atr = AverageTrueRange::new(3).unwrap(); 148 | 149 | let bar1 = Bar::new().high(10).low(7.5).close(9); 150 | let bar2 = Bar::new().high(11).low(9).close(9.5); 151 | let bar3 = Bar::new().high(9).low(5).close(8); 152 | 153 | assert_eq!(atr.next(&bar1), 0f64); 154 | assert_eq!(atr.next(&bar2), 0f64); 155 | assert_eq!(atr.next(&bar3), 3f64); 156 | } 157 | 158 | #[test] 159 | fn test_reset() { 160 | let mut atr = AverageTrueRange::new(9).unwrap(); 161 | 162 | let bar1 = Bar::new().high(10).low(7.5).close(9); 163 | let bar2 = Bar::new().high(11).low(9).close(9.5); 164 | 165 | atr.next(&bar1); 166 | atr.next(&bar2); 167 | 168 | atr.reset(); 169 | let bar3 = Bar::new().high(60).low(15).close(51); 170 | assert_eq!(atr.next(&bar3), 0.0); 171 | } 172 | 173 | #[test] 174 | fn test_default() { 175 | AverageTrueRange::default(); 176 | } 177 | 178 | #[test] 179 | fn test_display() { 180 | let indicator = AverageTrueRange::new(8).unwrap(); 181 | assert_eq!(format!("{}", indicator), "ATR(8)"); 182 | } 183 | } 184 | -------------------------------------------------------------------------------- /src/indicators/simple_moving_average.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::*; 4 | use crate::{Close, Next, Reset}; 5 | 6 | /// Simple moving average (SMA). 7 | /// 8 | /// # Formula 9 | /// 10 | /// ![SMA](https://wikimedia.org/api/rest_v1/media/math/render/svg/e2bf09dc6deaf86b3607040585fac6078f9c7c89) 11 | /// 12 | /// Where: 13 | /// 14 | /// * _SMAt_ - value of simple moving average at a point of time _t_ 15 | /// * _n_ - number of periods (length) 16 | /// * _pt_ - input value at a point of time _t_ 17 | /// 18 | /// # Parameters 19 | /// 20 | /// * _n_ - number of periods (integer greater than 0) 21 | /// 22 | /// # Example 23 | /// 24 | /// ``` 25 | /// use quantaxis_rs::indicators::SimpleMovingAverage; 26 | /// use quantaxis_rs::Next; 27 | /// 28 | /// let mut sma = SimpleMovingAverage::new(3).unwrap(); 29 | /// assert_eq!(sma.next(10.0), 10.0); 30 | /// assert_eq!(sma.next(11.0), 10.5); 31 | /// assert_eq!(sma.next(12.0), 11.0); 32 | /// assert_eq!(sma.next(13.0), 12.0); 33 | /// ``` 34 | /// 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct SimpleMovingAverage { 38 | n: u32, 39 | index: usize, 40 | count: u32, 41 | sum: f64, 42 | vec: Vec, 43 | } 44 | 45 | impl SimpleMovingAverage { 46 | pub fn new(n: u32) -> Result { 47 | match n { 48 | 0 => Err(Error::from_kind(ErrorKind::InvalidParameter)), 49 | _ => { 50 | let indicator = Self { 51 | n: n, 52 | index: 0, 53 | count: 0, 54 | sum: 0.0, 55 | vec: vec![0.0; n as usize], 56 | }; 57 | Ok(indicator) 58 | } 59 | } 60 | } 61 | } 62 | 63 | impl Next for SimpleMovingAverage { 64 | type Output = f64; 65 | 66 | fn next(&mut self, input: f64) -> Self::Output { 67 | self.index = (self.index + 1) % (self.n as usize); 68 | 69 | let old_val = self.vec[self.index]; 70 | self.vec[self.index] = input; 71 | 72 | if self.count < self.n { 73 | self.count += 1; 74 | } 75 | 76 | self.sum = self.sum - old_val + input; 77 | self.sum / (self.count as f64) 78 | } 79 | } 80 | 81 | impl<'a, T: Close> Next<&'a T> for SimpleMovingAverage { 82 | type Output = f64; 83 | 84 | fn next(&mut self, input: &'a T) -> Self::Output { 85 | self.next(input.close()) 86 | } 87 | } 88 | 89 | impl Reset for SimpleMovingAverage { 90 | fn reset(&mut self) { 91 | self.index = 0; 92 | self.count = 0; 93 | self.sum = 0.0; 94 | for i in 0..(self.n as usize) { 95 | self.vec[i] = 0.0; 96 | } 97 | } 98 | } 99 | 100 | impl Default for SimpleMovingAverage { 101 | fn default() -> Self { 102 | Self::new(9).unwrap() 103 | } 104 | } 105 | 106 | impl fmt::Display for SimpleMovingAverage { 107 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 108 | write!(f, "SMA({})", self.n) 109 | } 110 | } 111 | 112 | #[cfg(test)] 113 | mod tests { 114 | use super::*; 115 | use crate::test_helper::*; 116 | macro_rules! test_indicator { 117 | ($i:tt) => { 118 | #[test] 119 | fn test_indicator() { 120 | let bar = Bar::new(); 121 | 122 | // ensure Default trait is implemented 123 | let mut indicator = $i::default(); 124 | 125 | // ensure Next is implemented 126 | let first_output = indicator.next(12.3); 127 | 128 | // ensure next accepts &DataItem as well 129 | indicator.next(&bar); 130 | 131 | // ensure Reset is implemented and works correctly 132 | indicator.reset(); 133 | assert_eq!(indicator.next(12.3), first_output); 134 | 135 | // ensure Display is implemented 136 | format!("{}", indicator); 137 | } 138 | }; 139 | } 140 | test_indicator!(SimpleMovingAverage); 141 | 142 | #[test] 143 | fn test_new() { 144 | assert!(SimpleMovingAverage::new(0).is_err()); 145 | assert!(SimpleMovingAverage::new(1).is_ok()); 146 | } 147 | 148 | #[test] 149 | fn test_next() { 150 | let mut sma = SimpleMovingAverage::new(4).unwrap(); 151 | assert_eq!(sma.next(4.0), 4.0); 152 | assert_eq!(sma.next(5.0), 4.5); 153 | assert_eq!(sma.next(6.0), 5.0); 154 | assert_eq!(sma.next(6.0), 5.25); 155 | assert_eq!(sma.next(6.0), 5.75); 156 | assert_eq!(sma.next(6.0), 6.0); 157 | assert_eq!(sma.next(2.0), 5.0); 158 | } 159 | 160 | #[test] 161 | fn test_next_with_bars() { 162 | fn bar(close: f64) -> Bar { 163 | Bar::new().close(close) 164 | } 165 | 166 | let mut sma = SimpleMovingAverage::new(3).unwrap(); 167 | assert_eq!(sma.next(&bar(4.0)), 4.0); 168 | assert_eq!(sma.next(&bar(4.0)), 4.0); 169 | assert_eq!(sma.next(&bar(7.0)), 5.0); 170 | assert_eq!(sma.next(&bar(1.0)), 4.0); 171 | } 172 | 173 | #[test] 174 | fn test_reset() { 175 | let mut sma = SimpleMovingAverage::new(4).unwrap(); 176 | assert_eq!(sma.next(4.0), 4.0); 177 | assert_eq!(sma.next(5.0), 4.5); 178 | assert_eq!(sma.next(6.0), 5.0); 179 | 180 | sma.reset(); 181 | assert_eq!(sma.next(99.0), 99.0); 182 | } 183 | 184 | #[test] 185 | fn test_default() { 186 | SimpleMovingAverage::default(); 187 | } 188 | 189 | #[test] 190 | fn test_display() { 191 | let sma = SimpleMovingAverage::new(5).unwrap(); 192 | assert_eq!(format!("{}", sma), "SMA(5)"); 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/indicators/hhv.rs: -------------------------------------------------------------------------------- 1 | use std::f64::INFINITY; 2 | use std::fmt; 3 | 4 | use crate::{High, Next, Reset, Update}; 5 | use crate::errors::*; 6 | 7 | /// Returns the highest value in a given time frame. 8 | /// 9 | /// # Parameters 10 | /// 11 | /// * _n_ - size of the time frame (integer greater than 0). Default value is 14. 12 | /// 13 | /// # Example 14 | /// 15 | /// ``` 16 | /// use quantaxis_rs::indicators::HHV; 17 | /// use quantaxis_rs::Next; 18 | /// 19 | /// let mut max = HHV::new(3).unwrap(); 20 | /// assert_eq!(max.next(7.0), 7.0); 21 | /// assert_eq!(max.next(5.0), 7.0); 22 | /// assert_eq!(max.next(4.0), 7.0); 23 | /// assert_eq!(max.next(4.0), 5.0); 24 | /// assert_eq!(max.next(8.0), 8.0); 25 | /// ``` 26 | #[derive(Debug, Clone)] 27 | pub struct HHV { 28 | n: usize, 29 | vec: Vec, 30 | max_index: usize, 31 | cur_index: usize, 32 | pub cached: Vec 33 | } 34 | 35 | impl HHV { 36 | pub fn new(n: u32) -> Result { 37 | let n = n as usize; 38 | 39 | if n == 0 { 40 | return Err(Error::from_kind(ErrorKind::InvalidParameter)); 41 | } 42 | 43 | let indicator = Self { 44 | n: n, 45 | vec: vec![-INFINITY; n], 46 | max_index: 0, 47 | cur_index: 0, 48 | cached: vec![-INFINITY; n] 49 | }; 50 | Ok(indicator) 51 | } 52 | 53 | fn find_max_index(&self) -> usize { 54 | let mut max = -INFINITY; 55 | let mut index: usize = 0; 56 | 57 | for (i, &val) in self.vec.iter().enumerate() { 58 | if val > max { 59 | max = val; 60 | index = i; 61 | } 62 | } 63 | 64 | index 65 | } 66 | } 67 | 68 | impl Next for HHV { 69 | type Output = f64; 70 | 71 | fn next(&mut self, input: f64) -> Self::Output { 72 | self.cur_index = (self.cur_index + 1) % (self.n as usize); 73 | self.vec[self.cur_index] = input; 74 | 75 | if input > self.vec[self.max_index] { 76 | self.max_index = self.cur_index; 77 | } else if self.max_index == self.cur_index { 78 | self.max_index = self.find_max_index(); 79 | } 80 | self.cached.push(self.vec[self.max_index]); 81 | self.cached.remove(0); 82 | self.vec[self.max_index] 83 | } 84 | } 85 | 86 | impl Update for HHV { 87 | type Output = f64; 88 | 89 | fn update(&mut self, input: f64) -> Self::Output { 90 | self.vec[self.cur_index] = input; 91 | 92 | if input > self.vec[self.max_index] { 93 | self.max_index = self.cur_index; 94 | } else if self.max_index == self.cur_index { 95 | self.max_index = self.find_max_index(); 96 | } 97 | self.cached.remove(self.n - 1); 98 | self.cached.push(self.vec[self.max_index]); 99 | 100 | self.vec[self.max_index] 101 | } 102 | } 103 | 104 | impl<'a, T: High> Next<&'a T> for HHV { 105 | type Output = f64; 106 | 107 | fn next(&mut self, input: &'a T) -> Self::Output { 108 | self.next(input.high()) 109 | } 110 | } 111 | 112 | impl Reset for HHV { 113 | fn reset(&mut self) { 114 | for i in 0..self.n { 115 | self.vec[i] = -INFINITY; 116 | } 117 | } 118 | } 119 | 120 | impl Default for HHV { 121 | fn default() -> Self { 122 | Self::new(14).unwrap() 123 | } 124 | } 125 | 126 | impl fmt::Display for HHV { 127 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 128 | write!(f, "MAX({})", self.n) 129 | } 130 | } 131 | 132 | #[cfg(test)] 133 | mod tests { 134 | use crate::test_helper::*; 135 | 136 | use super::*; 137 | 138 | macro_rules! test_indicator { 139 | ($i:tt) => { 140 | #[test] 141 | fn test_indicator() { 142 | let bar = Bar::new(); 143 | 144 | // ensure Default trait is implemented 145 | let mut indicator = $i::default(); 146 | 147 | // ensure Next is implemented 148 | let first_output = indicator.next(12.3); 149 | 150 | // ensure next accepts &DataItem as well 151 | indicator.next(&bar); 152 | 153 | // ensure Reset is implemented and works correctly 154 | indicator.reset(); 155 | assert_eq!(indicator.next(12.3), first_output); 156 | 157 | // ensure Display is implemented 158 | format!("{}", indicator); 159 | } 160 | }; 161 | } 162 | test_indicator!(HHV); 163 | 164 | #[test] 165 | fn test_new() { 166 | assert!(HHV::new(0).is_err()); 167 | assert!(HHV::new(1).is_ok()); 168 | } 169 | 170 | #[test] 171 | fn test_next() { 172 | let mut max = HHV::new(3).unwrap(); 173 | 174 | assert_eq!(max.next(4.0), 4.0); 175 | assert_eq!(max.next(1.2), 4.0); 176 | assert_eq!(max.next(5.0), 5.0); 177 | assert_eq!(max.next(3.0), 5.0); 178 | assert_eq!(max.next(4.0), 5.0); 179 | assert_eq!(max.next(0.0), 4.0); 180 | assert_eq!(max.next(-1.0), 4.0); 181 | assert_eq!(max.next(-2.0), 0.0); 182 | assert_eq!(max.next(-1.5), -1.0); 183 | } 184 | 185 | #[test] 186 | fn test_update() { 187 | let mut max = HHV::new(3).unwrap(); 188 | 189 | assert_eq!(max.next(4.0), 4.0); 190 | assert_eq!(max.next(1.2), 4.0); 191 | assert_eq!(max.next(5.0), 5.0); 192 | assert_eq!(max.update(3.0), 4.0); 193 | assert_eq!(max.next(3.0), 3.0); 194 | } 195 | 196 | #[test] 197 | fn test_next_with_bars() { 198 | fn bar(high: f64) -> Bar { 199 | Bar::new().high(high) 200 | } 201 | 202 | let mut max = HHV::new(2).unwrap(); 203 | 204 | assert_eq!(max.next(&bar(1.1)), 1.1); 205 | assert_eq!(max.next(&bar(4.0)), 4.0); 206 | assert_eq!(max.next(&bar(3.5)), 4.0); 207 | assert_eq!(max.next(&bar(2.0)), 3.5); 208 | } 209 | 210 | #[test] 211 | fn test_reset() { 212 | let mut max = HHV::new(100).unwrap(); 213 | assert_eq!(max.next(4.0), 4.0); 214 | assert_eq!(max.next(10.0), 10.0); 215 | assert_eq!(max.next(4.0), 10.0); 216 | 217 | max.reset(); 218 | assert_eq!(max.next(4.0), 4.0); 219 | } 220 | 221 | #[test] 222 | fn test_default() { 223 | HHV::default(); 224 | } 225 | 226 | #[test] 227 | fn test_display() { 228 | let indicator = HHV::new(7).unwrap(); 229 | assert_eq!(format!("{}", indicator), "MAX(7)"); 230 | } 231 | } 232 | -------------------------------------------------------------------------------- /src/indicators/standard_deviation.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::*; 4 | use crate::{Close, Next, Reset}; 5 | 6 | /// Standard deviation (SD). 7 | /// 8 | /// Returns the standard deviation of the last n values. 9 | /// 10 | /// # Formula 11 | /// 12 | /// ![SD formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/2845de27edc898d2a2a4320eda5f57e0dac6f650) 13 | /// 14 | /// Where: 15 | /// 16 | /// * _σ_ - value of standard deviation for N given probes. 17 | /// * _N_ - number of probes in observation. 18 | /// * _xi_ - i-th observed value from N elements observation. 19 | /// 20 | /// # Parameters 21 | /// 22 | /// * _n_ - number of periods (integer greater than 0) 23 | /// 24 | /// # Example 25 | /// 26 | /// ``` 27 | /// use quantaxis_rs::indicators::StandardDeviation; 28 | /// use quantaxis_rs::Next; 29 | /// 30 | /// let mut sd = StandardDeviation::new(3).unwrap(); 31 | /// assert_eq!(sd.next(10.0), 0.0); 32 | /// assert_eq!(sd.next(20.0), 5.0); 33 | /// ``` 34 | /// 35 | 36 | #[derive(Debug, Clone)] 37 | pub struct StandardDeviation { 38 | n: u32, 39 | index: usize, 40 | count: u32, 41 | m: f64, 42 | m2: f64, 43 | vec: Vec, 44 | } 45 | 46 | impl StandardDeviation { 47 | pub fn new(n: u32) -> Result { 48 | match n { 49 | 0 => Err(Error::from_kind(ErrorKind::InvalidParameter)), 50 | _ => { 51 | let std = StandardDeviation { 52 | n, 53 | index: 0, 54 | count: 0, 55 | m: 0.0, 56 | m2: 0.0, 57 | vec: vec![0.0; n as usize], 58 | }; 59 | Ok(std) 60 | } 61 | } 62 | } 63 | 64 | pub(super) fn mean(&self) -> f64 { 65 | self.m 66 | } 67 | } 68 | 69 | impl Next for StandardDeviation { 70 | type Output = f64; 71 | 72 | fn next(&mut self, input: f64) -> Self::Output { 73 | self.index = (self.index + 1) % (self.n as usize); 74 | 75 | let old_val = self.vec[self.index]; 76 | self.vec[self.index] = input; 77 | 78 | if self.count < self.n { 79 | self.count += 1; 80 | let delta = input - self.m; 81 | self.m += delta / self.count as f64; 82 | let delta2 = input - self.m; 83 | self.m2 += delta * delta2; 84 | } else { 85 | let delta = input - old_val; 86 | let old_m = self.m; 87 | self.m += delta / self.n as f64; 88 | let delta2 = input - self.m + old_val - old_m; 89 | self.m2 += delta * delta2; 90 | } 91 | 92 | (self.m2 / self.count as f64).sqrt() 93 | } 94 | } 95 | 96 | impl<'a, T: Close> Next<&'a T> for StandardDeviation { 97 | type Output = f64; 98 | 99 | fn next(&mut self, input: &'a T) -> Self::Output { 100 | self.next(input.close()) 101 | } 102 | } 103 | 104 | impl Reset for StandardDeviation { 105 | fn reset(&mut self) { 106 | self.index = 0; 107 | self.count = 0; 108 | self.m = 0.0; 109 | self.m2 = 0.0; 110 | for i in 0..(self.n as usize) { 111 | self.vec[i] = 0.0; 112 | } 113 | } 114 | } 115 | 116 | impl Default for StandardDeviation { 117 | fn default() -> Self { 118 | Self::new(9).unwrap() 119 | } 120 | } 121 | 122 | impl fmt::Display for StandardDeviation { 123 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 124 | write!(f, "SD({})", self.n) 125 | } 126 | } 127 | 128 | #[cfg(test)] 129 | mod tests { 130 | use super::*; 131 | use crate::test_helper::*; 132 | macro_rules! test_indicator { 133 | ($i:tt) => { 134 | #[test] 135 | fn test_indicator() { 136 | let bar = Bar::new(); 137 | 138 | // ensure Default trait is implemented 139 | let mut indicator = $i::default(); 140 | 141 | // ensure Next is implemented 142 | let first_output = indicator.next(12.3); 143 | 144 | // ensure next accepts &DataItem as well 145 | indicator.next(&bar); 146 | 147 | // ensure Reset is implemented and works correctly 148 | indicator.reset(); 149 | assert_eq!(indicator.next(12.3), first_output); 150 | 151 | // ensure Display is implemented 152 | format!("{}", indicator); 153 | } 154 | }; 155 | } 156 | test_indicator!(StandardDeviation); 157 | 158 | #[test] 159 | fn test_new() { 160 | assert!(StandardDeviation::new(0).is_err()); 161 | assert!(StandardDeviation::new(1).is_ok()); 162 | } 163 | 164 | #[test] 165 | fn test_next() { 166 | let mut sd = StandardDeviation::new(4).unwrap(); 167 | assert_eq!(sd.next(10.0), 0.0); 168 | assert_eq!(sd.next(20.0), 5.0); 169 | assert_eq!(round(sd.next(30.0)), 8.165); 170 | assert_eq!(round(sd.next(20.0)), 7.071); 171 | assert_eq!(round(sd.next(10.0)), 7.071); 172 | assert_eq!(round(sd.next(100.0)), 35.355); 173 | } 174 | 175 | #[test] 176 | fn test_next_with_bars() { 177 | fn bar(close: f64) -> Bar { 178 | Bar::new().close(close) 179 | } 180 | 181 | let mut sd = StandardDeviation::new(4).unwrap(); 182 | assert_eq!(sd.next(&bar(10.0)), 0.0); 183 | assert_eq!(sd.next(&bar(20.0)), 5.0); 184 | assert_eq!(round(sd.next(&bar(30.0))), 8.165); 185 | assert_eq!(round(sd.next(&bar(20.0))), 7.071); 186 | assert_eq!(round(sd.next(&bar(10.0))), 7.071); 187 | assert_eq!(round(sd.next(&bar(100.0))), 35.355); 188 | } 189 | 190 | #[test] 191 | fn test_next_same_values() { 192 | let mut sd = StandardDeviation::new(3).unwrap(); 193 | assert_eq!(sd.next(4.2), 0.0); 194 | assert_eq!(sd.next(4.2), 0.0); 195 | assert_eq!(sd.next(4.2), 0.0); 196 | assert_eq!(sd.next(4.2), 0.0); 197 | } 198 | 199 | #[test] 200 | fn test_reset() { 201 | let mut sd = StandardDeviation::new(4).unwrap(); 202 | assert_eq!(sd.next(10.0), 0.0); 203 | assert_eq!(sd.next(20.0), 5.0); 204 | assert_eq!(round(sd.next(30.0)), 8.165); 205 | 206 | sd.reset(); 207 | assert_eq!(sd.next(20.0), 0.0); 208 | } 209 | 210 | #[test] 211 | fn test_default() { 212 | StandardDeviation::default(); 213 | } 214 | 215 | #[test] 216 | fn test_display() { 217 | let sd = StandardDeviation::new(5).unwrap(); 218 | assert_eq!(format!("{}", sd), "SD(5)"); 219 | } 220 | } 221 | -------------------------------------------------------------------------------- /src/indicators/bollinger_bands.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::*; 4 | use crate::indicators::StandardDeviation as Sd; 5 | use crate::{Close, Next, Reset}; 6 | 7 | /// A Bollinger Bands (BB). 8 | /// (BB). 9 | /// It is a type of infinite impulse response filter that calculates Bollinger Bands using Exponential Moving Average. 10 | /// The Bollinger Bands are represented by Average EMA and standard deviaton that is moved 'k' times away in both directions from calculated average value. 11 | /// 12 | /// # Formula 13 | /// 14 | /// See SMA, SD documentation. 15 | /// 16 | /// BB is composed as: 17 | /// 18 | /// * _BBMiddle Band_ - Simple Moving Average (SMA). 19 | /// * _BBUpper Band_ = SMA + SD of observation * multipler (usually 2.0) 20 | /// * _BBLower Band_ = SMA - SD of observation * multipler (usually 2.0) 21 | /// 22 | /// # Example 23 | /// 24 | ///``` 25 | /// use quantaxis_rs::indicators::{BollingerBands, BollingerBandsOutput}; 26 | /// use quantaxis_rs::Next; 27 | /// 28 | /// let mut bb = BollingerBands::new(3, 2.0_f64).unwrap(); 29 | /// 30 | /// let out_0 = bb.next(2.0); 31 | /// 32 | /// let out_1 = bb.next(5.0); 33 | /// 34 | /// assert_eq!(out_0.average, 2.0); 35 | /// assert_eq!(out_0.upper, 2.0); 36 | /// assert_eq!(out_0.lower, 2.0); 37 | /// 38 | /// assert_eq!(out_1.average, 3.5); 39 | /// assert_eq!(out_1.upper, 6.5); 40 | /// assert_eq!(out_1.lower, 0.5); 41 | /// ``` 42 | /// 43 | /// # Links 44 | /// 45 | /// ![Bollinger Bands, Wikipedia](https://en.wikipedia.org/wiki/Bollinger_Bands) 46 | #[derive(Debug, Clone)] 47 | pub struct BollingerBands { 48 | length: u32, 49 | multiplier: f64, 50 | sd: Sd, 51 | } 52 | 53 | #[derive(Debug, Clone, PartialEq)] 54 | pub struct BollingerBandsOutput { 55 | pub average: f64, 56 | pub upper: f64, 57 | pub lower: f64, 58 | } 59 | 60 | impl BollingerBands { 61 | pub fn new(length: u32, multiplier: f64) -> Result { 62 | if multiplier <= 0.0 { 63 | return Err(Error::from_kind(ErrorKind::InvalidParameter)); 64 | } 65 | Ok(Self { 66 | length, 67 | multiplier, 68 | sd: Sd::new(length)?, 69 | }) 70 | } 71 | 72 | pub fn length(&self) -> u32 { 73 | self.length 74 | } 75 | 76 | pub fn multiplier(&self) -> f64 { 77 | self.multiplier 78 | } 79 | } 80 | 81 | impl Next for BollingerBands { 82 | type Output = BollingerBandsOutput; 83 | 84 | fn next(&mut self, input: f64) -> Self::Output { 85 | let sd = self.sd.next(input); 86 | let mean = self.sd.mean(); 87 | 88 | Self::Output { 89 | average: mean, 90 | upper: mean + sd * self.multiplier, 91 | lower: mean - sd * self.multiplier, 92 | } 93 | } 94 | } 95 | 96 | impl<'a, T: Close> Next<&'a T> for BollingerBands { 97 | type Output = BollingerBandsOutput; 98 | 99 | fn next(&mut self, input: &'a T) -> Self::Output { 100 | self.next(input.close()) 101 | } 102 | } 103 | 104 | impl Reset for BollingerBands { 105 | fn reset(&mut self) { 106 | self.sd.reset(); 107 | } 108 | } 109 | 110 | impl Default for BollingerBands { 111 | fn default() -> Self { 112 | Self::new(9, 2_f64).unwrap() 113 | } 114 | } 115 | 116 | impl fmt::Display for BollingerBands { 117 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 118 | write!(f, "BB({}, {})", self.length, self.multiplier) 119 | } 120 | } 121 | 122 | #[cfg(test)] 123 | mod tests { 124 | use super::*; 125 | use crate::test_helper::*; 126 | macro_rules! test_indicator { 127 | ($i:tt) => { 128 | #[test] 129 | fn test_indicator() { 130 | let bar = Bar::new(); 131 | 132 | // ensure Default trait is implemented 133 | let mut indicator = $i::default(); 134 | 135 | // ensure Next is implemented 136 | let first_output = indicator.next(12.3); 137 | 138 | // ensure next accepts &DataItem as well 139 | indicator.next(&bar); 140 | 141 | // ensure Reset is implemented and works correctly 142 | indicator.reset(); 143 | assert_eq!(indicator.next(12.3), first_output); 144 | 145 | // ensure Display is implemented 146 | format!("{}", indicator); 147 | } 148 | }; 149 | } 150 | test_indicator!(BollingerBands); 151 | 152 | #[test] 153 | fn test_new() { 154 | assert!(BollingerBands::new(0, 2_f64).is_err()); 155 | assert!(BollingerBands::new(1, 2_f64).is_ok()); 156 | assert!(BollingerBands::new(2, 2_f64).is_ok()); 157 | } 158 | 159 | #[test] 160 | fn test_next() { 161 | let mut bb = BollingerBands::new(3, 2.0_f64).unwrap(); 162 | 163 | let a = bb.next(2.0); 164 | let b = bb.next(5.0); 165 | let c = bb.next(1.0); 166 | let d = bb.next(6.25); 167 | 168 | assert_eq!(round(a.average), 2.0); 169 | assert_eq!(round(b.average), 3.5); 170 | assert_eq!(round(c.average), 2.667); 171 | assert_eq!(round(d.average), 4.083); 172 | 173 | assert_eq!(round(a.upper), 2.0); 174 | assert_eq!(round(b.upper), 6.5); 175 | assert_eq!(round(c.upper), 6.066); 176 | assert_eq!(round(d.upper), 8.562); 177 | 178 | assert_eq!(round(a.lower), 2.0); 179 | assert_eq!(round(b.lower), 0.5); 180 | assert_eq!(round(c.lower), -0.733); 181 | assert_eq!(round(d.lower), -0.395); 182 | } 183 | 184 | #[test] 185 | fn test_reset() { 186 | let mut bb = BollingerBands::new(5, 2.0_f64).unwrap(); 187 | 188 | let out = bb.next(3.0); 189 | 190 | assert_eq!(out.average, 3.0); 191 | assert_eq!(out.upper, 3.0); 192 | assert_eq!(out.lower, 3.0); 193 | 194 | bb.next(2.5); 195 | bb.next(3.5); 196 | bb.next(4.0); 197 | 198 | let out = bb.next(2.0); 199 | 200 | assert_eq!(out.average, 3.0); 201 | assert_eq!(round(out.upper), 4.414); 202 | assert_eq!(round(out.lower), 1.586); 203 | 204 | bb.reset(); 205 | let out = bb.next(3.0); 206 | assert_eq!(out.average, 3.0); 207 | assert_eq!(out.upper, 3.0); 208 | assert_eq!(out.lower, 3.0); 209 | } 210 | 211 | #[test] 212 | fn test_default() { 213 | BollingerBands::default(); 214 | } 215 | 216 | #[test] 217 | fn test_display() { 218 | let bb = BollingerBands::new(10, 3.0_f64).unwrap(); 219 | println!("{:#?}", bb); 220 | assert_eq!(format!("{}", bb), "BB(10, 3)"); 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /src/indicators/fast_stochastic.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::*; 4 | use crate::indicators::{Maximum, Minimum}; 5 | use crate::{Close, High, Low, Next, Reset}; 6 | 7 | /// Fast stochastic oscillator. 8 | /// 9 | /// The stochastic oscillator is a momentum indicator comparing the closing price 10 | /// of a security to the range of its prices over a certain period of time. 11 | /// 12 | /// # Formula 13 | /// 14 | /// ![Fast stochastic oscillator formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/5a419041034a8044308c999f85661a08bcf91b1d) 15 | /// 16 | /// Where: 17 | /// 18 | /// * \%Kt - value of fast stochastic oscillator 19 | /// * Ct - close price of the current period 20 | /// * Ln - lowest price for the last _n_ periods 21 | /// * Hn - highest price for the last _n_ periods 22 | /// 23 | /// 24 | /// # Parameters 25 | /// 26 | /// * _length_ - number of periods (integer greater than 0). Default is 14. 27 | /// 28 | /// # Example 29 | /// 30 | /// ``` 31 | /// use quantaxis_rs::indicators::FastStochastic; 32 | /// use quantaxis_rs::Next; 33 | /// 34 | /// let mut stoch = FastStochastic::new(5).unwrap(); 35 | /// assert_eq!(stoch.next(20.0), 50.0); 36 | /// assert_eq!(stoch.next(30.0), 100.0); 37 | /// assert_eq!(stoch.next(40.0), 100.0); 38 | /// assert_eq!(stoch.next(35.0), 75.0); 39 | /// assert_eq!(stoch.next(15.0), 0.0); 40 | /// ``` 41 | #[derive(Debug, Clone)] 42 | pub struct FastStochastic { 43 | length: u32, 44 | minimum: Minimum, 45 | maximum: Maximum, 46 | } 47 | 48 | impl FastStochastic { 49 | pub fn new(length: u32) -> Result { 50 | let indicator = Self { 51 | length: length, 52 | minimum: Minimum::new(length)?, 53 | maximum: Maximum::new(length)?, 54 | }; 55 | Ok(indicator) 56 | } 57 | 58 | pub fn length(&self) -> u32 { 59 | self.length 60 | } 61 | } 62 | 63 | impl Next for FastStochastic { 64 | type Output = f64; 65 | 66 | fn next(&mut self, input: f64) -> Self::Output { 67 | let min = self.minimum.next(input); 68 | let max = self.maximum.next(input); 69 | 70 | if min == max { 71 | // When only 1 input was given, than min and max are the same, 72 | // therefore it makes sense to return 50 73 | 50.0 74 | } else { 75 | (input - min) / (max - min) * 100.0 76 | } 77 | } 78 | } 79 | 80 | impl<'a, T: High + Low + Close> Next<&'a T> for FastStochastic { 81 | type Output = f64; 82 | 83 | fn next(&mut self, input: &'a T) -> Self::Output { 84 | let highest = self.maximum.next(input.high()); 85 | let lowest = self.minimum.next(input.low()); 86 | let close = input.close(); 87 | 88 | if highest == lowest { 89 | // To avoid division by zero, return 50.0 90 | 50.0 91 | } else { 92 | (close - lowest) / (highest - lowest) * 100.0 93 | } 94 | } 95 | } 96 | 97 | impl Reset for FastStochastic { 98 | fn reset(&mut self) { 99 | self.minimum.reset(); 100 | self.maximum.reset(); 101 | } 102 | } 103 | 104 | impl Default for FastStochastic { 105 | fn default() -> Self { 106 | Self::new(14).unwrap() 107 | } 108 | } 109 | 110 | impl fmt::Display for FastStochastic { 111 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 112 | write!(f, "FAST_STOCH({})", self.length) 113 | } 114 | } 115 | 116 | #[cfg(test)] 117 | mod tests { 118 | use super::*; 119 | use crate::test_helper::*; 120 | macro_rules! test_indicator { 121 | ($i:tt) => { 122 | #[test] 123 | fn test_indicator() { 124 | let bar = Bar::new(); 125 | 126 | // ensure Default trait is implemented 127 | let mut indicator = $i::default(); 128 | 129 | // ensure Next is implemented 130 | let first_output = indicator.next(12.3); 131 | 132 | // ensure next accepts &DataItem as well 133 | indicator.next(&bar); 134 | 135 | // ensure Reset is implemented and works correctly 136 | indicator.reset(); 137 | assert_eq!(indicator.next(12.3), first_output); 138 | 139 | // ensure Display is implemented 140 | format!("{}", indicator); 141 | } 142 | }; 143 | } 144 | test_indicator!(FastStochastic); 145 | 146 | #[test] 147 | fn test_new() { 148 | assert!(FastStochastic::new(0).is_err()); 149 | assert!(FastStochastic::new(1).is_ok()); 150 | } 151 | 152 | #[test] 153 | fn test_next_with_f64() { 154 | let mut stoch = FastStochastic::new(3).unwrap(); 155 | assert_eq!(stoch.next(0.0), 50.0); 156 | assert_eq!(stoch.next(200.0), 100.0); 157 | assert_eq!(stoch.next(100.0), 50.0); 158 | assert_eq!(stoch.next(120.0), 20.0); 159 | assert_eq!(stoch.next(115.0), 75.0); 160 | } 161 | 162 | #[test] 163 | fn test_next_with_bars() { 164 | let test_data = vec![ 165 | // high, low , close, expected 166 | (20.0, 20.0, 20.0, 50.0), // min = 20, max = 20 167 | (30.0, 10.0, 25.0, 75.0), // min = 10, max = 30 168 | (40.0, 20.0, 16.0, 20.0), // min = 10, max = 40 169 | (35.0, 15.0, 19.0, 30.0), // min = 10, max = 40 170 | (30.0, 20.0, 25.0, 40.0), // min = 15, max = 40 171 | (35.0, 25.0, 30.0, 75.0), // min = 15, max = 35 172 | ]; 173 | 174 | let mut stoch = FastStochastic::new(3).unwrap(); 175 | 176 | for (high, low, close, expected) in test_data { 177 | let input_bar = Bar::new().high(high).low(low).close(close); 178 | assert_eq!(stoch.next(&input_bar), expected); 179 | } 180 | } 181 | 182 | #[test] 183 | fn test_reset() { 184 | let mut indicator = FastStochastic::new(10).unwrap(); 185 | assert_eq!(indicator.next(10.0), 50.0); 186 | assert_eq!(indicator.next(210.0), 100.0); 187 | assert_eq!(indicator.next(10.0), 0.0); 188 | assert_eq!(indicator.next(60.0), 25.0); 189 | 190 | indicator.reset(); 191 | assert_eq!(indicator.next(10.0), 50.0); 192 | assert_eq!(indicator.next(20.0), 100.0); 193 | assert_eq!(indicator.next(12.5), 25.0); 194 | } 195 | 196 | #[test] 197 | fn test_default() { 198 | FastStochastic::default(); 199 | } 200 | 201 | #[test] 202 | fn test_display() { 203 | let indicator = FastStochastic::new(21).unwrap(); 204 | assert_eq!(format!("{}", indicator), "FAST_STOCH(21)"); 205 | } 206 | } 207 | -------------------------------------------------------------------------------- /src/indicators/relative_strength_index.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::*; 4 | use crate::indicators::ExponentialMovingAverage as Ema; 5 | use crate::{Close, Next, Reset}; 6 | 7 | /// The relative strength index (RSI). 8 | /// 9 | /// It is a momentum oscillator, 10 | /// that compares the magnitude of recent gains 11 | /// and losses over a specified time period to measure speed and change of price 12 | /// movements of a security. It is primarily used to attempt to identify 13 | /// overbought or oversold conditions in the trading of an asset. 14 | /// 15 | /// The oscillator returns output in the range of 0..100. 16 | /// 17 | /// ![RSI](https://upload.wikimedia.org/wikipedia/commons/6/67/RSIwiki.gif) 18 | /// 19 | /// # Formula 20 | /// 21 | /// RSIt = EMAUt * 100 / (EMAUt + EMADt) 22 | /// 23 | /// Where: 24 | /// 25 | /// * RSIt - value of RSI indicator in a moment of time _t_ 26 | /// * EMAUt - value of [EMA](struct.ExponentialMovingAverage.html) of up periods in a moment of time _t_ 27 | /// * EMADt - value of [EMA](struct.ExponentialMovingAverage.html) of down periods in a moment of time _t_ 28 | /// 29 | /// If current period has value higher than previous period, than: 30 | /// 31 | /// U = pt - pt-1 32 | /// 33 | /// D = 0 34 | /// 35 | /// Otherwise: 36 | /// 37 | /// U = 0 38 | /// 39 | /// D = pt-1 - pt 40 | /// 41 | /// Where: 42 | /// 43 | /// * U = up period value 44 | /// * D = down period value 45 | /// * pt - input value in a moment of time _t_ 46 | /// * pt-1 - input value in a moment of time _t-1_ 47 | /// 48 | /// # Parameters 49 | /// 50 | /// * _n_ - number of periods (integer greater than 0). Default value is 14. 51 | /// 52 | /// # Example 53 | /// 54 | /// ``` 55 | /// use quantaxis_rs::indicators::RelativeStrengthIndex; 56 | /// use quantaxis_rs::Next; 57 | /// 58 | /// let mut rsi = RelativeStrengthIndex::new(3).unwrap(); 59 | /// assert_eq!(rsi.next(10.0), 50.0); 60 | /// assert_eq!(rsi.next(10.5).round(), 86.0); 61 | /// assert_eq!(rsi.next(10.0).round(), 35.0); 62 | /// assert_eq!(rsi.next(9.5).round(), 16.0); 63 | /// ``` 64 | /// 65 | /// # Links 66 | /// * [Relative strength index (Wikipedia)](https://en.wikipedia.org/wiki/Relative_strength_index) 67 | /// * [RSI (Investopedia)](http://www.investopedia.com/terms/r/rsi.asp) 68 | /// 69 | #[derive(Debug, Clone)] 70 | pub struct RelativeStrengthIndex { 71 | n: u32, 72 | up_ema_indicator: Ema, 73 | down_ema_indicator: Ema, 74 | prev_val: f64, 75 | is_new: bool, 76 | } 77 | 78 | impl RelativeStrengthIndex { 79 | pub fn new(n: u32) -> Result { 80 | let rsi = Self { 81 | n: n, 82 | up_ema_indicator: Ema::new(n)?, 83 | down_ema_indicator: Ema::new(n)?, 84 | prev_val: 0.0, 85 | is_new: true, 86 | }; 87 | Ok(rsi) 88 | } 89 | } 90 | 91 | impl Next for RelativeStrengthIndex { 92 | type Output = f64; 93 | 94 | fn next(&mut self, input: f64) -> Self::Output { 95 | let mut up = 0.0; 96 | let mut down = 0.0; 97 | 98 | if self.is_new { 99 | self.is_new = false; 100 | // Initialize with some small seed numbers to avoid division by zero 101 | up = 0.1; 102 | down = 0.1; 103 | } else { 104 | if input > self.prev_val { 105 | up = input - self.prev_val; 106 | } else { 107 | down = self.prev_val - input; 108 | } 109 | } 110 | 111 | self.prev_val = input; 112 | let up_ema = self.up_ema_indicator.next(up); 113 | let down_ema = self.down_ema_indicator.next(down); 114 | 100.0 * up_ema / (up_ema + down_ema) 115 | } 116 | } 117 | 118 | impl<'a, T: Close> Next<&'a T> for RelativeStrengthIndex { 119 | type Output = f64; 120 | 121 | fn next(&mut self, input: &'a T) -> Self::Output { 122 | self.next(input.close()) 123 | } 124 | } 125 | 126 | impl Reset for RelativeStrengthIndex { 127 | fn reset(&mut self) { 128 | self.is_new = true; 129 | self.prev_val = 0.0; 130 | self.up_ema_indicator.reset(); 131 | self.down_ema_indicator.reset(); 132 | } 133 | } 134 | 135 | impl Default for RelativeStrengthIndex { 136 | fn default() -> Self { 137 | Self::new(14).unwrap() 138 | } 139 | } 140 | 141 | impl fmt::Display for RelativeStrengthIndex { 142 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 143 | write!(f, "RSI({})", self.n) 144 | } 145 | } 146 | 147 | #[cfg(test)] 148 | mod tests { 149 | use super::*; 150 | use crate::test_helper::*; 151 | macro_rules! test_indicator { 152 | ($i:tt) => { 153 | #[test] 154 | fn test_indicator() { 155 | let bar = Bar::new(); 156 | 157 | // ensure Default trait is implemented 158 | let mut indicator = $i::default(); 159 | 160 | // ensure Next is implemented 161 | let first_output = indicator.next(12.3); 162 | 163 | // ensure next accepts &DataItem as well 164 | indicator.next(&bar); 165 | 166 | // ensure Reset is implemented and works correctly 167 | indicator.reset(); 168 | assert_eq!(indicator.next(12.3), first_output); 169 | 170 | // ensure Display is implemented 171 | format!("{}", indicator); 172 | } 173 | }; 174 | } 175 | test_indicator!(RelativeStrengthIndex); 176 | 177 | #[test] 178 | fn test_new() { 179 | assert!(RelativeStrengthIndex::new(0).is_err()); 180 | assert!(RelativeStrengthIndex::new(1).is_ok()); 181 | } 182 | 183 | #[test] 184 | fn test_next() { 185 | let mut rsi = RelativeStrengthIndex::new(3).unwrap(); 186 | assert_eq!(rsi.next(10.0), 50.0); 187 | assert_eq!(rsi.next(10.5).round(), 86.0); 188 | assert_eq!(rsi.next(10.0).round(), 35.0); 189 | assert_eq!(rsi.next(9.5).round(), 16.0); 190 | } 191 | 192 | #[test] 193 | fn test_reset() { 194 | let mut rsi = RelativeStrengthIndex::new(3).unwrap(); 195 | assert_eq!(rsi.next(10.0), 50.0); 196 | assert_eq!(rsi.next(10.5).round(), 86.0); 197 | 198 | rsi.reset(); 199 | assert_eq!(rsi.next(10.0).round(), 50.0); 200 | assert_eq!(rsi.next(10.5).round(), 86.0); 201 | } 202 | 203 | #[test] 204 | fn test_default() { 205 | RelativeStrengthIndex::default(); 206 | } 207 | 208 | #[test] 209 | fn test_display() { 210 | let rsi = RelativeStrengthIndex::new(16).unwrap(); 211 | assert_eq!(format!("{}", rsi), "RSI(16)"); 212 | } 213 | } 214 | -------------------------------------------------------------------------------- /src/indicators/moving_average_convergence_divergence.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::*; 4 | use crate::indicators::ExponentialMovingAverage as Ema; 5 | use crate::{Close, Next, Reset}; 6 | 7 | /// Moving average converge divergence (MACD). 8 | /// 9 | /// The MACD indicator (or "oscillator") is a collection of three time series 10 | /// calculated from historical price data, most often the closing price. 11 | /// These three series are: 12 | /// 13 | /// * The MACD series proper 14 | /// * The "signal" or "average" series 15 | /// * The "divergence" series which is the difference between the two 16 | /// 17 | /// The MACD series is the difference between a "fast" (short period) exponential 18 | /// moving average (EMA), and a "slow" (longer period) EMA of the price series. 19 | /// The average series is an EMA of the MACD series itself. 20 | /// 21 | /// # Formula 22 | /// 23 | /// # Parameters 24 | /// 25 | /// * _fast_length_ - length for the fast EMA. Default is 12. 26 | /// * _slow_length_ - length for the slow EMA. Default is 26. 27 | /// * _signal_length_ - length for the signal EMA. Default is 9. 28 | /// 29 | /// # Example 30 | /// 31 | /// ``` 32 | /// use quantaxis_rs::indicators::MovingAverageConvergenceDivergence as Macd; 33 | /// use quantaxis_rs::Next; 34 | /// 35 | /// let mut macd = Macd::new(3, 6, 4).unwrap(); 36 | /// 37 | /// assert_eq!(round(macd.next(2.0)), (0.0, 0.0, 0.0)); 38 | /// assert_eq!(round(macd.next(3.0)), (0.21, 0.09, 0.13)); 39 | /// assert_eq!(round(macd.next(4.2)), (0.52, 0.26, 0.26)); 40 | /// assert_eq!(round(macd.next(7.0)), (1.15, 0.62, 0.54)); 41 | /// assert_eq!(round(macd.next(6.7)), (1.15, 0.83, 0.32)); 42 | /// assert_eq!(round(macd.next(6.5)), (0.94, 0.87, 0.07)); 43 | /// 44 | /// fn round(nums: (f64, f64, f64)) -> (f64, f64, f64) { 45 | /// let n0 = (nums.0 * 100.0).round() / 100.0; 46 | /// let n1 = (nums.1 * 100.0).round() / 100.0; 47 | /// let n2 = (nums.2 * 100.0).round() / 100.0; 48 | /// (n0, n1, n2) 49 | /// } 50 | /// ``` 51 | #[derive(Debug, Clone)] 52 | pub struct MovingAverageConvergenceDivergence { 53 | fast_ema: Ema, 54 | slow_ema: Ema, 55 | signal_ema: Ema, 56 | } 57 | 58 | impl MovingAverageConvergenceDivergence { 59 | pub fn new(fast_length: u32, slow_length: u32, signal_length: u32) -> Result { 60 | let indicator = Self { 61 | fast_ema: Ema::new(fast_length)?, 62 | slow_ema: Ema::new(slow_length)?, 63 | signal_ema: Ema::new(signal_length)?, 64 | }; 65 | Ok(indicator) 66 | } 67 | } 68 | 69 | impl Next for MovingAverageConvergenceDivergence { 70 | type Output = (f64, f64, f64); 71 | 72 | fn next(&mut self, input: f64) -> Self::Output { 73 | let fast_val = self.fast_ema.next(input); 74 | let slow_val = self.slow_ema.next(input); 75 | 76 | let macd = fast_val - slow_val; 77 | let signal = self.signal_ema.next(macd); 78 | let histogram = macd - signal; 79 | 80 | (macd, signal, histogram) 81 | } 82 | } 83 | 84 | impl<'a, T: Close> Next<&'a T> for MovingAverageConvergenceDivergence { 85 | type Output = (f64, f64, f64); 86 | 87 | fn next(&mut self, input: &'a T) -> Self::Output { 88 | self.next(input.close()) 89 | } 90 | } 91 | 92 | impl Reset for MovingAverageConvergenceDivergence { 93 | fn reset(&mut self) { 94 | self.fast_ema.reset(); 95 | self.slow_ema.reset(); 96 | self.signal_ema.reset(); 97 | } 98 | } 99 | 100 | impl Default for MovingAverageConvergenceDivergence { 101 | fn default() -> Self { 102 | Self::new(12, 26, 9).unwrap() 103 | } 104 | } 105 | 106 | impl fmt::Display for MovingAverageConvergenceDivergence { 107 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 108 | write!( 109 | f, 110 | "MACD({}, {}, {})", 111 | self.fast_ema.length(), 112 | self.slow_ema.length(), 113 | self.signal_ema.length() 114 | ) 115 | } 116 | } 117 | 118 | #[cfg(test)] 119 | mod tests { 120 | use super::*; 121 | use crate::test_helper::*; 122 | type Macd = MovingAverageConvergenceDivergence; 123 | macro_rules! test_indicator { 124 | ($i:tt) => { 125 | #[test] 126 | fn test_indicator() { 127 | let bar = Bar::new(); 128 | 129 | // ensure Default trait is implemented 130 | let mut indicator = $i::default(); 131 | 132 | // ensure Next is implemented 133 | let first_output = indicator.next(12.3); 134 | 135 | // ensure next accepts &DataItem as well 136 | indicator.next(&bar); 137 | 138 | // ensure Reset is implemented and works correctly 139 | indicator.reset(); 140 | assert_eq!(indicator.next(12.3), first_output); 141 | 142 | // ensure Display is implemented 143 | format!("{}", indicator); 144 | } 145 | }; 146 | } 147 | test_indicator!(Macd); 148 | 149 | fn round(nums: (f64, f64, f64)) -> (f64, f64, f64) { 150 | let n0 = (nums.0 * 100.0).round() / 100.0; 151 | let n1 = (nums.1 * 100.0).round() / 100.0; 152 | let n2 = (nums.2 * 100.0).round() / 100.0; 153 | (n0, n1, n2) 154 | } 155 | 156 | #[test] 157 | fn test_new() { 158 | assert!(Macd::new(0, 1, 1).is_err()); 159 | assert!(Macd::new(1, 0, 1).is_err()); 160 | assert!(Macd::new(1, 1, 0).is_err()); 161 | assert!(Macd::new(1, 1, 1).is_ok()); 162 | } 163 | 164 | #[test] 165 | fn test_macd() { 166 | let mut macd = Macd::new(3, 6, 4).unwrap(); 167 | 168 | assert_eq!(round(macd.next(2.0)), (0.0, 0.0, 0.0)); 169 | assert_eq!(round(macd.next(3.0)), (0.21, 0.09, 0.13)); 170 | assert_eq!(round(macd.next(4.2)), (0.52, 0.26, 0.26)); 171 | assert_eq!(round(macd.next(7.0)), (1.15, 0.62, 0.54)); 172 | assert_eq!(round(macd.next(6.7)), (1.15, 0.83, 0.32)); 173 | assert_eq!(round(macd.next(6.5)), (0.94, 0.87, 0.07)); 174 | } 175 | 176 | #[test] 177 | fn test_reset() { 178 | let mut macd = Macd::new(3, 6, 4).unwrap(); 179 | 180 | assert_eq!(round(macd.next(2.0)), (0.0, 0.0, 0.0)); 181 | assert_eq!(round(macd.next(3.0)), (0.21, 0.09, 0.13)); 182 | 183 | macd.reset(); 184 | 185 | assert_eq!(round(macd.next(2.0)), (0.0, 0.0, 0.0)); 186 | assert_eq!(round(macd.next(3.0)), (0.21, 0.09, 0.13)); 187 | } 188 | 189 | #[test] 190 | fn test_default() { 191 | Macd::default(); 192 | } 193 | 194 | #[test] 195 | fn test_display() { 196 | let indicator = Macd::new(13, 30, 10).unwrap(); 197 | assert_eq!(format!("{}", indicator), "MACD(13, 30, 10)"); 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /src/indicators/true_range.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::helpers::max3; 4 | use crate::{Close, High, Low, Next, Reset, Update}; 5 | 6 | 7 | #[derive(Debug, Clone)] 8 | pub struct TrueRange { 9 | 10 | 11 | prev_closeque: Vec 12 | 13 | } 14 | 15 | impl TrueRange { 16 | pub fn new() -> Self { 17 | Self { prev_closeque:vec![] } 18 | } 19 | } 20 | 21 | impl Default for TrueRange { 22 | fn default() -> Self { 23 | Self::new() 24 | } 25 | } 26 | 27 | impl fmt::Display for TrueRange { 28 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 29 | write!(f, "TRUE_RANGE()") 30 | } 31 | } 32 | 33 | impl Next for TrueRange { 34 | type Output = f64; 35 | 36 | fn next(&mut self, input: f64) -> Self::Output { 37 | if self.prev_closeque.len()<1{ 38 | self.prev_closeque.push(input); 39 | 0.0 40 | }else { 41 | let distance = match self.prev_closeque[self.prev_closeque.len() - 1] { 42 | prev => (input - prev).abs(), 43 | _ => 0.0 44 | }; 45 | 46 | self.prev_closeque.push(input); 47 | distance 48 | } 49 | 50 | 51 | } 52 | } 53 | impl Update for TrueRange { 54 | type Output = f64; 55 | 56 | fn update(&mut self, input: f64) -> Self::Output { 57 | 58 | if self.prev_closeque.len()<2{ 59 | let u = self.prev_closeque.last_mut().unwrap(); 60 | *u = input; 61 | 0.0 62 | }else{ 63 | let distance = match self.prev_closeque[self.prev_closeque.len() -2]{ 64 | prev => (input - prev).abs(), 65 | _ => 0.0 66 | }; 67 | 68 | let u = self.prev_closeque.last_mut().unwrap(); 69 | *u = input; 70 | //self.prev_close = Some(input); 71 | distance 72 | } 73 | 74 | } 75 | } 76 | 77 | impl<'a, T: High + Low + Close> Next<&'a T> for TrueRange { 78 | type Output = f64; 79 | 80 | fn next(&mut self, bar: &'a T) -> Self::Output { 81 | if self.prev_closeque.len()<1{ 82 | self.prev_closeque.push(bar.close()); 83 | bar.high()- bar.low() 84 | }else{ 85 | let max_dist = match self.prev_closeque[self.prev_closeque.len() - 1] { 86 | prev_close => { 87 | let dist1 = bar.high() - bar.low(); 88 | let dist2 = (bar.high() - prev_close).abs(); 89 | let dist3 = (bar.low() - prev_close).abs(); 90 | max3(dist1, dist2, dist3) 91 | } 92 | _ => bar.high() - bar.low(), 93 | }; 94 | self.prev_closeque.push(bar.close()); 95 | max_dist 96 | } 97 | } 98 | } 99 | impl<'a, T: High + Low + Close> Update<&'a T> for TrueRange { 100 | type Output = f64; 101 | 102 | fn update(&mut self, bar: &'a T) -> Self::Output { 103 | if self.prev_closeque.len()<2{ 104 | let u = self.prev_closeque.last_mut().unwrap(); 105 | *u = bar.close(); 106 | bar.high() - bar.low() 107 | }else{ 108 | let max_dist = match self.prev_closeque[self.prev_closeque.len() -2] { 109 | prev_close => { 110 | let dist1 = bar.high() - bar.low(); 111 | let dist2 = (bar.high() - prev_close).abs(); 112 | let dist3 = (bar.low() - prev_close).abs(); 113 | max3(dist1, dist2, dist3) 114 | } 115 | _ => bar.high() - bar.low(), 116 | }; 117 | let u = self.prev_closeque.last_mut().unwrap(); 118 | *u = bar.close(); 119 | max_dist 120 | } 121 | 122 | } 123 | } 124 | 125 | impl Reset for TrueRange { 126 | fn reset(&mut self) { 127 | self.prev_closeque = vec![]; 128 | } 129 | } 130 | 131 | #[cfg(test)] 132 | mod tests { 133 | use super::*; 134 | use crate::test_helper::*; 135 | macro_rules! test_indicator { 136 | ($i:tt) => { 137 | #[test] 138 | fn test_indicator() { 139 | let bar = Bar::new(); 140 | 141 | // ensure Default trait is implemented 142 | let mut indicator = $i::default(); 143 | 144 | // ensure Next is implemented 145 | let first_output = indicator.next(12.3); 146 | 147 | // ensure next accepts &DataItem as well 148 | indicator.next(&bar); 149 | 150 | // ensure Reset is implemented and works correctly 151 | indicator.reset(); 152 | assert_eq!(indicator.next(12.3), first_output); 153 | 154 | // ensure Display is implemented 155 | format!("{}", indicator); 156 | } 157 | }; 158 | } 159 | test_indicator!(TrueRange); 160 | 161 | #[test] 162 | fn test_next_f64() { 163 | let mut tr = TrueRange::new(); 164 | assert_eq!(round(tr.next(2.5)), 0.0); 165 | assert_eq!(round(tr.next(3.6)), 1.1); 166 | assert_eq!(round(tr.next(3.3)), 0.3); 167 | } 168 | 169 | 170 | 171 | #[test] 172 | fn test_update_f64() { 173 | let mut tr = TrueRange::new(); 174 | assert_eq!(round(tr.next(2.5)), 0.0); 175 | 176 | assert_eq!(round(tr.update(3.3)), 0.0); 177 | 178 | 179 | let mut tr = TrueRange::new(); 180 | assert_eq!(round(tr.next(2.5)), 0.0); 181 | println!("{:#?}", tr); 182 | assert_eq!(round(tr.update(3.3)), 0.0); 183 | println!("{:#?}", tr); 184 | // println!("{:#?}",tr.next(2.5)); 185 | assert_eq!(round(tr.next(2.5)), 0.8); 186 | println!("{:#?}", tr); 187 | assert_eq!(round(tr.update(3.6)), 0.3); 188 | println!("{:#?}", tr); 189 | 190 | } 191 | 192 | #[test] 193 | fn test_next_bar() { 194 | let mut tr = TrueRange::new(); 195 | 196 | let bar1 = Bar::new().high(10).low(7.5).close(9); 197 | let bar2 = Bar::new().high(11).low(9).close(9.5); 198 | let bar3 = Bar::new().high(9).low(5).close(8); 199 | 200 | assert_eq!(tr.next(&bar1), 2.5); 201 | assert_eq!(tr.next(&bar2), 2.0); 202 | assert_eq!(tr.next(&bar3), 4.5); 203 | } 204 | 205 | #[test] 206 | fn test_reset() { 207 | let mut tr = TrueRange::new(); 208 | 209 | let bar1 = Bar::new().high(10).low(7.5).close(9); 210 | let bar2 = Bar::new().high(11).low(9).close(9.5); 211 | 212 | tr.next(&bar1); 213 | tr.next(&bar2); 214 | 215 | tr.reset(); 216 | let bar3 = Bar::new().high(60).low(15).close(51); 217 | assert_eq!(tr.next(&bar3), 45.0); 218 | } 219 | 220 | #[test] 221 | fn test_default() { 222 | TrueRange::default(); 223 | } 224 | 225 | #[test] 226 | fn test_display() { 227 | let indicator = TrueRange::new(); 228 | assert_eq!(format!("{}", indicator), "TRUE_RANGE()"); 229 | } 230 | } 231 | -------------------------------------------------------------------------------- /debug/t01b2.py: -------------------------------------------------------------------------------- 1 | import QUANTAXIS as QA 2 | import numpy as np 3 | from QAStrategy import QAStrategyCTABase 4 | 5 | 6 | class RBT01(QAStrategyCTABase): 7 | count1=0 8 | HAE=0 9 | LAE=0 10 | 11 | def calc_HHV(self,market_data, n): 12 | try: 13 | ind = QA.HHV(market_data.iloc[-n-1:]['high'],n) 14 | return ind.iloc[-2] 15 | except Exception as e: 16 | print(e) 17 | return np.nan 18 | 19 | def calc_LLV(self,market_data, n): 20 | try: 21 | ind = QA.LLV(market_data.iloc[-n-1:]['low'],n) 22 | return ind.iloc[-2] 23 | except Exception as e: 24 | print(e) 25 | return np.nan 26 | 27 | def on_bar(self, bar): 28 | #print(bar) 29 | mp = bar['close']* self.acc.market_preset.get_frozen(self.code)* self.acc.market_preset.get_unit(self.code) 30 | lots = int(300000/mp) -1 31 | 32 | priceoffset = 1 33 | lossP = 1.3 34 | K1 = 20 35 | K2 = 20 36 | n1 = 30 37 | TrailingStart1 = 90 38 | TrailingStop1 = 10 39 | 40 | bar_id = self.bar_id 41 | market_data = self.market_data 42 | market_datetime = self.market_datetime 43 | 44 | 45 | if len(market_data) > 2: 46 | print(market_data.index[-1]) 47 | bar['lastclose'] =market_data.iloc[-2]['close'] 48 | bar['lasthigh'] = market_data.iloc[-2]['high'] 49 | bar['lastlow'] = market_data.iloc[-2]['low'] 50 | # timec = (datetime.datetime.strptime(market_data[-1]['datetime'],'%Y-%m-%d %H:%M:%S')-datetime.datetime.strptime(market_data[-2]['datetime'],'%Y-%m-%d %H:%M:%S')).total_seconds()/3600 51 | hour = int(str(market_datetime[-1])[11:13]) 52 | 53 | HHV = self.calc_HHV(market_data, K1) 54 | print("HHV",HHV) 55 | LLV = self.calc_LLV(market_data, K2) 56 | print("LLV", LLV) 57 | #print(1) 58 | if self.positions.volume_long > 0 or self.positions.volume_short > 0: 59 | if bar_id-self.count1 == 1: 60 | self.HAE = bar['lasthigh'] 61 | self.LAE = bar['lastlow'] 62 | elif bar_id-self.count1 > 1: 63 | self.HAE = max(bar['lasthigh'],self.HAE) 64 | self.LAE = min(bar['lastlow'],self.LAE) 65 | 66 | CrossOver = bar['high'] > HHV and bar['lasthigh'] < HHV 67 | 68 | print("CrossOVer", CrossOver) 69 | CrossUnder = bar['low'] < LLV and bar['lastlow'] > LLV 70 | print("CrossUnder", CrossOver) 71 | #print(3) 72 | MA = QA.MA(market_data.iloc[-n1-5:-1]['open'],n1) 73 | 74 | print("MA", MA) 75 | 76 | 77 | cond1 = MA.iloc[-1]>MA.iloc[-2] and MA.iloc[-2]>MA.iloc[-3] and MA.iloc[-3]>MA.iloc[-4] and MA.iloc[-4]>MA.iloc[-5] 78 | cond2 = MA.iloc[-1]= 9 and hour <= 15: 84 | 85 | if CrossOver and cond1: 86 | self.send_order('BUY', 'OPEN', price=max(bar['open'], HHV) + priceoffset, volume= lots) 87 | self.count1 = bar_id 88 | self.HAE=0 89 | self.LAE=0 90 | print('BUY_OPEN') 91 | 92 | if CrossUnder and cond2: 93 | self.send_order('SELL', 'OPEN', price=min(bar['open'], LLV) - priceoffset, volume= lots) 94 | self.count1 = bar_id 95 | self.HAE=0 96 | self.LAE=0 97 | print('SELL_OPEN') 98 | #----------------------------------------------------------------------------------------------------------------------------------------------- 99 | 100 | elif self.positions.volume_long > 0 and self.positions.volume_short == 0: 101 | 102 | Stopline = round(self.positions.open_price_long*(100-lossP)/100,0) 103 | print('SELLCLOSE STOPLINE', Stopline) 104 | print('HAE', self.HAE) 105 | print('openprice', self.positions.open_price_long) 106 | print(self.HAE >= self.positions.open_price_long*(1+TrailingStart1/1000)) 107 | if (self.HAE >= self.positions.open_price_long*(1+TrailingStart1/1000) and bar_id-self.count1 >= 1): 108 | Stopline = self.HAE*(1-TrailingStop1/1000) 109 | print('SELLCLOSE STOPLINE222', Stopline) 110 | 111 | if CrossUnder and cond2: 112 | self.send_order('SELL', 'CLOSE', price=min(bar['open'], LLV) - priceoffset, volume= self.positions.volume_long) 113 | print('SELL_CLOSE') 114 | 115 | elif bar['low'] <= Stopline: 116 | self.send_order('SELL', 'CLOSE', price=min(bar['open'], Stopline) - priceoffset, volume= self.positions.volume_long) 117 | print('SELL_CLOSE_STOPLOSS') 118 | #----------------------------------------------------------------------------------------------------------------------------------------------- 119 | elif self.positions.volume_long == 0 and self.positions.volume_short > 0: 120 | 121 | Stopline = round(self.positions.open_price_short*(100+lossP)/100,0) 122 | if (self.LAE <= self.positions.open_price_short*(1-TrailingStart1/1000) and bar_id-self.count1 >= 1): 123 | Stopline = self.LAE*(1+TrailingStop1/1000) 124 | if CrossOver and cond1: 125 | self.send_order('BUY', 'CLOSE', price=max(bar['open'], HHV) + priceoffset, volume= self.positions.volume_short) 126 | print('BUY_CLOSE') 127 | 128 | elif bar['high'] >= Stopline: 129 | self.send_order('BUY', 'CLOSE', price=max(bar['open'], Stopline) + priceoffset, volume= self.positions.volume_short) 130 | print('BUY_CLOSE_STOPLOSS') 131 | #----------------------------------------------------------------------------------------------------------------------------------------------- 132 | if self.positions.volume_long == 0 and self.positions.volume_short == 0: 133 | self.count1 = 0 134 | self.HAE = 0 135 | self.LAE = 0 136 | # "ru2005", "sc2002" 137 | 138 | running ={} 139 | #'TAL8', 'ML8', 'RBL8', 'IL8', 'JL8', 'NIL8', 'ZNL8', 'CFL8', 'PL8', 'AL8', 'SML8', 'MAL8', 'SRL8', 'CUL8', 'VL8', 'HCL8', 'LL8', 'SFL8', 140 | 141 | #'URL8', 'RRL8', 'WHL8', 'APL8', 'ICL8', 'CJL8', 'FGL8', 'RML8', 'SSL8', 'CYL8', 'SPL8', 'CL8', 'JML8', 'BL8' 142 | #for item in [ 'BUL8', 'IHL8', 'AUL8', 'PPL8', 'AGL8', 'YL8', 'ZCL8', 'IFL8', 'OIL8', 'JDL8', 'RUL8', 'PBL8', 'EGL8', 'CSL8', 'ALL8', 'EBL8', ]: 143 | for item in ['RBL8']: 144 | running[item] = {} 145 | for frequence in ['15min']: 146 | running[item][frequence] = RBT01(code=item, frequence=frequence,portfolio= 'debugx', 147 | strategy_id='DETx5_{}_{}'.format(item, frequence), send_wx=True,start='2019-01-01', end= '2020-01-22') 148 | 149 | running[item][frequence].run_backtest() 150 | -------------------------------------------------------------------------------- /src/indicators/exponential_moving_average.rs: -------------------------------------------------------------------------------- 1 | use std::fmt; 2 | 3 | use crate::errors::*; 4 | use crate::{Close, Next, Reset, Update}; 5 | use std::f64::INFINITY; 6 | 7 | /// An exponential moving average (EMA), also known as an exponentially weighted moving average 8 | /// (EWMA). 9 | /// 10 | /// It is a type of infinite impulse response filter that applies weighting factors which decrease exponentially. 11 | /// The weighting for each older datum decreases exponentially, never reaching zero. 12 | /// 13 | /// # Formula 14 | /// 15 | /// ![EMA formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/05d06bdbee2c14031fd91ead6f5f772aec1ec964) 16 | /// 17 | /// Where: 18 | /// 19 | /// * _EMAt_ - is the value of the EMA at any time period _t_. 20 | /// * _EMAt-1_ - is the value of the EMA at the previous period _t-1_. 21 | /// * _pt_ - is the input value at a time period t. 22 | /// * _α_ - is the coefficient that represents the degree of weighting decrease, a constant smoothing factor between 0 and 1. 23 | /// 24 | /// _α_ is calculated with the following formula: 25 | /// 26 | /// ![alpha formula](https://wikimedia.org/api/rest_v1/media/math/render/svg/d9f6258e152db0644af548972bd6c50a8becf7ee) 27 | /// 28 | /// Where: 29 | /// 30 | /// * _length_ - number of periods 31 | /// 32 | /// # Parameters 33 | /// 34 | /// * _length_ - number of periods (integer greater than 0) 35 | /// 36 | /// # Example 37 | /// 38 | /// ``` 39 | /// use quantaxis_rs::indicators::ExponentialMovingAverage; 40 | /// use quantaxis_rs::Next; 41 | /// 42 | /// let mut ema = ExponentialMovingAverage::new(3).unwrap(); 43 | /// assert_eq!(ema.next(2.0), 2.0); 44 | /// assert_eq!(ema.next(5.0), 3.5); 45 | /// assert_eq!(ema.next(1.0), 2.25); 46 | /// assert_eq!(ema.next(6.25), 4.25); 47 | /// ``` 48 | /// 49 | /// # Links 50 | /// 51 | /// * [Exponential moving average, Wikipedia](https://en.wikipedia.org/wiki/Moving_average#Exponential_moving_average) 52 | /// 53 | 54 | #[derive(Debug, Clone)] 55 | pub struct ExponentialMovingAverage { 56 | length: u32, 57 | k: f64, 58 | current: f64, 59 | is_new: bool, 60 | pub cached: Vec 61 | } 62 | 63 | impl ExponentialMovingAverage { 64 | pub fn new(length: u32) -> Result { 65 | match length { 66 | 0 => Err(Error::from_kind(ErrorKind::InvalidParameter)), 67 | _ => { 68 | let k = 2f64 / (length as f64 + 1f64); 69 | let indicator = Self { 70 | length, 71 | k, 72 | current: 0f64, 73 | is_new: true, 74 | cached: vec![-INFINITY; length as usize] 75 | }; 76 | Ok(indicator) 77 | } 78 | } 79 | } 80 | 81 | pub fn length(&self) -> u32 { 82 | self.length 83 | } 84 | } 85 | 86 | impl Next for ExponentialMovingAverage { 87 | type Output = f64; 88 | 89 | fn next(&mut self, input: f64) -> Self::Output { 90 | 91 | if self.is_new { 92 | self.is_new = false; 93 | self.current = input; 94 | } else { 95 | self.current = self.k * input + (1.0 - self.k) * self.current; 96 | } 97 | 98 | self.cached.push(self.current.clone()); 99 | self.cached.remove(0); 100 | self.current 101 | } 102 | 103 | } 104 | 105 | impl Update for ExponentialMovingAverage { 106 | type Output = f64; 107 | 108 | fn update(&mut self, input: f64) -> Self::Output { 109 | if self.is_new { 110 | self.is_new = false; 111 | self.current = input; 112 | } else { 113 | self.current = self.k * input + (1.0 - self.k) * self.cached[(self.length -2) as usize]; 114 | } 115 | self.cached.remove((self.length - 1) as usize); 116 | self.cached.push(self.current.clone()); 117 | self.current 118 | } 119 | 120 | } 121 | 122 | impl<'a, T: Close> Next<&'a T> for ExponentialMovingAverage { 123 | type Output = f64; 124 | 125 | fn next(&mut self, input: &'a T) -> Self::Output { 126 | self.next(input.close()) 127 | } 128 | 129 | } 130 | 131 | 132 | impl<'a, T: Close> Update<&'a T> for ExponentialMovingAverage { 133 | type Output = f64; 134 | 135 | fn update(&mut self, input: &'a T) -> Self::Output { 136 | self.update(input.close()) 137 | } 138 | 139 | } 140 | 141 | impl Reset for ExponentialMovingAverage { 142 | fn reset(&mut self) { 143 | self.current = 0.0; 144 | self.is_new = true; 145 | } 146 | } 147 | 148 | impl Default for ExponentialMovingAverage { 149 | fn default() -> Self { 150 | Self::new(9).unwrap() 151 | } 152 | } 153 | 154 | impl fmt::Display for ExponentialMovingAverage { 155 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 156 | write!(f, "EMA({})", self.length) 157 | } 158 | } 159 | 160 | #[cfg(test)] 161 | mod tests { 162 | use super::*; 163 | use crate::test_helper::*; 164 | macro_rules! test_indicator { 165 | ($i:tt) => { 166 | #[test] 167 | fn test_indicator() { 168 | let bar = Bar::new(); 169 | 170 | // ensure Default trait is implemented 171 | let mut indicator = $i::default(); 172 | 173 | // ensure Next is implemented 174 | let first_output = indicator.next(12.3); 175 | 176 | // ensure next accepts &DataItem as well 177 | indicator.next(&bar); 178 | 179 | // ensure Reset is implemented and works correctly 180 | indicator.reset(); 181 | assert_eq!(indicator.next(12.3), first_output); 182 | 183 | // ensure Display is implemented 184 | format!("{}", indicator); 185 | } 186 | }; 187 | } 188 | 189 | test_indicator!(ExponentialMovingAverage); 190 | 191 | #[test] 192 | fn test_new() { 193 | assert!(ExponentialMovingAverage::new(0).is_err()); 194 | assert!(ExponentialMovingAverage::new(1).is_ok()); 195 | } 196 | 197 | #[test] 198 | fn test_next() { 199 | let mut ema = ExponentialMovingAverage::new(2).unwrap(); 200 | 201 | assert_eq!(ema.next(2.0), 2.0); 202 | assert_eq!(ema.next(5.0), 4.0); 203 | // assert_eq!(ema.next(1.0), 2.25); 204 | // assert_eq!(ema.next(6.25), 4.875); 205 | 206 | let mut ema = ExponentialMovingAverage::new(3).unwrap(); 207 | let bar1 = Bar::new().close(2); 208 | let bar2 = Bar::new().close(5); 209 | assert_eq!(ema.next(&bar1), 2.0); 210 | assert_eq!(ema.next(&bar2), 3.5); 211 | } 212 | 213 | #[test] 214 | fn test_update() { 215 | let mut ema = ExponentialMovingAverage::new(3).unwrap(); 216 | 217 | assert_eq!(ema.next(2.0), 2.0); 218 | assert_eq!(ema.next(5.0), 3.5); 219 | assert_eq!(ema.next(1.0), 2.25); 220 | assert_eq!(ema.update(6.25), 4.875); 221 | 222 | // let mut ema = ExponentialMovingAverage::new(3).unwrap(); 223 | // let bar1 = Bar::new().close(2); 224 | // let bar2 = Bar::new().close(5); 225 | // assert_eq!(ema.next(&bar1), 2.0); 226 | // assert_eq!(ema.next(&bar2), 3.5); 227 | } 228 | 229 | 230 | 231 | #[test] 232 | fn test_reset() { 233 | let mut ema = ExponentialMovingAverage::new(5).unwrap(); 234 | 235 | assert_eq!(ema.next(4.0), 4.0); 236 | ema.next(10.0); 237 | ema.next(15.0); 238 | ema.next(20.0); 239 | assert_ne!(ema.next(4.0), 4.0); 240 | 241 | ema.reset(); 242 | assert_eq!(ema.next(4.0), 4.0); 243 | } 244 | 245 | #[test] 246 | fn test_default() { 247 | ExponentialMovingAverage::default(); 248 | } 249 | 250 | #[test] 251 | fn test_display() { 252 | let ema = ExponentialMovingAverage::new(7).unwrap(); 253 | assert_eq!(format!("{}", ema), "EMA(7)"); 254 | } 255 | } 256 | -------------------------------------------------------------------------------- /examples/t01b2.rs: -------------------------------------------------------------------------------- 1 | extern crate csv; 2 | extern crate ndarray; 3 | extern crate ndarray_csv; 4 | extern crate num_traits; 5 | extern crate serde; 6 | extern crate stopwatch; 7 | 8 | use std::borrow::BorrowMut; 9 | use std::cmp::{max, min}; 10 | use std::f64; 11 | use std::io; 12 | 13 | use ndarray::{array, stack}; 14 | use stopwatch::Stopwatch; 15 | 16 | use quantaxis_rs::{ 17 | indicators, Next, qaaccount, qadata, qafetch, qaindicator, qaposition, transaction, 18 | }; 19 | use quantaxis_rs::indicators::{ 20 | BollingerBands, EfficiencyRatio, ExponentialMovingAverage, FastStochastic, HHV, LLV, 21 | Maximum, Minimum, MoneyFlowIndex, MovingAverage, 22 | MovingAverageConvergenceDivergence, OnBalanceVolume, RateOfChange, RelativeStrengthIndex, SimpleMovingAverage, 23 | SlowStochastic, StandardDeviation, TrueRange, 24 | }; 25 | use quantaxis_rs::qaaccount::QA_Account; 26 | use quantaxis_rs::qaposition::QA_Postions; 27 | 28 | trait FloatIterExt { 29 | fn float_min(&mut self) -> f64; 30 | fn float_max(&mut self) -> f64; 31 | } 32 | 33 | impl FloatIterExt for T 34 | where 35 | T: Iterator, 36 | { 37 | fn float_max(&mut self) -> f64 { 38 | self.fold(f64::NAN, f64::max) 39 | } 40 | 41 | fn float_min(&mut self) -> f64 { 42 | self.fold(f64::NAN, f64::min) 43 | } 44 | } 45 | 46 | fn compare_max(a: f64, b: f64) -> f64 { 47 | if a >= b { 48 | a 49 | } else { 50 | b 51 | } 52 | } 53 | 54 | fn compare_min(a: f64, b: f64) -> f64 { 55 | if a >= b { 56 | b 57 | } else { 58 | a 59 | } 60 | } 61 | 62 | pub fn backtest() -> QA_Account { 63 | let priceoffset = 2; 64 | let lossP = 1.3; 65 | let K1: usize = 20; 66 | let K2: usize = 20; 67 | let n1: usize = 30; 68 | let mut bar_id = 0; 69 | let mut count1 = 0; 70 | let mut HAE: f64 = 0 as f64; 71 | let mut LAE: f64 = 0 as f64; 72 | let TrailingStart1 = 90.0; 73 | let TrailingStop1 = 10.0; 74 | let mut acc = qaaccount::QA_Account::new( 75 | "RustT01B2_RBL8", 76 | "test", 77 | "admin", 78 | 1000000.0, 79 | false, 80 | "backtest", 81 | ); 82 | acc.init_h("RBL8"); 83 | let mut llv_i = LLV::new(K1 as u32).unwrap(); 84 | let mut hhv_i = HHV::new(K2 as u32).unwrap(); 85 | let mut ma = MovingAverage::new(n1 as u32).unwrap(); 86 | let mut rdr = csv::Reader::from_reader(io::stdin()); 87 | let mut lastbar = qafetch::BAR { 88 | code: "".to_string(), 89 | datetime: "".to_string(), 90 | open: 0.0, 91 | high: 0.0, 92 | low: 0.0, 93 | close: 0.0, 94 | volume: 0.0, 95 | }; 96 | for result in rdr.deserialize() { 97 | let bar: qafetch::BAR = result.unwrap(); 98 | bar_id += 1; 99 | let hour = &bar.datetime[11..13]; 100 | let hour_i32 = hour.parse::().unwrap(); 101 | let ind_llv = llv_i.next(bar.low); 102 | let ind_hhv = hhv_i.next(bar.high); 103 | let ind_ma = ma.next(bar.open); 104 | let crossOver = bar.high > hhv_i.cached[K1 - 2] && lastbar.high < hhv_i.cached[K1 - 2]; 105 | 106 | let crossUnder = bar.low < llv_i.cached[K2 - 2] && lastbar.low > llv_i.cached[K2 - 2]; 107 | 108 | let cond1 = ma.cached[n1 - 2] > ma.cached[n1 - 3] 109 | && ma.cached[n1 - 3] > ma.cached[n1 - 4] 110 | && ma.cached[n1 - 4] > ma.cached[n1 - 5] 111 | && ma.cached[n1 - 5] > ma.cached[n1 - 6]; 112 | 113 | let cond2 = ma.cached[n1 - 2] < ma.cached[n1 - 3] 114 | && ma.cached[n1 - 3] < ma.cached[n1 - 4] 115 | && ma.cached[n1 - 4] < ma.cached[n1 - 5] 116 | && ma.cached[n1 - 5] < ma.cached[n1 - 6]; 117 | 118 | let code = bar.code.as_ref(); 119 | 120 | let long_pos = acc.get_volume_long(code); 121 | let short_pos = acc.get_volume_short(code); 122 | if long_pos > 0.0 || short_pos > 0.0 { 123 | if bar_id - count1 == 1 { 124 | HAE = lastbar.high; 125 | LAE = lastbar.low; 126 | } else if bar_id - count1 > 1 { 127 | HAE = compare_max(HAE, lastbar.high); 128 | LAE = compare_min(LAE, lastbar.low); 129 | } 130 | } 131 | 132 | if long_pos == 0.0 && short_pos == 0.0 && hour_i32 < 21 && hour_i32 >= 9 { 133 | if crossOver && cond1 { 134 | println!("BUY OPEN"); 135 | acc.buy_open( 136 | bar.code.as_ref(), 137 | 90.0, 138 | bar.datetime.as_ref(), 139 | compare_max(bar.open, hhv_i.cached[K1 - 2]), 140 | ); 141 | count1 = bar_id; 142 | HAE = 0.0; 143 | LAE = 0.0; 144 | } else if crossUnder && cond2 { 145 | println!("SELL OPEN"); 146 | acc.sell_open( 147 | bar.code.as_ref(), 148 | 90.0, 149 | bar.datetime.as_ref(), 150 | compare_min(bar.open, llv_i.cached[K2 - 2]), 151 | ); 152 | count1 = bar_id; 153 | HAE = 0.0; 154 | LAE = 0.0; 155 | } 156 | } 157 | if long_pos > 0.0 && short_pos == 0.0 { 158 | //println!("当前多单持仓"); 159 | 160 | let mut stopLine: f64 = acc.get_open_price_long(code) * (100.0 - lossP) / 100.0; 161 | if HAE >= (acc.get_open_price_long(code) * (1.0 + TrailingStart1 / 1000.0)) 162 | && bar_id - count1 >= 1 163 | { 164 | //println!("CHANGE STOPLINE"); 165 | stopLine = (HAE * (1.0 - TrailingStop1 / 1000.0)); 166 | } 167 | 168 | if (crossUnder && cond2) { 169 | //println!("CORSSUNDER_SELLCLOSE"); 170 | println!("SELL CLOSE"); 171 | acc.sell_close( 172 | code, 173 | 90.0, 174 | bar.datetime.as_ref(), 175 | compare_min(bar.open, llv_i.cached[K2 - 2]), 176 | ); 177 | } else if (bar.low < stopLine) { 178 | //println!("LOW UNDER_SELLCLOSE"); 179 | println!("SELL CLOSE FORCE"); 180 | acc.sell_close( 181 | code, 182 | 90.0, 183 | bar.datetime.as_ref(), 184 | compare_min(bar.open, stopLine), 185 | ); 186 | } 187 | } 188 | if (short_pos > 0.0 && long_pos == 0.0) { 189 | //println!("当前空单持仓 {:#?}", acc.get_position_short(code)); 190 | let mut stopLine: f64 = acc.get_open_price_short(code) * (100.0 + lossP) / 100.0; 191 | 192 | if (LAE >= (acc.get_open_price_short(code) * (1.0 - TrailingStart1 / 1000.0) as f64) 193 | && bar_id - count1 >= 1) 194 | { 195 | stopLine = (LAE * (1.0 + TrailingStop1 / 1000.0)); 196 | } 197 | if crossOver && cond1 { 198 | println!("BUY CLOSE"); 199 | acc.buy_close( 200 | code, 201 | 90.0, 202 | bar.datetime.as_ref(), 203 | compare_max(bar.open, hhv_i.cached[K1 - 2]), 204 | ); 205 | } else if (bar.high >= stopLine) { 206 | println!("BUY CLOSE Force"); 207 | acc.buy_close( 208 | code, 209 | 90.0, 210 | bar.datetime.as_ref(), 211 | compare_max(bar.open, stopLine), 212 | ); 213 | } 214 | } 215 | if (short_pos == 0.0 && long_pos == 0.0) { 216 | count1 = bar_id; 217 | HAE = 0.0; 218 | LAE = 0.0; 219 | } 220 | 221 | lastbar = bar; 222 | } 223 | 224 | //qaaccount::QA_Account::history_table(&mut acc); 225 | 226 | acc 227 | } 228 | 229 | fn main() { 230 | let sw = Stopwatch::start_new(); 231 | let acc = backtest(); 232 | println!("LAST MONEY {:?}", acc.money); 233 | println!("{:?}", acc.cash); 234 | //println!("{:?}", acc.frozen); 235 | acc.to_csv(); 236 | println!("It took {0:.8} ms", sw.elapsed_ms()); 237 | } 238 | -------------------------------------------------------------------------------- /examples/t01b2D.rs: -------------------------------------------------------------------------------- 1 | extern crate csv; 2 | extern crate ndarray; 3 | extern crate ndarray_csv; 4 | extern crate num_traits; 5 | extern crate serde; 6 | extern crate stopwatch; 7 | 8 | use std::borrow::BorrowMut; 9 | use std::cmp::{max, min}; 10 | use std::f64; 11 | use std::io; 12 | 13 | use ndarray::{array, stack}; 14 | use stopwatch::Stopwatch; 15 | 16 | use quantaxis_rs::{ 17 | indicators, Next, qaaccount, qadata, qafetch, qaindicator, qaposition, transaction, 18 | }; 19 | use quantaxis_rs::indicators::{ 20 | BollingerBands, EfficiencyRatio, ExponentialMovingAverage, FastStochastic, HHV, LLV, 21 | Maximum, Minimum, MoneyFlowIndex, MovingAverage, 22 | MovingAverageConvergenceDivergence, OnBalanceVolume, RateOfChange, RelativeStrengthIndex, SimpleMovingAverage, 23 | SlowStochastic, StandardDeviation, TrueRange, 24 | }; 25 | use quantaxis_rs::qaaccount::QA_Account; 26 | use quantaxis_rs::qaposition::QA_Postions; 27 | 28 | trait FloatIterExt { 29 | fn float_min(&mut self) -> f64; 30 | fn float_max(&mut self) -> f64; 31 | } 32 | 33 | impl FloatIterExt for T 34 | where 35 | T: Iterator, 36 | { 37 | fn float_min(&mut self) -> f64 { 38 | self.fold(f64::NAN, f64::min) 39 | } 40 | 41 | fn float_max(&mut self) -> f64 { 42 | self.fold(f64::NAN, f64::max) 43 | } 44 | } 45 | 46 | fn compare_max(a: f64, b: f64) -> f64 { 47 | if a >= b { 48 | a 49 | } else { 50 | b 51 | } 52 | } 53 | 54 | fn compare_min(a: f64, b: f64) -> f64 { 55 | if a >= b { 56 | b 57 | } else { 58 | a 59 | } 60 | } 61 | 62 | pub fn backtest() -> QA_Account { 63 | let priceoffset = 1; 64 | let lossP = 1.3; 65 | let K1: usize = 20; 66 | let K2: usize = 20; 67 | let n1: usize = 30; 68 | 69 | let count1 = 0; 70 | let mut HAE: f64 = 0 as f64; 71 | let mut LAE: f64 = 0 as f64; 72 | let TrailingStart1 = 90.0; 73 | let TrailingStop1 = 10.0; 74 | let mut acc = qaaccount::QA_Account::new( 75 | "RustT01B2_RBL8", 76 | "test", 77 | "admin", 78 | 100000.0, 79 | false, 80 | "backtest", 81 | ); 82 | acc.init_h("RBL8"); 83 | let mut llv_i = LLV::new(K1 as u32).unwrap(); 84 | let mut hhv_i = HHV::new(K2 as u32).unwrap(); 85 | let mut ma = MovingAverage::new(n1 as u32).unwrap(); 86 | let mut rdr = csv::Reader::from_reader(io::stdin()); 87 | let mut lastbar = qafetch::BAR { 88 | code: "".to_string(), 89 | datetime: "".to_string(), 90 | open: 0.0, 91 | high: 0.0, 92 | low: 0.0, 93 | close: 0.0, 94 | volume: 0.0, 95 | }; 96 | for result in rdr.deserialize() { 97 | let bar: qafetch::BAR = result.unwrap(); 98 | let hour = &bar.datetime[11..13]; 99 | 100 | let hour_i32 = hour.parse::().unwrap(); 101 | println!("hour ==== {:#?}", hour); 102 | println!(" this is datetime {:#?}", bar.datetime); 103 | let ind_llv = llv_i.next(bar.low); 104 | let ind_hhv = hhv_i.next(bar.high); 105 | let ind_ma = ma.next(bar.open); 106 | 107 | let crossOver = bar.high > hhv_i.cached[K1 - 2] && lastbar.high < hhv_i.cached[K1 - 2]; 108 | println!("CrossOVER: {:#?}", crossOver); 109 | 110 | let crossUnder = bar.low < llv_i.cached[K2 - 2] && lastbar.low > llv_i.cached[K2 - 2]; 111 | println!("CrossUnder: {:#?}", crossUnder); 112 | 113 | let cond1 = ma.cached[n1 - 2] > ma.cached[n1 - 3] 114 | && ma.cached[n1 - 3] > ma.cached[n1 - 4] 115 | && ma.cached[n1 - 4] > ma.cached[n1 - 5] 116 | && ma.cached[n1 - 5] > ma.cached[n1 - 6]; 117 | 118 | let cond2 = ma.cached[n1 - 2] < ma.cached[n1 - 3] 119 | && ma.cached[n1 - 3] < ma.cached[n1 - 4] 120 | && ma.cached[n1 - 4] < ma.cached[n1 - 5] 121 | && ma.cached[n1 - 5] < ma.cached[n1 - 6]; 122 | 123 | let code = bar.code.as_ref(); 124 | 125 | let long_pos = acc.get_volume_long(code); 126 | let short_pos = acc.get_volume_short(code); 127 | if long_pos > 0.0 || short_pos > 0.0 { 128 | if HAE == 0.0 { 129 | HAE = lastbar.high; 130 | LAE = lastbar.low; 131 | } else { 132 | HAE = compare_max(HAE, lastbar.high); 133 | LAE = compare_min(LAE, lastbar.low); 134 | } 135 | } 136 | 137 | if long_pos == 0.0 && short_pos == 0.0 && hour_i32 < 21 && hour_i32 >= 9 { 138 | if crossOver && cond1 { 139 | acc.buy_open( 140 | bar.code.as_ref(), 141 | 10.0, 142 | bar.datetime.as_ref(), 143 | compare_max(bar.open, hhv_i.cached[K1 - 2]), 144 | ); 145 | } else if crossUnder && cond2 { 146 | acc.sell_open( 147 | bar.code.as_ref(), 148 | 10.0, 149 | bar.datetime.as_ref(), 150 | compare_min(bar.open, llv_i.cached[K2 - 2]), 151 | ); 152 | } 153 | } 154 | if long_pos > 0.0 && short_pos == 0.0 { 155 | //println!("当前多单持仓"); 156 | 157 | let mut stopLine: f64 = acc.get_open_price_long(code) * (100.0 - lossP) / 100.0; 158 | println!("RAW STOPLINE {:#?}", stopLine); 159 | println!("openprice {:#?}", acc.get_open_price_long(code)); 160 | println!("HAE {:#?}", HAE); 161 | if HAE >= (acc.get_open_price_long(code) * (1.0 + TrailingStart1 / 1000.0)) { 162 | //println!("CHANGE STOPLINE"); 163 | stopLine = (HAE * (1.0 - TrailingStop1 / 1000.0)); 164 | } 165 | println!("NEW STOPLINE {:#?}", stopLine); 166 | if crossUnder && cond2 { 167 | //println!("CORSSUNDER_SELLCLOSE"); 168 | acc.sell_close( 169 | code, 170 | 10.0, 171 | bar.datetime.as_ref(), 172 | compare_min(bar.open, llv_i.cached[K2 - 2]), 173 | ); 174 | } else if bar.low < stopLine { 175 | //println!("LOW UNDER_SELLCLOSE"); 176 | acc.sell_close( 177 | code, 178 | 10.0, 179 | bar.datetime.as_ref(), 180 | compare_min(bar.open, stopLine), 181 | ); 182 | } 183 | } 184 | if short_pos > 0.0 && long_pos == 0.0 { 185 | //println!("当前空单持仓 {:#?}", acc.get_position_short(code)); 186 | let mut stopLine: f64 = acc.get_open_price_short(code) * (100.0 + lossP) / 100.0; 187 | println!("RAW STOPLINE {:#?}", stopLine); 188 | println!("openprice {:#?}", acc.get_open_price_short(code)); 189 | println!("LAE {:#?}", LAE); 190 | if LAE >= (acc.get_open_price_short(code) * (1.0 - TrailingStart1 / 1000.0) as f64) { 191 | stopLine = (LAE * (1.0 + TrailingStop1 / 1000.0)); 192 | } 193 | println!("NEW STOPLINE {:#?}", stopLine); 194 | if crossOver && cond1 { 195 | acc.buy_close( 196 | code, 197 | 10.0, 198 | bar.datetime.as_ref(), 199 | compare_max(bar.open, hhv_i.cached[K1 - 2]), 200 | ); 201 | } 202 | if bar.high >= stopLine { 203 | println!("BUYCLOSE FORCE {:#?} {:#?}", stopLine, bar.high); 204 | println!("CurrentBar {:#?}", bar); 205 | acc.buy_close( 206 | code, 207 | 10.0, 208 | bar.datetime.as_ref(), 209 | compare_max(bar.open, stopLine), 210 | ); 211 | } 212 | } 213 | 214 | lastbar = bar; 215 | } 216 | //println!("{:?}", acc.history_table()); 217 | 218 | //qaaccount::QA_Account::history_table(&mut acc); 219 | 220 | acc 221 | } 222 | 223 | fn main() { 224 | let sw = Stopwatch::start_new(); 225 | let acc = backtest(); 226 | //let file = File::open("data15.csv").unwrap(); 227 | 228 | //println!("{:?}", acc.history_table()); 229 | acc.to_csv(); 230 | println!("It took {0:.8} ms", sw.elapsed_ms()); 231 | } 232 | -------------------------------------------------------------------------------- /src/indicators/llv.rs: -------------------------------------------------------------------------------- 1 | use std::f64::INFINITY; 2 | use std::fmt; 3 | 4 | use crate::{Low, Next, Reset, Update}; 5 | use crate::errors::*; 6 | 7 | /// Returns the lowest value in a given time frame. 8 | /// 9 | /// # Parameters 10 | /// 11 | /// * _n_ - size of the time frame (integer greater than 0). Default value is 14. 12 | /// 13 | /// # Example 14 | /// 15 | /// ``` 16 | /// use quantaxis_rs::indicators::LLV; 17 | /// use quantaxis_rs::Next; 18 | /// 19 | /// let mut min = LLV::new(3).unwrap(); 20 | /// assert_eq!(min.next(10.0), 10.0); 21 | /// assert_eq!(min.next(11.0), 10.0); 22 | /// assert_eq!(min.next(12.0), 10.0); 23 | /// assert_eq!(min.next(13.0), 11.0); 24 | /// ``` 25 | 26 | #[derive(Debug, Clone)] 27 | pub struct LLV { 28 | n: usize, 29 | vec: Vec, 30 | min_index: usize, 31 | cur_index: usize, 32 | pub cached: Vec 33 | } 34 | 35 | impl LLV { 36 | pub fn new(n: u32) -> Result { 37 | let n = n as usize; 38 | 39 | if n <= 0 { 40 | return Err(Error::from_kind(ErrorKind::InvalidParameter)); 41 | } 42 | 43 | let indicator = Self { 44 | n: n, 45 | vec: vec![INFINITY; n], 46 | min_index: 0, 47 | cur_index: 0, 48 | cached: vec![INFINITY; n], 49 | }; 50 | 51 | Ok(indicator) 52 | } 53 | pub fn new_init(n: u32, vec: Vec) -> Result { 54 | let n = n as usize; 55 | 56 | if n <= 0 { 57 | return Err(Error::from_kind(ErrorKind::InvalidParameter)); 58 | } 59 | 60 | let mut indicator = Self { 61 | n: n, 62 | vec: vec![INFINITY; n], 63 | min_index: 0, 64 | cur_index: 0, 65 | cached: vec![INFINITY; n], 66 | }; 67 | for data in vec{ 68 | indicator.next(data as f64); 69 | } 70 | Ok(indicator) 71 | } 72 | 73 | fn find_min_index(&self) -> usize { 74 | let mut min = ::std::f64::INFINITY; 75 | let mut index: usize = 0; 76 | 77 | for (i, &val) in self.vec.iter().enumerate() { 78 | if val < min { 79 | min = val; 80 | index = i; 81 | } 82 | } 83 | 84 | index 85 | } 86 | } 87 | 88 | impl Next for LLV { 89 | type Output = f64; 90 | 91 | fn next(&mut self, input: f64) -> Self::Output { 92 | self.cur_index = (self.cur_index + 1) % (self.n as usize); 93 | self.vec[self.cur_index] = input; 94 | 95 | if input < self.vec[self.min_index] { 96 | self.min_index = self.cur_index; 97 | } else if self.min_index == self.cur_index { 98 | self.min_index = self.find_min_index(); 99 | } 100 | self.cached.push(self.vec[self.min_index]); 101 | self.cached.remove(0); 102 | self.vec[self.min_index] 103 | } 104 | } 105 | 106 | 107 | impl Update for LLV { 108 | type Output = f64; 109 | 110 | fn update(&mut self, input: f64) -> Self::Output { 111 | self.vec[self.cur_index] = input; 112 | 113 | if input < self.vec[self.min_index] { 114 | self.min_index = self.cur_index; 115 | } else if self.min_index == self.cur_index { 116 | self.min_index = self.find_min_index(); 117 | } 118 | self.cached.remove(self.n - 1); 119 | self.cached.push(self.vec[self.min_index]); 120 | 121 | self.vec[self.min_index] 122 | } 123 | } 124 | 125 | impl<'a, T: Low> Next<&'a T> for LLV { 126 | type Output = f64; 127 | 128 | fn next(&mut self, input: &'a T) -> Self::Output { 129 | self.next(input.low()) 130 | } 131 | } 132 | 133 | impl Reset for LLV { 134 | fn reset(&mut self) { 135 | for i in 0..self.n { 136 | self.vec[i] = INFINITY; 137 | } 138 | } 139 | } 140 | 141 | impl Default for LLV { 142 | fn default() -> Self { 143 | Self::new(14).unwrap() 144 | } 145 | } 146 | 147 | impl fmt::Display for LLV { 148 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 149 | write!(f, "MIN({})", self.n) 150 | } 151 | } 152 | 153 | #[cfg(test)] 154 | mod tests { 155 | use crate::test_helper::*; 156 | 157 | use super::*; 158 | 159 | macro_rules! test_indicator { 160 | ($i:tt) => { 161 | #[test] 162 | fn test_indicator() { 163 | let bar = Bar::new(); 164 | 165 | // ensure Default trait is implemented 166 | let mut indicator = $i::default(); 167 | 168 | // ensure Next is implemented 169 | let first_output = indicator.next(12.3); 170 | 171 | // ensure next accepts &DataItem as well 172 | indicator.next(&bar); 173 | 174 | // ensure Reset is implemented and works correctly 175 | indicator.reset(); 176 | assert_eq!(indicator.next(12.3), first_output); 177 | 178 | // ensure Display is implemented 179 | format!("{}", indicator); 180 | } 181 | }; 182 | } 183 | test_indicator!(LLV); 184 | 185 | #[test] 186 | fn test_new() { 187 | assert!(LLV::new(0).is_err()); 188 | assert!(LLV::new(1).is_ok()); 189 | } 190 | 191 | #[test] 192 | fn test_next() { 193 | let mut min = LLV::new(3).unwrap(); 194 | 195 | assert_eq!(min.next(4.0), 4.0); 196 | assert_eq!(min.next(1.2), 1.2); 197 | assert_eq!(min.next(5.0), 1.2); 198 | assert_eq!(min.next(3.0), 1.2); 199 | assert_eq!(min.next(4.0), 3.0); 200 | assert_eq!(min.next(6.0), 3.0); 201 | assert_eq!(min.next(7.0), 4.0); 202 | assert_eq!(min.next(8.0), 6.0); 203 | assert_eq!(min.next(-9.0), -9.0); 204 | assert_eq!(min.next(0.0), -9.0); 205 | } 206 | 207 | #[test] 208 | fn test_update() { 209 | let mut min = LLV::new(3).unwrap(); 210 | 211 | assert_eq!(min.next(4.0), 4.0); 212 | assert_eq!(min.next(1.2), 1.2); 213 | assert_eq!(min.next(5.0), 1.2); 214 | assert_eq!(min.next(3.0), 1.2); 215 | assert_eq!(min.next(2.0), 2.0); 216 | assert_eq!(min.update(2.5), 2.5); 217 | assert_eq!(min.next(2.2), 2.2); 218 | } 219 | 220 | 221 | #[test] 222 | fn test_next_with_bars() { 223 | fn bar(low: f64) -> Bar { 224 | Bar::new().low(low) 225 | } 226 | 227 | let mut LLV_Indicator = LLV::new(3).unwrap(); 228 | println!("{:#?}", LLV_Indicator); 229 | assert_eq!(LLV_Indicator.next(&bar(4.0)), 4.0); 230 | assert_eq!(LLV_Indicator.next(&bar(4.0)), 4.0); 231 | println!("{:#?}", LLV_Indicator); 232 | assert_eq!(LLV_Indicator.next(&bar(1.2)), 1.2); 233 | assert_eq!(LLV_Indicator.next(&bar(5.0)), 1.2); 234 | println!("{:#?}", LLV_Indicator); 235 | } 236 | 237 | #[test] 238 | fn test_reset() { 239 | let mut min = LLV::new(10).unwrap(); 240 | 241 | assert_eq!(min.next(5.0), 5.0); 242 | assert_eq!(min.next(7.0), 5.0); 243 | 244 | min.reset(); 245 | assert_eq!(min.next(8.0), 8.0); 246 | } 247 | 248 | #[test] 249 | fn test_newx() { 250 | let mut LLV_Indicator = LLV::new_init(2, vec![3.0,4.0,3.0]).unwrap(); 251 | println!("{:#?}", LLV_Indicator); 252 | assert_eq!(LLV_Indicator.next(5.0), 3.0); 253 | println!("{:#?}", LLV_Indicator); 254 | assert_eq!(LLV_Indicator.next(7.0), 5.0); 255 | println!("{:#?}", LLV_Indicator); 256 | LLV_Indicator.reset(); 257 | assert_eq!(LLV_Indicator.next(8.0), 8.0); 258 | } 259 | 260 | #[test] 261 | fn test_new_notenough() { 262 | let mut LLV_Indicator = LLV::new_init(4, vec![4.0,6.0,3.0]).unwrap(); 263 | println!("{:#?}", LLV_Indicator); 264 | assert_eq!(LLV_Indicator.next(5.0), 3.0); 265 | println!("{:#?}", LLV_Indicator); 266 | assert_eq!(LLV_Indicator.next(2.0), 2.0); 267 | println!("{:#?}", LLV_Indicator); 268 | LLV_Indicator.reset(); 269 | assert_eq!(LLV_Indicator.next(8.0), 8.0); 270 | } 271 | #[test] 272 | fn test_default() { 273 | LLV::default(); 274 | } 275 | 276 | #[test] 277 | fn test_display() { 278 | let indicator = LLV::new(10).unwrap(); 279 | println!("{}", indicator); 280 | assert_eq!(format!("{}", indicator), "MIN(10)"); 281 | } 282 | } 283 | -------------------------------------------------------------------------------- /src/indicators/moving_average.rs: -------------------------------------------------------------------------------- 1 | use std::f64::NAN; 2 | use std::fmt; 3 | 4 | use crate::{Close, Next, Reset, Update}; 5 | use crate::errors::*; 6 | 7 | /// moving average (ma). 8 | /// 9 | /// # Formula 10 | /// 11 | /// ![ma](https://wikimedia.org/api/rest_v1/media/math/render/svg/e2bf09dc6deaf86b3607040585fac6078f9c7c89) 12 | /// 13 | /// Where: 14 | /// 15 | /// * _MAt_ - value of moving average at a point of time _t_ 16 | /// * _n_ - number of periods (length) 17 | /// * _pt_ - input value at a point of time _t_ 18 | /// 19 | /// # Parameters 20 | /// 21 | /// * _n_ - number of periods (integer greater than 0) 22 | /// 23 | /// # Example 24 | /// 25 | /// ``` 26 | /// use quantaxis_rs::indicators::MovingAverage; 27 | /// use quantaxis_rs::Next; 28 | /// 29 | /// let mut ma = MovingAverage::new(3).unwrap(); 30 | /// assert_eq!(ma.next(10.0), 0.0); 31 | /// assert_eq!(ma.next(11.0), 0.0); 32 | /// assert_eq!(ma.next(12.0), 11.0); 33 | /// assert_eq!(ma.next(13.0), 12.0); 34 | /// ``` 35 | /// 36 | #[derive(Debug, Clone)] 37 | pub struct MovingAverage { 38 | pub(crate) n: u32, 39 | index: usize, 40 | count: u32, 41 | sum: f64, 42 | vec: Vec, 43 | pub cached: Vec 44 | } 45 | 46 | impl MovingAverage { 47 | pub fn new(n: u32) -> Result { 48 | match n { 49 | 0 => Err(Error::from_kind(ErrorKind::InvalidParameter)), 50 | _ => { 51 | let indicator = Self { 52 | n, 53 | index: 0, 54 | count: 1, 55 | sum: 0.0, 56 | vec: vec![0.0; n as usize], 57 | cached: vec![0.0; n as usize], 58 | }; 59 | Ok(indicator) 60 | } 61 | } 62 | } 63 | 64 | pub fn is_real(&self)->bool{ 65 | self.count-1 >= self.n 66 | } 67 | } 68 | 69 | impl Next for MovingAverage { 70 | type Output = f64; 71 | 72 | fn next(&mut self, input: f64) -> Self::Output { 73 | self.index = (self.index + 1) % (self.n as usize); 74 | 75 | let old_val = self.vec[self.index]; 76 | self.vec[self.index] = input; 77 | let mut res = 0.0; 78 | 79 | if self.count < self.n { 80 | self.sum = self.sum - old_val + input; 81 | } else { 82 | self.sum = self.sum - old_val + input; 83 | res = self.sum / (self.n as f64); 84 | } 85 | self.count += 1; 86 | self.cached.push(res); 87 | self.cached.remove(0); 88 | res 89 | } 90 | } 91 | 92 | 93 | impl<'a, T: Close> Next<&'a T> for MovingAverage { 94 | type Output = f64; 95 | 96 | fn next(&mut self, input: &'a T) -> Self::Output { 97 | self.next(input.close()) 98 | } 99 | } 100 | 101 | impl Update for MovingAverage { 102 | type Output = f64; 103 | 104 | fn update(&mut self, input: f64) -> Self::Output { 105 | //self.index = (self.index + 1) % (self.n as usize); 106 | 107 | let old_val = self.vec[self.index]; 108 | self.vec[self.index] = input; 109 | let mut res = 0.0; 110 | self.count -= 1; 111 | if self.count < self.n { 112 | self.sum = self.sum - old_val + input; 113 | } else { 114 | self.sum = self.sum - old_val + input; 115 | res = self.sum / (self.n as f64); 116 | } 117 | self.count += 1; 118 | //println!("VEC {:#?} index {}, count {}", self.vec, self.index, self.count); 119 | self.cached.remove((self.n - 1) as usize); 120 | self.cached.push(res); 121 | res 122 | } 123 | } 124 | 125 | 126 | impl<'a, T: Close> Update<&'a T> for MovingAverage { 127 | type Output = f64; 128 | 129 | fn update(&mut self, input: &'a T) -> Self::Output { 130 | self.update(input.close()) 131 | } 132 | } 133 | 134 | 135 | impl Reset for MovingAverage { 136 | fn reset(&mut self) { 137 | self.index = 0; 138 | self.count = 0; 139 | self.sum = 0.0; 140 | for i in 0..(self.n as usize) { 141 | self.vec[i] = 0.0; 142 | self.cached[i] = 0.0; 143 | } 144 | 145 | } 146 | } 147 | 148 | impl Default for MovingAverage { 149 | fn default() -> Self { 150 | Self::new(9).unwrap() 151 | } 152 | } 153 | 154 | impl fmt::Display for MovingAverage { 155 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 156 | write!(f, "ma({})", self.n) 157 | } 158 | } 159 | 160 | #[cfg(test)] 161 | mod tests { 162 | use crate::test_helper::*; 163 | 164 | use super::*; 165 | 166 | macro_rules! test_indicator { 167 | ($i:tt) => { 168 | #[test] 169 | fn test_indicator() { 170 | let bar = Bar::new(); 171 | 172 | // ensure Default trait is implemented 173 | let mut indicator = $i::default(); 174 | 175 | // ensure Next is implemented 176 | let first_output = indicator.next(12.3); 177 | 178 | // ensure next accepts &DataItem as well 179 | indicator.next(&bar); 180 | 181 | // ensure Reset is implemented and works correctly 182 | indicator.reset(); 183 | assert_eq!(indicator.next(12.3), first_output); 184 | 185 | // ensure Display is implemented 186 | format!("{}", indicator); 187 | } 188 | }; 189 | } 190 | test_indicator!(MovingAverage); 191 | 192 | #[test] 193 | fn test_new() { 194 | assert!(MovingAverage::new(0).is_err()); 195 | assert!(MovingAverage::new(1).is_ok()); 196 | } 197 | 198 | #[test] 199 | fn test_next() { 200 | let mut ma = MovingAverage::new(4).unwrap(); 201 | assert_eq!(ma.next(4.0), 0.0); 202 | assert_eq!(ma.next(5.0), 0.0); 203 | assert_eq!(ma.next(6.0), 0.0); 204 | assert_eq!(ma.next(6.0), 5.25); 205 | assert_eq!(ma.next(6.0), 5.75); 206 | assert_eq!(ma.next(6.0), 6.0); 207 | assert_eq!(ma.next(2.0), 5.0); 208 | } 209 | 210 | #[test] 211 | fn test_real() { 212 | let mut ma = MovingAverage::new(4).unwrap(); 213 | ma.next(1.0); 214 | assert_eq!(ma.is_real(), false); 215 | ma.next(2.0); 216 | assert_eq!(ma.is_real(), false); 217 | ma.next(3.0); 218 | assert_eq!(ma.is_real(), false); 219 | ma.next(4.0); 220 | assert_eq!(ma.is_real(), true); 221 | 222 | 223 | } 224 | 225 | #[test] 226 | fn test_update() { 227 | let mut ma = MovingAverage::new(2).unwrap(); 228 | assert_eq!(ma.next(5.0), 0.0); 229 | assert_eq!(ma.update(6.0), 0.0); 230 | assert_eq!(ma.next(7.0), 6.5); 231 | assert_eq!(ma.update(8.0), 7.0); 232 | assert_eq!(ma.next(9.0), 8.5); 233 | } 234 | 235 | 236 | #[test] 237 | fn test_cached() { 238 | let mut ma = MovingAverage::new(2).unwrap(); 239 | assert_eq!(ma.next(4.0), 0.0); 240 | assert_eq!(ma.next(5.0), 4.5); 241 | assert_eq!(ma.next(6.0), 5.5); 242 | println!("{:#?}", ma.cached); 243 | assert_eq!(ma.next(6.0), 6.0); 244 | println!("{:#?}", ma.cached); 245 | assert_eq!(ma.next(6.0), 6.0); 246 | println!("{:#?}", ma.cached); 247 | assert_eq!(ma.next(6.0), 6.0); 248 | println!("{:#?}", ma.cached); 249 | assert_eq!(ma.next(2.0), 4.0); 250 | println!("{:#?}", ma.cached); 251 | } 252 | 253 | #[test] 254 | fn test_next_with_bars() { 255 | fn bar(close: f64) -> Bar { 256 | Bar::new().close(close) 257 | } 258 | 259 | let mut ma = MovingAverage::new(3).unwrap(); 260 | assert_eq!(ma.next(&bar(4.0)), 0.0); 261 | assert_eq!(ma.next(&bar(4.0)), 0.0); 262 | assert_eq!(ma.next(&bar(7.0)), 5.0); 263 | assert_eq!(ma.next(&bar(1.0)), 4.0); 264 | } 265 | 266 | #[test] 267 | fn test_reset() { 268 | let mut ma = MovingAverage::new(4).unwrap(); 269 | assert_eq!(ma.next(4.0), 0.0); 270 | assert_eq!(ma.next(5.0), 0.0); 271 | assert_eq!(ma.next(6.0), 0.0); 272 | assert_eq!(ma.next(5.0), 5.0); 273 | ma.reset(); 274 | assert_eq!(ma.next(99.0), 0.0); 275 | } 276 | 277 | #[test] 278 | fn test_default() { 279 | MovingAverage::default(); 280 | } 281 | 282 | #[test] 283 | fn test_display() { 284 | let ma = MovingAverage::new(5).unwrap(); 285 | assert_eq!(format!("{}", ma), "ma(5)"); 286 | } 287 | } 288 | -------------------------------------------------------------------------------- /src/indicators/money_flow_index.rs: -------------------------------------------------------------------------------- 1 | use std::collections::VecDeque; 2 | use std::fmt; 3 | 4 | use crate::errors::*; 5 | use crate::{Close, High, Low, Next, Reset, Volume}; 6 | 7 | /// Money Flow Index (MFI). 8 | /// 9 | /// The MFI is an volume and price based oscillator which gives moneyflow over n periods. 10 | /// MFI is used to measure buying and selling pressure. 11 | /// MFI is also known as volume-weighted RSI. 12 | /// 13 | /// # Formula 14 | /// 15 | /// Typical Price(TP) = (High + Low + Close)/3 16 | /// 17 | /// Money Flow(MF) = Typical Price x Volume 18 | /// 19 | /// MF is positive when currennt TP is greater that previous period TP and 20 | /// negative when current TP is less than preivous TP. 21 | /// 22 | /// Positive money flow (PMF)- calculated by adding the money flow of all the days RMF is positive. 23 | /// 24 | /// Negative money flow (NMF)- calculated by adding the money flow of all the days RMF is negative. 25 | /// 26 | /// Money Flow Index(MFI) = PMF / (PMF + NMF) * 100 27 | /// 28 | /// 29 | /// # Parameters 30 | /// 31 | /// * _n_ - number of periods, integer greater than 0 32 | /// 33 | /// # Example 34 | /// 35 | /// ``` 36 | /// use quantaxis_rs::indicators::MoneyFlowIndex; 37 | /// use quantaxis_rs::{Next, DataItem}; 38 | /// 39 | /// let mut mfi = MoneyFlowIndex::new(3).unwrap(); 40 | /// let di = DataItem::builder() 41 | /// .high(3.0) 42 | /// .low(1.0) 43 | /// .close(2.0) 44 | /// .open(1.5) 45 | /// .volume(1000.0) 46 | /// .build().unwrap(); 47 | /// mfi.next(&di); 48 | /// 49 | /// ``` 50 | /// # Links 51 | /// * [Money Flow Index, Wikipedia](https://en.wikipedia.org/wiki/Money_flow_index) 52 | /// * [Money Flow Index, stockcharts](https://stockcharts.com/school/doku.php?id=chart_school:technical_indicators:money_flow_index_mfi) 53 | 54 | #[derive(Debug, Clone)] 55 | pub struct MoneyFlowIndex { 56 | n: u32, 57 | money_flows: VecDeque, 58 | prev_typical_price: f64, 59 | total_positive_money_flow: f64, 60 | total_absolute_money_flow: f64, 61 | is_new: bool, 62 | } 63 | 64 | impl MoneyFlowIndex { 65 | pub fn new(n: u32) -> Result { 66 | match n { 67 | 0 => Err(Error::from_kind(ErrorKind::InvalidParameter)), 68 | _ => { 69 | let indicator = Self { 70 | n: n, 71 | money_flows: VecDeque::with_capacity(n as usize + 1), 72 | prev_typical_price: 0.0, 73 | total_positive_money_flow: 0.0, 74 | total_absolute_money_flow: 0.0, 75 | is_new: true, 76 | }; 77 | Ok(indicator) 78 | } 79 | } 80 | } 81 | } 82 | 83 | impl<'a, T: High + Low + Close + Volume> Next<&'a T> for MoneyFlowIndex { 84 | type Output = f64; 85 | 86 | fn next(&mut self, input: &'a T) -> f64 { 87 | let typical_price = (input.high() + input.low() + input.close()) / 3.0; 88 | 89 | if self.is_new { 90 | // money flow is 0, because without having previous typical_price 91 | // it is not possible to determine is it positive or negative. 92 | self.money_flows.push_back(0.0); 93 | self.prev_typical_price = typical_price; 94 | self.is_new = false; 95 | return 50.0; 96 | } else { 97 | let money_flow = typical_price * input.volume(); 98 | 99 | let signed_money_flow = if typical_price >= self.prev_typical_price { 100 | self.total_positive_money_flow += money_flow; 101 | money_flow 102 | } else { 103 | -money_flow 104 | }; 105 | 106 | self.total_absolute_money_flow += money_flow; 107 | 108 | if self.money_flows.len() == (self.n as usize) { 109 | let old_signed_money_flow = self.money_flows.pop_front().unwrap(); 110 | if old_signed_money_flow > 0.0 { 111 | self.total_positive_money_flow -= old_signed_money_flow; 112 | self.total_absolute_money_flow -= old_signed_money_flow; 113 | } else { 114 | // it is actually subtraction, because old_signed_money_flow is negative 115 | self.total_absolute_money_flow += old_signed_money_flow; 116 | } 117 | } 118 | 119 | self.money_flows.push_back(signed_money_flow); 120 | self.prev_typical_price = typical_price; 121 | 122 | (self.total_positive_money_flow / self.total_absolute_money_flow) * 100.0 123 | } 124 | } 125 | } 126 | 127 | impl Default for MoneyFlowIndex { 128 | fn default() -> Self { 129 | Self::new(14).unwrap() 130 | } 131 | } 132 | 133 | impl fmt::Display for MoneyFlowIndex { 134 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 135 | write!(f, "MFI({})", self.n) 136 | } 137 | } 138 | 139 | impl Reset for MoneyFlowIndex { 140 | fn reset(&mut self) { 141 | self.money_flows.clear(); 142 | self.prev_typical_price = 0.0; 143 | self.total_positive_money_flow = 0.0; 144 | self.total_absolute_money_flow = 0.0; 145 | self.is_new = true; 146 | } 147 | } 148 | 149 | #[cfg(test)] 150 | mod tests { 151 | use super::*; 152 | use crate::test_helper::*; 153 | macro_rules! test_indicator { 154 | ($i:tt) => { 155 | #[test] 156 | fn test_indicator() { 157 | let bar = Bar::new(); 158 | 159 | // ensure Default trait is implemented 160 | let mut indicator = $i::default(); 161 | 162 | // ensure Next is implemented 163 | let first_output = indicator.next(12.3); 164 | 165 | // ensure next accepts &DataItem as well 166 | indicator.next(&bar); 167 | 168 | // ensure Reset is implemented and works correctly 169 | indicator.reset(); 170 | assert_eq!(indicator.next(12.3), first_output); 171 | 172 | // ensure Display is implemented 173 | format!("{}", indicator); 174 | } 175 | }; 176 | } 177 | #[test] 178 | fn test_new() { 179 | assert!(MoneyFlowIndex::new(0).is_err()); 180 | assert!(MoneyFlowIndex::new(1).is_ok()); 181 | } 182 | 183 | #[test] 184 | fn test_next_bar() { 185 | let mut mfi = MoneyFlowIndex::new(3).unwrap(); 186 | 187 | // tp = 2.0 188 | let bar1 = Bar::new().high(3).low(1).close(2).volume(500.0); 189 | assert_eq!(mfi.next(&bar1), 50.0); 190 | 191 | // tp = 2.2, fm = 2.2*1000 = 2200, abs_total = 2200, pos_total = 2200 192 | let bar2 = Bar::new().high(2.3).low(2.0).close(2.3).volume(1000.0); 193 | assert_eq!(mfi.next(&bar2), 100.0); 194 | 195 | // tp = 8.0, fm = 8*200 = 1600, abs_total = 3800, pos_total = 3800 196 | let bar3 = Bar::new().high(9).low(7).close(8).volume(200.0); 197 | assert_eq!(mfi.next(&bar3), 100.0); 198 | 199 | // tp = 4.0, fm = -4.0*500 = -2000, abs_total = 5800 , pos_total = 3800 200 | let bar4 = Bar::new().high(5).low(3).close(4).volume(500.0); 201 | assert_eq!(mfi.next(&bar4), 3800.0 / 5800.0 * 100.0); 202 | 203 | // tp = 3.0, fm = -3 * 5000 = -15000, abs_total = 5800+15000-2200=18600, pos_total=3800-2200=1600 204 | let bar5 = Bar::new().high(4).low(2).close(3).volume(5000.0); 205 | assert_eq!(mfi.next(&bar5), 1600.0 / 18600.0 * 100.0); 206 | 207 | // tp = 1.5, fm = -1.5*6000= -9000, abs_total=18600+9000-1600=26000, pos_total=0 208 | let bar6 = Bar::new().high(2).low(1).close(1.5).volume(6000.0); 209 | assert_eq!(mfi.next(&bar6), 0.0 / 23800.0 * 100.0); 210 | 211 | // tp = 2, fm = 2*7000=14000, abs_total=26000+14000-2000=38000, pos_total=14000 212 | let bar7 = Bar::new().high(2).low(2).close(2).volume(7000.0); 213 | assert_eq!(mfi.next(&bar7), 14000.0 / 38000.0 * 100.0); 214 | } 215 | 216 | #[test] 217 | fn test_reset() { 218 | let mut mfi = MoneyFlowIndex::new(3).unwrap(); 219 | 220 | let bar1 = Bar::new().high(2).low(1).close(1.5).volume(1000.0); 221 | let bar2 = Bar::new().high(5).low(3).close(4).volume(2000.0); 222 | let bar3 = Bar::new().high(9).low(7).close(8).volume(3000.0); 223 | let bar4 = Bar::new().high(5).low(3).close(4).volume(4000.0); 224 | let bar5 = Bar::new().high(5).low(3).close(4).volume(5000.0); 225 | let bar6 = Bar::new().high(2).low(1).close(1.5).volume(6000.0); 226 | 227 | assert_eq!(mfi.next(&bar1), 50.0); 228 | assert_eq!(mfi.next(&bar2), 100.0); 229 | assert_eq!(mfi.next(&bar3), 100.0); 230 | assert_eq!(round(mfi.next(&bar4)), 66.667); 231 | assert_eq!(round(mfi.next(&bar5)), 73.333); 232 | assert_eq!(round(mfi.next(&bar6)), 44.444); 233 | 234 | mfi.reset(); 235 | 236 | assert_eq!(mfi.next(&bar1), 50.0); 237 | assert_eq!(mfi.next(&bar2), 100.0); 238 | assert_eq!(mfi.next(&bar3), 100.0); 239 | assert_eq!(round(mfi.next(&bar4)), 66.667); 240 | assert_eq!(round(mfi.next(&bar5)), 73.333); 241 | assert_eq!(round(mfi.next(&bar6)), 44.444); 242 | } 243 | 244 | #[test] 245 | fn test_default() { 246 | MoneyFlowIndex::default(); 247 | } 248 | 249 | #[test] 250 | fn test_display() { 251 | let mfi = MoneyFlowIndex::new(10).unwrap(); 252 | assert_eq!(format!("{}", mfi), "MFI(10)"); 253 | } 254 | 255 | } 256 | -------------------------------------------------------------------------------- /src/qaindicator.rs: -------------------------------------------------------------------------------- 1 | 2 | // use std::f64::NAN; 3 | 4 | 5 | // use ndarray; 6 | // use ndarray::prelude::*; 7 | // use std::iter::FromIterator; 8 | 9 | 10 | // #[derive(Debug)] 11 | // pub enum Err { 12 | // NotEnoughtData, // not enought data to compute requested values 13 | // } 14 | 15 | 16 | // pub fn round_array(array: &mut [f64], decimals: u8) { 17 | // let divider = (10.0 as f64).powi(decimals as i32); 18 | // for number in array { 19 | // *number = (*number * divider).round() / divider; 20 | // } 21 | // } 22 | 23 | 24 | 25 | 26 | // //pub fn HHV(data: &[f64], period: usize) -> Result, Err> { 27 | // // if length <= 0 { 28 | // // return Err(Err::NotEnoughtData); 29 | // // } 30 | // // 31 | // // let highest = source.get(0); 32 | // // 33 | // // if highest.is_none() { 34 | // // return Err(Err::NotEnoughtData); 35 | // // } 36 | // // 37 | // // let mut highest = highest.unwrap(); 38 | // // 39 | // // for index in 1..(length) { 40 | // // match source.get(index) { 41 | // // None => {} 42 | // // Some(maybe_highest) => { 43 | // // if maybe_highest > highest { 44 | // // highest = maybe_highest; 45 | // // } 46 | // // } 47 | // // } 48 | // // } 49 | // // 50 | // // Ok(highest) 51 | // //} 52 | // // 53 | 54 | // pub fn ema(data: &[f64], period: usize) -> Result, Err> { 55 | // if period > data.len() { 56 | // return Err(Err::NotEnoughtData); 57 | // } 58 | // let mut ema = Vec::new(); 59 | // let mut j = 1; 60 | 61 | // // get period sma first and calculate the next period period ema 62 | // let sma = (data[0..period]) 63 | // .iter() 64 | // .sum::() / period as f64; 65 | // let multiplier: f64 = 2.0 / (1.0 + period as f64); 66 | // ema.push(sma); 67 | 68 | // // EMA(current) = ( (Price(current) - EMA(prev) ) x Multiplier) + EMA(prev) 69 | // ema.push(((data[period] - sma) * multiplier) + sma); 70 | 71 | // // now calculate the rest of the values 72 | // for i in &data[period + 1..data.len()] { 73 | // println!("{:#?}", i); 74 | // let tmp = ((*i - ema[j]) * multiplier) + ema[j]; 75 | // j = j + 1; 76 | // ema.push(tmp); 77 | // } 78 | 79 | // Ok(ema) 80 | // } 81 | 82 | // pub fn sma(data: &[f64], period: usize) -> Result, Err> { 83 | // if period > data.len() { 84 | // return Err(Err::NotEnoughtData); 85 | // } 86 | 87 | // let mut result = Vec::new(); 88 | // let mut running_total = 0.0; 89 | 90 | // for i in 0..data.len() { 91 | // running_total += data[i]; 92 | // if i >= period { 93 | // running_total -= data[i - period]; 94 | // } 95 | // if i >= period - 1 { 96 | // result.push(running_total / period as f64); 97 | // } 98 | // } 99 | // Ok(result) 100 | // } 101 | 102 | 103 | // pub fn psar(high: &[f64], low: &[f64], iaf: f64, maxaf: f64) -> Result, Err> { 104 | // let mut psar = vec![NAN; high.len()]; 105 | 106 | // if high.len() < 2 { 107 | // return Err(Err::NotEnoughtData); 108 | // }; 109 | 110 | // let mut long = false; 111 | // if high[0] + low[0] <= high[1] + low[1] { 112 | // long = true; 113 | // } 114 | 115 | // let mut sar; 116 | // let mut extreme; 117 | 118 | // if long { 119 | // extreme = high[0]; 120 | // sar = low[0]; 121 | // } else { 122 | // extreme = low[0]; 123 | // sar = high[0]; 124 | // } 125 | 126 | // psar[0] = sar; 127 | 128 | // let mut af = iaf; 129 | 130 | // for i in 1..high.len() { 131 | // sar = (extreme - sar) * af + sar; 132 | 133 | // if long { 134 | // if i >= 2 && (sar > low[i - 2]) { 135 | // sar = low[i - 2] 136 | // }; 137 | // if sar > low[i - 1] { 138 | // sar = low[i - 1] 139 | // }; 140 | 141 | // if af < maxaf && high[i] > extreme { 142 | // af += iaf; 143 | // if af > maxaf { 144 | // af = maxaf 145 | // }; 146 | // } 147 | 148 | // if high[i] > extreme { 149 | // extreme = high[i]; 150 | // } 151 | // } else { 152 | // if i >= 2 && sar < high[i - 2] { 153 | // sar = high[i - 2] 154 | // }; 155 | // if sar < high[i - 1] { 156 | // sar = high[i - 1] 157 | // }; 158 | 159 | // if af < maxaf && low[i] < extreme { 160 | // af += iaf; 161 | // if af > maxaf { 162 | // af = maxaf 163 | // }; 164 | // } 165 | 166 | // if low[i] < extreme { 167 | // extreme = low[i] 168 | // }; 169 | // } 170 | 171 | // if long && low[i] < sar || !long && high[i] > sar { 172 | // af = iaf; 173 | // sar = extreme; 174 | 175 | // long = !long; 176 | 177 | // if !long { 178 | // extreme = low[i]; 179 | // } else { 180 | // extreme = high[i]; 181 | // } 182 | // } 183 | 184 | // psar[i] = sar; 185 | // } 186 | 187 | // Ok(psar) 188 | // } 189 | 190 | 191 | // pub fn rsi(data: &[f64], period: usize) -> Result, Err> { 192 | // if period > data.len() { 193 | // return Err(Err::NotEnoughtData); 194 | // } 195 | 196 | // let mut changes = Vec::new(); 197 | // for i in 0..data.len() - 1 { 198 | // let change = data[i + 1] - data[i]; 199 | // changes.push(change); 200 | // } 201 | 202 | // let rsi_range = data.len() - period; 203 | 204 | // let mut rsis = vec![NAN ; rsi_range]; 205 | 206 | // // gains & losses 207 | // let mut gains = Vec::new(); 208 | // let mut losses = Vec::new(); 209 | 210 | // for i in 0..changes.len() { 211 | // if changes[i] > 0.0 { 212 | // gains.push(changes[i]); 213 | // losses.push(0.0); 214 | // } else if changes[i] < 0.0 { 215 | // losses.push(changes[i] * -1.0); 216 | // gains.push(0.0); 217 | // } else { 218 | // gains.push(0.0); 219 | // losses.push(0.0); 220 | // } 221 | // } 222 | 223 | // let mut avg_gain: f64 = gains[..period].iter().sum::() / gains[..period].len() as f64; 224 | // let mut avg_loss: f64 = losses[..period].iter().sum::() / losses[..period].len() as f64; 225 | 226 | // if avg_loss == 0.0 { 227 | // rsis[0] = 100.0; 228 | // } else { 229 | // let rs = avg_gain / avg_loss; 230 | // rsis[0] = 100.0 - (100.0 / (1.0 + rs)); 231 | // } 232 | 233 | // for i in 1..rsi_range { 234 | // avg_gain = (avg_gain * (period - 1) as f64 + gains[i + (period - 1)]) / period as f64; 235 | // avg_loss = (avg_loss * (period - 1) as f64 + losses[i + (period - 1)]) / period as f64; 236 | 237 | // if avg_loss == 0.0 { 238 | // rsis[i] = 100.0; 239 | // } else { 240 | // let rs = avg_gain / avg_loss; 241 | // rsis[i] = 100.0 - (100.0 / (1.0 + rs)); 242 | // } 243 | // } 244 | 245 | // Ok(rsis) 246 | // } 247 | 248 | 249 | 250 | 251 | // // Some randomly generated data to test against TA-Lib (see generate_data.py & correct_values.py) 252 | // const OPEN: &[f64] = &[1984.03, 1959.83, 2041.42, 2019.04, 1969.53, 2082.75, 2209.52, 2200.9, 253 | // 2364.04, 2543.32, 2423.95, 2483.28, 2604.88, 2393.81, 2231.27, 2420.82, 254 | // 2544.0, 2766.67, 2919.62, 2763.25]; 255 | // const HIGH: &[f64] = &[2174.72, 2129.49, 2158.92, 2050.2, 2042.12, 2151.19, 2220.64, 2352.98, 256 | // 2456.25, 2691.53, 2572.81, 2494.14, 2845.93, 2682.66, 2527.13, 2455.68, 257 | // 2607.54, 2872.17, 3004.26, 3036.05]; 258 | // const LOW: &[f64] = &[1934.7, 1921.02, 1793.77, 1887.36, 1919.72, 1868.23, 1991.19, 2011.08, 259 | // 2193.91, 2183.96, 2223.15, 2363.19, 2240.03, 2208.31, 2192.15, 2199.02, 260 | // 2311.16, 2463.15, 2651.8, 2749.42]; 261 | // const CLOSE: &[f64] = &[1959.83, 2041.42, 2019.04, 1969.53, 2082.75, 2209.52, 2200.9, 2364.04, 262 | // 2543.32, 2423.95, 2483.28, 2604.88, 2393.81, 2231.27, 2420.82, 2544.0, 263 | // 2766.67, 2919.62, 2763.25, 2922.14]; 264 | 265 | 266 | // #[test] 267 | // fn sma_works() { 268 | // let mut result = sma(CLOSE, 4).unwrap(); 269 | // let expected = &[1997.455, 2028.185, 2070.21, 2115.675, 2214.3025, 2329.445, 2383.0525, 270 | // 2453.6475, 2513.8575, 2476.48, 2428.31, 2412.695, 2397.475, 2490.69, 271 | // 2662.7775, 2748.385, 2842.92]; 272 | // round_array(result.as_mut(), 4); 273 | // assert_eq!(result, expected); 274 | // } 275 | 276 | // #[test] 277 | // fn ema_works() { 278 | // let mut result = ema(CLOSE, 4).unwrap(); 279 | // let expected = &[1997.455, 2031.573, 2102.7518, 2142.0111, 2230.8226, 2355.8216, 2383.073, 280 | // 2423.1558, 2495.8455, 2455.0313, 2365.5268, 2387.6441, 2450.1864, 2576.7799, 281 | // 2713.9159, 2733.6496, 2809.0457]; 282 | // round_array(result.as_mut(), 4); 283 | // assert_eq!(result, expected); 284 | // } 285 | 286 | // #[test] 287 | // fn psar_works() { 288 | // let mut result = psar(HIGH, LOW, 0.02, 0.2).unwrap(); 289 | // let expected = &[2174.72, 2169.646, 2158.92, 2158.92, 1793.77, 1800.9184, 1817.7073, 290 | // 1849.8236, 1898.3377, 1977.657, 2049.0443, 2113.2928, 2201.2093, 2845.93, 291 | // 2832.8544, 2820.0403, 2192.15, 2205.7504, 2237.6908]; 292 | // round_array(result.as_mut(), 4); 293 | // // For some reasons, the first values are not exactly the same but since this indicator 294 | // // was not clearly described by its author, we can say that current implementation is correct. 295 | // assert_eq!(result[result.len() - 16..result.len()], 296 | // expected[expected.len() - 16..expected.len()]); 297 | // } 298 | 299 | // #[test] 300 | // fn rsi_works() { 301 | // let mut result = rsi(CLOSE, 6).unwrap(); 302 | // let expected = &[79.9771, 86.5336, 90.5949, 73.0035, 75.8056, 80.7258, 56.706, 44.4766, 303 | // 57.3488, 63.879, 72.8847, 77.5072, 64.1009, 70.3536]; 304 | // round_array(result.as_mut(), 4); 305 | // assert_eq!(result, expected); 306 | // } 307 | -------------------------------------------------------------------------------- /src/qaperformance.rs: -------------------------------------------------------------------------------- 1 | use std::collections::HashMap; 2 | 3 | use chrono::{DateTime, FixedOffset, NaiveDate, NaiveDateTime, NaiveTime, TimeZone, Utc}; 4 | use chrono::format::ParseError; 5 | use qifi_rs::account::Trade; 6 | use serde::{Deserialize, Serialize}; 7 | 8 | use crate::market_preset::MarketPreset; 9 | 10 | /// performace is a simple way for analaysis single pair of every trades 11 | #[derive(Debug, Clone, Deserialize, Serialize)] 12 | pub struct QATradePair { 13 | pub open_datetime: i64, 14 | pub close_datetime: i64, 15 | pub opendate: String, 16 | pub closedate: String, 17 | pub if_buyopen: bool, 18 | pub code: String, 19 | pub amount: f64, 20 | pub openprice: f64, 21 | pub closeprice: f64, 22 | pub open_trade_id: String, 23 | pub close_trade_id: String, 24 | pub pnl_ratio: f64, 25 | pub pnl_money: f64, 26 | pub hold_gap: f64, 27 | } 28 | 29 | 30 | 31 | #[derive(Debug, Clone, Deserialize, Serialize)] 32 | pub struct Temp { 33 | pub amount: f64, 34 | pub direction: String, 35 | pub offset: String, 36 | pub datetime: i64, 37 | pub code: String, 38 | pub price: f64, 39 | pub trade_id: String, 40 | } 41 | 42 | #[derive(Debug, Clone, Deserialize, Serialize)] 43 | pub struct QAPerformance_Single { 44 | pub market_set: MarketPreset, 45 | pub pair: Vec, 46 | pub temp: HashMap>, 47 | } 48 | 49 | #[derive(Debug, Clone, Deserialize, Serialize)] 50 | pub struct QAPerformance { 51 | pub market: HashMap 52 | } 53 | 54 | #[derive(Debug, Clone, Deserialize, Serialize)] 55 | pub struct QARiskMessage {} 56 | 57 | 58 | impl QAPerformance { 59 | pub fn new() -> Self { 60 | QAPerformance { 61 | market: HashMap::new() 62 | } 63 | } 64 | 65 | pub fn insert_trade(&mut self, trade: Trade) { 66 | let code = trade.instrument_id.clone(); 67 | if self.market.contains_key(&code) { 68 | self.market.get_mut(&code).unwrap().insert_trade(trade.clone()); 69 | } else { 70 | let mut u = QAPerformance_Single::new(); 71 | u.insert_trade(trade.clone()); 72 | self.market.insert(code.clone(), u); 73 | } 74 | } 75 | pub fn get_totalprofit(&mut self) -> f64 { 76 | let mut tp: f64 = 0.0; 77 | for (_, ps) in self.market.iter_mut() { 78 | tp += ps.get_totalprofit(); 79 | } 80 | tp 81 | } 82 | 83 | pub fn pair(&mut self) -> Vec { 84 | let mut px = vec![]; 85 | for (_, ps) in self.market.iter_mut() { 86 | for item in &ps.pair { 87 | px.push(item.to_owned()) 88 | } 89 | } 90 | px 91 | } 92 | } 93 | 94 | impl QAPerformance_Single { 95 | pub fn new() -> Self { 96 | let mut temp = HashMap::new(); 97 | temp.insert("BUY".to_string(), vec![]); 98 | temp.insert("SELL".to_string(), vec![]); 99 | QAPerformance_Single { 100 | market_set: MarketPreset::new(), 101 | pair: vec![], 102 | temp, 103 | } 104 | } 105 | pub fn insert_trade(&mut self, trade: Trade) { 106 | match trade.offset.as_str() { 107 | "OPEN" => { 108 | let direction = trade.direction.as_str(); 109 | let u = self.temp.get_mut(direction).unwrap(); 110 | u.push(Temp { 111 | amount: trade.volume.clone(), 112 | direction: trade.direction.clone(), 113 | offset: "OPEN".to_string(), 114 | datetime: trade.trade_date_time.clone(), 115 | code: trade.instrument_id.clone(), 116 | price: trade.price.clone(), 117 | trade_id: trade.trade_id.clone(), 118 | }); 119 | } 120 | "CLOSE" | "CLOSETODAY" => { 121 | let (raw_direction, is_buy) = match trade.direction.as_str() { 122 | "BUY" => ("SELL", false), 123 | "SELL" => ("BUY", true), 124 | _ => ("", false), 125 | }; 126 | let u = self.temp.get_mut(raw_direction).unwrap(); 127 | //println!("{:#?}", u); 128 | 129 | let mut codeset = self.market_set.get(trade.instrument_id.as_ref()); 130 | 131 | let f = u.get_mut(0).unwrap(); 132 | 133 | if trade.volume > f.amount { 134 | // close> raw ==> 注销继续loop 135 | let hold_gap = (trade.trade_date_time.clone() - f.datetime.clone()) as f64/1000000000.0; 136 | let mut pnl_money = codeset.unit_table as f64 137 | * (trade.price.clone() - f.price.clone()) 138 | * f.amount.clone(); 139 | if !is_buy{ 140 | pnl_money = pnl_money * -1.0; 141 | } 142 | let pnl_ratio = 143 | pnl_money / (f.price.clone() * f.amount.clone() * codeset.calc_coeff()); 144 | self.pair.push(QATradePair { 145 | open_datetime: f.datetime.clone(), 146 | close_datetime: trade.trade_date_time.clone(), 147 | opendate: Utc.timestamp_nanos(f.datetime.clone()+ 28800000000000).to_string()[0..19].to_string(), 148 | closedate: Utc.timestamp_nanos(trade.trade_date_time.clone()+ 28800000000000).to_string()[0..19].to_string(), 149 | if_buyopen: is_buy, 150 | code: f.code.clone(), 151 | amount: f.amount.clone(), 152 | openprice: f.price.clone(), 153 | closeprice: trade.price.clone(), 154 | open_trade_id: f.trade_id.clone(), 155 | close_trade_id: trade.trade_id.clone(), 156 | pnl_ratio, 157 | pnl_money, 158 | hold_gap 159 | }); 160 | let mut new_t = trade.clone(); 161 | 162 | new_t.volume -= f.amount; 163 | u.remove(0); 164 | self.insert_trade(new_t) 165 | } else if trade.volume < f.amount { 166 | let hold_gap: f64 = (trade.trade_date_time.clone() - f.datetime.clone()) as f64/1000000000.0; 167 | let mut pnl_money = codeset.unit_table as f64 168 | * (trade.price.clone() - f.price.clone()) 169 | * trade.volume.clone(); 170 | if !is_buy{ 171 | pnl_money = pnl_money * -1.0; 172 | } 173 | let pnl_ratio = 174 | pnl_money / (f.price.clone() * trade.volume.clone() * codeset.calc_coeff()); 175 | self.pair.push(QATradePair { 176 | open_datetime: f.datetime.clone(), 177 | close_datetime: trade.trade_date_time.clone(), 178 | opendate: Utc.timestamp_nanos(f.datetime.clone()+ 28800000000000).to_string()[0..19].to_string(), 179 | closedate: Utc.timestamp_nanos(trade.trade_date_time.clone()+ 28800000000000).to_string()[0..19].to_string(), 180 | if_buyopen: is_buy, 181 | code: f.code.clone(), 182 | amount: trade.volume.clone(), 183 | openprice: f.price.clone(), 184 | closeprice: trade.price.clone(), 185 | open_trade_id: f.trade_id.clone(), 186 | close_trade_id: trade.trade_id.clone(), 187 | pnl_ratio, 188 | pnl_money, 189 | hold_gap 190 | }); 191 | f.amount -= trade.volume.clone(); 192 | 193 | //u.insert(0, f.clone()); 194 | } else { 195 | let mut pnl_money = codeset.unit_table as f64 196 | * (trade.price.clone() - f.price.clone()) 197 | * f.amount.clone(); 198 | if !is_buy{ 199 | pnl_money = pnl_money * -1.0; 200 | } 201 | let pnl_ratio = 202 | pnl_money / (f.price.clone() * f.amount.clone() * codeset.calc_coeff()); 203 | let hold_gap:f64 = (trade.trade_date_time.clone() - f.datetime.clone()) as f64/1000000000.0; 204 | self.pair.push(QATradePair { 205 | open_datetime: f.datetime.clone(), 206 | close_datetime: trade.trade_date_time.clone(), 207 | opendate: Utc.timestamp_nanos(f.datetime.clone()+ 28800000000000).to_string()[0..19].to_string(), 208 | closedate: Utc.timestamp_nanos(trade.trade_date_time.clone()+ 28800000000000).to_string()[0..19].to_string(), 209 | if_buyopen: is_buy, 210 | code: f.code.clone(), 211 | amount: f.amount.clone(), 212 | openprice: f.price.clone(), 213 | closeprice: trade.price.clone(), 214 | open_trade_id: f.trade_id.clone(), 215 | close_trade_id: trade.trade_id.clone(), 216 | pnl_ratio, 217 | pnl_money, 218 | hold_gap 219 | }); 220 | u.remove(0); 221 | } 222 | } 223 | _ => {} 224 | } 225 | } 226 | pub fn get_totalprofit(&mut self) -> f64 { 227 | let mut profit = 0.0; 228 | let _: Vec<_> = self 229 | .pair 230 | .iter_mut() 231 | .map(|a| profit += a.pnl_money) 232 | .collect(); 233 | profit 234 | } 235 | /// 236 | /// 15%交易盈亏比:每天交易10次,,平均亏损,最大亏损 237 | /// 参考:交易实时盈亏比:引入行情,重点在评估每次操盘手平均冒多大风险,赚多大利润 238 | /// 15%胜率:多少次盈利,多少次亏损 239 | /// 40%绝对收益能力:通过操盘手收益(元)/日初总金额(万)。 240 | /// 30%资源周转能力:实际交易金额(元)/日初总金额(万) 241 | /// 手续费贡献:差额手续费(元)/日出总金额(万) 242 | pub fn get_maxprofit(&mut self) -> f64 { 243 | let mut profit: Vec = vec![]; 244 | let _: Vec<_> = self 245 | .pair 246 | .iter_mut() 247 | .map(|a| profit.push(a.pnl_money)) 248 | .collect(); 249 | profit.iter().cloned().fold(0. / 0., f64::max) 250 | } 251 | pub fn get_averageprofit(&mut self) -> f64 { 252 | if self.pair.len() > 0 { 253 | self.get_totalprofit() / self.pair.len() as f64 254 | } else { 255 | 0.0 256 | } 257 | } 258 | pub fn get_profitcount(&mut self) -> i32 { 259 | let mut count = 0; 260 | let _: Vec<_> = self 261 | .pair 262 | .iter_mut() 263 | .map(|a| { 264 | if a.pnl_money > 0.0 { 265 | count += 1 266 | } 267 | }) 268 | .collect(); 269 | count 270 | } 271 | pub fn get_losscount(&mut self) -> i32 { 272 | let mut count = 0; 273 | let _: Vec<_> = self 274 | .pair 275 | .iter_mut() 276 | .map(|a| { 277 | if a.pnl_money < 0.0 { 278 | count += 1 279 | } 280 | }) 281 | .collect(); 282 | count 283 | } 284 | } 285 | 286 | #[cfg(test)] 287 | mod tests { 288 | use crate::qaaccount::QA_Account; 289 | 290 | use super::*; 291 | 292 | #[test] 293 | fn test_to_qifi() { 294 | let code = "rb2005"; 295 | let mut p = QAPerformance_Single::new(); 296 | let mut acc = QA_Account::new("RustT01B2_RBL8", "test", "admin", 10000000.0, false, "real"); 297 | acc.init_h(code); 298 | acc.sell_open(code, 10.0, "2020-01-20 09:30:22", 3500.0); 299 | acc.buy_open(code, 10.0, "2020-01-20 09:52:00", 3500.0); 300 | assert_eq!(acc.get_volume_short(code), 10.0); 301 | assert_eq!(acc.get_volume_long(code), 10.0); 302 | acc.buy_close(code, 10.0, "2020-01-20 10:22:00", 3600.0); 303 | acc.buy_open(code, 10.0, "2020-01-20 13:54:00", 3500.0); 304 | acc.buy_open(code, 10.0, "2020-01-20 13:55:00", 3510.0); 305 | 306 | acc.sell_close(code, 20.0, "2020-01-20 14:52:00", 3620.0); 307 | acc.buy_open(code, 20.0, "2020-01-21 13:54:00", 3500.0); 308 | acc.sell_close(code, 15.0, "2020-01-21 13:55:00", 3510.0); 309 | 310 | acc.sell_close(code, 5.0, "2020-01-21 14:52:00", 3420.0); 311 | println!("{:#?}", acc.dailytrades); 312 | for (_, i) in acc.dailytrades.iter_mut() { 313 | println!("{:#?}", i); 314 | p.insert_trade(i.to_owned()); 315 | } 316 | println!("{:#?}", p.pair); 317 | println!("{}", p.get_totalprofit()) 318 | } 319 | 320 | #[test] 321 | fn test_backtest() { 322 | let code = "rb2005"; 323 | let mut p = QAPerformance_Single::new(); 324 | let mut acc = QA_Account::new( 325 | "RustT01B2_RBL8", 326 | "test", 327 | "admin", 328 | 10000000.0, 329 | false, 330 | "backtest", 331 | ); 332 | acc.init_h(code); 333 | acc.sell_open(code, 10.0, "2020-01-20 09:30:22", 3500.0); 334 | acc.buy_open(code, 10.0, "2020-01-20 09:52:00", 3500.0); 335 | assert_eq!(acc.get_volume_short(code), 10.0); 336 | assert_eq!(acc.get_volume_long(code), 10.0); 337 | acc.buy_close(code, 10.0, "2020-01-20 10:22:00", 3600.0); 338 | acc.buy_open(code, 10.0, "2020-01-20 13:54:00", 3500.0); 339 | acc.buy_open(code, 10.0, "2020-01-20 13:55:00", 3510.0); 340 | 341 | acc.sell_close(code, 20.0, "2020-01-20 14:52:00", 3620.0); 342 | acc.buy_open(code, 20.0, "2020-01-21 13:54:00", 3500.0); 343 | acc.sell_close(code, 15.0, "2020-01-21 13:55:00", 3510.0); 344 | 345 | acc.sell_close(code, 5.0, "2020-01-21 14:52:00", 3420.0); 346 | 347 | for i in acc.history.iter_mut() { 348 | p.insert_trade(i.to_qifitrade()); 349 | } 350 | println!("{:#?}", p.pair); 351 | println!("{}", p.get_totalprofit()) 352 | } 353 | 354 | #[test] 355 | fn test_pair() { 356 | let mut acc = QA_Account::new("test", "test", "admin", 1000000.0, false, "real"); 357 | let code = "Z$002352"; 358 | let mut p = QAPerformance::new(); 359 | acc.sell_open(code, 1000.0, "2020-04-03 09:30:22", 46.33); 360 | acc.sell_open("RB2005", 10.0, "2020-04-03 09:30:22", 3346.33); 361 | acc.buy_open(code, 1000.0, "2020-04-03 09:52:00", 46.86); 362 | 363 | acc.buy_close(code, 1000.0, "2020-04-03 10:22:00", 47.34); 364 | acc.sell_close(code, 1000.0, "2020-04-03 10:22:00", 47.34); 365 | acc.buy_close("RB2005", 10.0, "2020-04-03 10:30:22", 3246.33); 366 | acc.buy_open(code, 1000.0, "2020-04-03 13:54:00", 47.1); 367 | acc.buy_open(code, 1000.0, "2020-04-03 13:55:00", 47.11); 368 | 369 | acc.sell_close(code, 2000.0, "2020-04-03 14:52:00", 47.17); 370 | 371 | // acc.buy_open(code, 2000.0, "2020-04-03 13:54:00", 47.1); 372 | // acc.sell_close(code, 1000.0, "2020-04-03 13:55:00", 47.11); 373 | // 374 | // acc.sell_close(code, 1000.0, "2020-04-03 14:52:00", 47.17); 375 | 376 | for (_, i) in acc.dailytrades.iter_mut() { 377 | println!("{:#?}", i); 378 | let ux: Trade = i.to_owned(); 379 | 380 | // insert_date_time: Utc 381 | // .datetime_from_str(time, "%Y-%m-%d %H:%M:%S") 382 | // .unwrap() 383 | // .timestamp_nanos() 384 | // - 28800000000000, 385 | println!("{:#?}", Utc.timestamp_nanos(ux.trade_date_time.clone() + 28800000000000).to_string()[0..19].to_string()); 386 | p.insert_trade(i.to_owned()); 387 | } 388 | println!("{:#?}", p.pair()); 389 | // println!("{:#?}", p.get_maxprofit()); 390 | // println!("{:#?}", p.get_averageprofit()); 391 | } 392 | 393 | #[test] 394 | fn test_pairtoday() { 395 | let mut acc = QA_Account::new("test", "test", "admin", 1000000.0, false, "real"); 396 | let code = "Z$002352"; 397 | let mut p = QAPerformance::new(); 398 | acc.sell_open(code, 1000.0, "2020-04-03 09:30:22", 46.33); 399 | acc.sell_open("RB2005", 10.0, "2020-04-03 09:30:22", 3346.33); 400 | acc.buy_open(code, 1000.0, "2020-04-03 09:52:00", 46.86); 401 | 402 | acc.buy_closetoday(code, 1000.0, "2020-04-03 10:22:00", 47.34); 403 | acc.sell_closetoday(code, 1000.0, "2020-04-03 10:22:00", 47.34); 404 | acc.buy_closetoday("RB2005", 10.0, "2020-04-03 10:30:22", 3246.33); 405 | acc.buy_open(code, 1000.0, "2020-04-03 13:54:00", 47.1); 406 | acc.buy_open(code, 1000.0, "2020-04-03 13:55:00", 47.11); 407 | 408 | acc.sell_closetoday(code, 2000.0, "2020-04-03 14:52:00", 47.17); 409 | 410 | // acc.buy_open(code, 2000.0, "2020-04-03 13:54:00", 47.1); 411 | // acc.sell_close(code, 1000.0, "2020-04-03 13:55:00", 47.11); 412 | // 413 | // acc.sell_close(code, 1000.0, "2020-04-03 14:52:00", 47.17); 414 | 415 | for (_, i) in acc.dailytrades.iter_mut() { 416 | println!("{:#?}", i); 417 | let ux: Trade = i.to_owned(); 418 | 419 | // insert_date_time: Utc 420 | // .datetime_from_str(time, "%Y-%m-%d %H:%M:%S") 421 | // .unwrap() 422 | // .timestamp_nanos() 423 | // - 28800000000000, 424 | println!("{:#?}", Utc.timestamp_nanos(ux.trade_date_time.clone() + 28800000000000).to_string()[0..19].to_string()); 425 | p.insert_trade(i.to_owned()); 426 | } 427 | println!("{:#?}", p.pair()); 428 | // println!("{:#?}", p.get_maxprofit()); 429 | // println!("{:#?}", p.get_averageprofit()); 430 | } 431 | } 432 | --------------------------------------------------------------------------------