Skip to content

Commit f9ab84c

Browse files
committed
xd
1 parent 3b91b34 commit f9ab84c

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

64 files changed

+3249
-2057
lines changed
File renamed without changes.

Cargo.lock

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 35 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,37 @@
99

1010
</div>
1111

12-
**qPACE**: The Technical Analysis framework for Python and JavaScript, written in Rust, designed to be extremely fast.
12+
**qPACE**: The Technical Analysis framework for Python and JavaScript, written in Rust, designed to be extremely fast. Everything you need in one place.
1313

1414
![backtest summary](/static/backtest_summary.png)
1515

1616
## Table of Contents
1717

18-
- [Features](#features)
18+
- [Features and Examples](#features-and-examples)
1919

20-
- [Installation](#installation)
21-
22-
- [Examples](#examples)
20+
- [Pine from Python/JavaScript](#pine-from-python-javascript)
2321

24-
- [Pine from Python](#pine-from-python)
22+
- [Installation](#installation)
2523

2624
- [Motivation](#motivation)
2725

28-
- [Contributing](#contributing)
26+
- [Community](#community)
2927

30-
## Features
28+
## Features and Examples
3129

32-
- Simple, yet powerful API
30+
- Simple, yet powerful and fully-typed API
3331

34-
- Comparable results to TradingView Pine
32+
- QPC - Pine compiler. Run Pine indicators/strategies from Python/JavaScript with accurate results.
3533

3634
- Extremely fast backtesting with vectorization support
3735

38-
- OHLCV - loading, resampling, and more
39-
40-
- Performance metrics - sharpe, sortino, omega, and more
36+
- Yahoo Finance API integration
4137

42-
- Utility functions - position sizing, risk management, and more
43-
44-
- Parameter optimization
45-
- Grid Search
46-
- Genetic Evolution
38+
- CLI
39+
- View/Export symbols
40+
- View/Export OHLCV
41+
- Compile Pine to Python/JavaScript
42+
- Backtest Pine strategies
4743

4844
- TA indicators written in [Pine](/content/ta.pine), compiled via [QPC](#pine-from-python)
4945
- Accumulation/Distribution (ACCDIST)
@@ -81,6 +77,25 @@
8177
- Vortex Indicator
8278
- Williams %R
8379

80+
- OHLCV ops
81+
- Integration with our own API and Yahoo Finance API
82+
- Loading
83+
- Resampling
84+
- Lower timeframe to higher timeframe
85+
- Tick Bars
86+
- Dollar Bars
87+
- And More
88+
89+
- Performance metrics
90+
- Sharpe Ratio
91+
- Sortino Ratio
92+
- Omega Ratio
93+
- And More
94+
95+
- Parameter optimization
96+
- Grid Search
97+
- Genetic Evolution
98+
8499
## Installation
85100

86101
### Python
@@ -95,12 +110,8 @@ pip install qpace
95110
npm install qpace
96111
```
97112

98-
## Examples
99-
100-
## Pine from Python
113+
## Pine from Python/JavaScript
101114

102115
QPC is specially designed compiler to translate any Pine code into efficient Rust code that is then exposed to Python and JavaScript, allowing you to run your favorite Pine scripts in the most efficient way possible.
103116

104-
## Motivation
105-
106-
## Contributing
117+
## Community

base/node/uuid.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { v4: _uuidv4 } = require("uuid");
2+
3+
export const uuidv4 = (): string => _uuidv4();

core/backtest.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,16 @@ impl BacktestConfig {
6262
pub fn process_orders_on_close(&self) -> bool {
6363
self.process_orders_on_close
6464
}
65+
66+
#[inline]
67+
pub fn set_initial_capital(&mut self, initial_capital: f64) {
68+
self.initial_capital = initial_capital;
69+
}
70+
71+
#[inline]
72+
pub fn set_process_orders_on_close(&mut self, process_orders_on_close: bool) {
73+
self.process_orders_on_close = process_orders_on_close;
74+
}
6575
}
6676

6777
pub struct Backtest {

core/backtest_js.rs

Lines changed: 115 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ cfg_if::cfg_if! { if #[cfg(feature = "bindings_wasm")] {
22
use wasm_bindgen::prelude::*;
33
use crate::ctx_js::{JsCtx};
44
use wasm_bindgen::convert::TryFromJsValue;
5+
use js_sys::{Object, Reflect};
56
}}
67
use crate::{
78
backtest::{Backtest, BacktestConfig},
@@ -16,15 +17,23 @@ use crate::{
1617
use chrono::{DateTime, Utc};
1718
use std::{cell::RefCell, collections::HashMap, ops::Deref, rc::Rc};
1819

20+
#[cfg(feature = "bindings_wasm")]
21+
#[wasm_bindgen]
22+
extern "C" {
23+
#[wasm_bindgen(js_namespace = console, js_name = table)]
24+
pub fn js_console_table(obj: &JsValue);
25+
26+
#[wasm_bindgen(js_namespace = console, js_name = log)]
27+
pub fn js_console_log(s: &str);
28+
}
29+
1930
#[cfg(feature = "bindings_wasm")]
2031
#[wasm_bindgen(js_class=BacktestConfig)]
2132
impl BacktestConfig {
2233
#[wasm_bindgen(constructor)]
2334
#[inline]
24-
pub fn js_new(initial_capital: Option<f64>, process_orders_on_close: Option<bool>) -> Self {
25-
let initial_capital = initial_capital.unwrap_or(1000.0);
26-
let process_orders_on_close = process_orders_on_close.unwrap_or(false);
27-
Self::new(initial_capital, process_orders_on_close)
35+
pub fn js_new() -> Self {
36+
Self::default()
2837
}
2938

3039
#[wasm_bindgen(getter = initialCapital)]
@@ -33,11 +42,23 @@ impl BacktestConfig {
3342
self.initial_capital()
3443
}
3544

45+
#[wasm_bindgen(setter = initialCapital)]
46+
#[inline]
47+
pub fn js_set_initial_capital(&mut self, initial_capital: f64) {
48+
self.set_initial_capital(initial_capital)
49+
}
50+
3651
#[wasm_bindgen(getter = processOrdersOnClose)]
3752
#[inline]
3853
pub fn js_process_orders_on_close(&self) -> bool {
3954
self.process_orders_on_close()
4055
}
56+
57+
#[wasm_bindgen(setter = processOrdersOnClose)]
58+
#[inline]
59+
pub fn js_set_process_orders_on_close(&mut self, process_orders_on_close: bool) {
60+
self.set_process_orders_on_close(process_orders_on_close)
61+
}
4162
}
4263

4364
#[cfg(feature = "bindings_wasm")]
@@ -69,8 +90,8 @@ impl Into<Rc<RefCell<Backtest>>> for JsBacktest {
6990
impl JsBacktest {
7091
#[wasm_bindgen(constructor)]
7192
#[inline]
72-
pub fn js_new(js_ctx: JsCtx, config: BacktestConfig) -> Self {
73-
Self::new(js_ctx, config)
93+
pub fn js_new(ctx: JsCtx, config: BacktestConfig) -> Self {
94+
Self::new(ctx, config)
7495
}
7596

7697
#[wasm_bindgen(getter = ctx)]
@@ -79,6 +100,27 @@ impl JsBacktest {
79100
self.js_ctx.clone()
80101
}
81102

103+
#[wasm_bindgen(js_name = "next")]
104+
#[inline]
105+
pub fn js_next(&mut self) -> Option<usize> {
106+
let mut bt = self.bt.borrow_mut();
107+
let next = bt.ctx().borrow_mut().next();
108+
bt.on_bar_open();
109+
return next;
110+
}
111+
112+
#[wasm_bindgen(js_name = "onBarOpen")]
113+
#[inline]
114+
pub fn js_on_bar_open(&mut self) {
115+
self.bt.borrow_mut().on_bar_open();
116+
}
117+
118+
#[wasm_bindgen(js_name = "onBarClose")]
119+
#[inline]
120+
pub fn js_on_bar_close(&mut self) {
121+
self.bt.borrow_mut().on_bar_close();
122+
}
123+
82124
#[wasm_bindgen(getter = config)]
83125
#[inline]
84126
pub fn is_config(&self) -> BacktestConfig {
@@ -99,13 +141,13 @@ impl JsBacktest {
99141
self.bt.borrow().net_equity()
100142
}
101143

102-
#[wasm_bindgen(getter = equitySeries)]
144+
#[wasm_bindgen(getter = equityList)]
103145
#[inline]
104146
pub fn js_equity_series(&self) -> Vec<f64> {
105147
self.bt.borrow().equity_series().to_vec()
106148
}
107149

108-
#[wasm_bindgen(getter = netEquitySeries)]
150+
#[wasm_bindgen(getter = netEquityList)]
109151
#[inline]
110152
pub fn js_net_equity_series(&self) -> Vec<f64> {
111153
self.bt.borrow().net_equity_series().to_vec()
@@ -123,7 +165,7 @@ impl JsBacktest {
123165
self.bt.borrow().net_equity_returns()
124166
}
125167

126-
#[wasm_bindgen(getter = pnlSeries)]
168+
#[wasm_bindgen(getter = pnlList)]
127169
#[inline]
128170
pub fn js_pnl_series(&self) -> Vec<f64> {
129171
self.bt.borrow().pnl_series()
@@ -300,22 +342,20 @@ impl JsBacktest {
300342
self.bt.borrow_mut().signal_batch_dict(_signals)
301343
}
302344

303-
#[wasm_bindgen(js_name = "skipRemainingBars")]
345+
#[wasm_bindgen(js_name = "skipTo")]
304346
#[inline]
305-
pub fn js_skip_remaining_bars(&mut self) {
306-
self.bt.borrow_mut().skip_remaining_bars()
307-
}
308-
309-
#[wasm_bindgen(js_name = "skipToBar")]
310-
#[inline]
311-
pub fn js_skip_to_bar(&mut self, bar_index: usize) {
347+
pub fn js_skip_to(&mut self, bar_index: usize) {
312348
self.bt.borrow_mut().skip_to_bar(bar_index)
313349
}
314350

315-
#[wasm_bindgen(js_name = "skipBars")]
351+
#[wasm_bindgen(js_name = "skip")]
316352
#[inline]
317-
pub fn js_skip_bars(&mut self, bars: usize) {
318-
self.bt.borrow_mut().skip_bars(bars)
353+
pub fn js_skip(&mut self, bars: Option<usize>) {
354+
if bars.is_none() {
355+
self.bt.borrow_mut().skip_remaining_bars()
356+
} else {
357+
self.bt.borrow_mut().skip_bars(bars.unwrap())
358+
}
319359
}
320360

321361
#[wasm_bindgen(getter = length)]
@@ -329,4 +369,59 @@ impl JsBacktest {
329369
pub fn js_to_pine(&self) -> String {
330370
self.bt.borrow().to_pine()
331371
}
372+
373+
#[wasm_bindgen(getter = metrics)]
374+
pub fn js_metrics(&self) -> JsValue {
375+
let bt = self.bt.borrow();
376+
let obj = Object::new();
377+
378+
let _ = Reflect::set(
379+
&obj,
380+
&JsValue::from_str("equity"),
381+
&JsValue::from_f64(bt.equity()),
382+
);
383+
let _ = Reflect::set(
384+
&obj,
385+
&JsValue::from_str("netEquity"),
386+
&JsValue::from_f64(bt.net_equity()),
387+
);
388+
let _ = Reflect::set(
389+
&obj,
390+
&JsValue::from_str("netProfit"),
391+
&JsValue::from_f64(bt.net_profit()),
392+
);
393+
let _ = Reflect::set(
394+
&obj,
395+
&JsValue::from_str("profitFactor"),
396+
&JsValue::from_f64(bt.profit_factor()),
397+
);
398+
let _ = Reflect::set(
399+
&obj,
400+
&JsValue::from_str("winRate"),
401+
&JsValue::from_f64(bt.win_rate()),
402+
);
403+
let _ = Reflect::set(
404+
&obj,
405+
&JsValue::from_str("positionSize"),
406+
&JsValue::from_f64(bt.position_size()),
407+
);
408+
let _ = Reflect::set(
409+
&obj,
410+
&JsValue::from_str("openTrades"),
411+
&JsValue::from_f64(bt.open_trades().len() as f64),
412+
);
413+
let _ = Reflect::set(
414+
&obj,
415+
&JsValue::from_str("closedTrades"),
416+
&JsValue::from_f64(bt.closed_trades().len() as f64),
417+
);
418+
419+
obj.into()
420+
}
421+
422+
#[wasm_bindgen(js_name = "print")]
423+
pub fn js_print(&self) {
424+
let metrics = self.js_metrics();
425+
js_console_table(&metrics);
426+
}
332427
}

0 commit comments

Comments
 (0)