wickra-backtest-wasm 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE-APACHE +201 -0
- package/LICENSE-MIT +21 -0
- package/README.md +396 -0
- package/package.json +33 -0
- package/wickra_backtest_wasm.d.ts +80 -0
- package/wickra_backtest_wasm.js +9 -0
- package/wickra_backtest_wasm_bg.js +470 -0
- package/wickra_backtest_wasm_bg.wasm +0 -0
package/LICENSE-APACHE
ADDED
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or Derivative
|
|
95
|
+
Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright 2026 kingchenc and the Wickra contributors
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
package/LICENSE-MIT
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kingchenc and the Wickra contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,396 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<a href="https://wickra.org"><img src="https://raw.githubusercontent.com/wickra-lib/.github/main/profile/wickra-banner.webp?v=514" alt="Wickra Backtest — backtest and live are byte-identical" width="100%"></a>
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
[](https://github.com/wickra-lib/wickra)
|
|
6
|
+
[-orange)](https://github.com/wickra-lib/wickra-backtest)
|
|
7
|
+
[](https://github.com/wickra-lib/wickra-backtest/actions/workflows/ci.yml)
|
|
8
|
+
[](https://github.com/wickra-lib/wickra-backtest/actions/workflows/codeql.yml)
|
|
9
|
+
[](https://codecov.io/gh/wickra-lib/wickra-backtest)
|
|
10
|
+
[](https://github.com/wickra-lib/wickra-backtest/releases/latest)
|
|
11
|
+
[](https://crates.io/crates/wickra-backtest)
|
|
12
|
+
[](https://pypi.org/project/wickra-backtest/)
|
|
13
|
+
[](https://www.npmjs.com/package/wickra-backtest)
|
|
14
|
+
[](https://www.nuget.org/packages/Wickra.Backtest)
|
|
15
|
+
[](https://central.sonatype.com/artifact/org.wickra/wickra-backtest)
|
|
16
|
+
[](https://pkg.go.dev/github.com/wickra-lib/wickra-backtest-go)
|
|
17
|
+
[](https://wickra-lib.r-universe.dev)
|
|
18
|
+
[](#license)
|
|
19
|
+
[](https://scorecard.dev/viewer/?uri=github.com/wickra-lib/wickra-backtest)
|
|
20
|
+
[](https://www.bestpractices.dev/)
|
|
21
|
+
[](https://github.com/wickra-lib/wickra-backtest/attestations)
|
|
22
|
+
[](https://backtest.wickra.org)
|
|
23
|
+
[](golden/)
|
|
24
|
+
[](https://backtest-live.wickra.org)
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
**Backtest and live — byte-identical, in 10 languages.** A streaming-native,
|
|
29
|
+
event-driven backtester built on the [Wickra](https://github.com/wickra-lib/wickra)
|
|
30
|
+
indicator core.
|
|
31
|
+
|
|
32
|
+
> **▶ Live demo:** run a strategy in your browser and watch the equity curve build bar by bar — **[backtest-live.wickra.org](https://backtest-live.wickra.org)** · zero backend, the same engine this repository ships, compiled to WebAssembly.
|
|
33
|
+
|
|
34
|
+
> **Part of the [Wickra ecosystem](https://github.com/wickra-lib):** the same data-driven core and ten-language binding surface also power [wickra-exchange](https://github.com/wickra-lib/wickra-exchange), [wickra-terminal](https://github.com/wickra-lib/wickra-terminal), [wickra-screener](https://github.com/wickra-lib/wickra-screener), [wickra-xray](https://github.com/wickra-lib/wickra-xray), [wickra-radar](https://github.com/wickra-lib/wickra-radar), [wickra-copilot](https://github.com/wickra-lib/wickra-copilot) and [wickra-shazam](https://github.com/wickra-lib/wickra-shazam).
|
|
35
|
+
|
|
36
|
+
The engine consumes the **exact same `wickra-core` O(1) indicator kernels** that
|
|
37
|
+
power live Wickra, and a strategy is **data (a JSON spec), not code** — so a
|
|
38
|
+
backtest and a live run over the same spec produce identical signals, across
|
|
39
|
+
every Wickra language binding. The same engine, fed live instead of historical
|
|
40
|
+
bars, becomes the live bot: **backtest ≡ live, by construction.**
|
|
41
|
+
|
|
42
|
+
The strategy spec can reference **495 `wickra-core` indicators** by name — every
|
|
43
|
+
backtestable scalar, candle, multi-output, pairwise, derivatives, order-book,
|
|
44
|
+
trade, trade-quote and cross-section indicator, with multi-output fields
|
|
45
|
+
addressed as `"name.field"` (`macd.signal`, `bb.upper`, `adx.plus_di`, …). The
|
|
46
|
+
registry is generated directly from the wickra-core sources, so it stays in
|
|
47
|
+
lock-step with the kernel.
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
pip install wickra-backtest
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
```python
|
|
54
|
+
import wickra_backtest as wbt
|
|
55
|
+
|
|
56
|
+
spec = { # a strategy is data, not code
|
|
57
|
+
"symbol": "BTCUSDT", "timeframe": "1h",
|
|
58
|
+
"indicators": {"fast": {"type": "Ema", "params": [12]},
|
|
59
|
+
"slow": {"type": "Ema", "params": [26]}},
|
|
60
|
+
"entry": {"cross_above": ["fast", "slow"]},
|
|
61
|
+
"exit": {"cross_below": ["fast", "slow"]},
|
|
62
|
+
"sizing": {"type": "fixed_fraction", "fraction": 0.95},
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
# Backtest: the whole series at once.
|
|
66
|
+
report = wbt.run(opens, highs, lows, closes, spec=spec)
|
|
67
|
+
print(report["metrics"]["return_pct"], report["metrics"]["sharpe"])
|
|
68
|
+
|
|
69
|
+
# Live: the same spec, the same engine, one bar at a time. Point `step` at a
|
|
70
|
+
# socket instead of an array and nothing else changes.
|
|
71
|
+
with wbt.StreamingBacktest(spec=spec) as live:
|
|
72
|
+
for bar in feed:
|
|
73
|
+
live.step(bar.open, bar.high, bar.low, bar.close)
|
|
74
|
+
print(live.num_trades, live.latest_equity())
|
|
75
|
+
report = live.finish()
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
The two reports are byte-identical. That is the whole claim, and a shared
|
|
79
|
+
[golden corpus](golden/) holds every one of the ten bindings to it.
|
|
80
|
+
|
|
81
|
+
What it does differently:
|
|
82
|
+
|
|
83
|
+
- **O(1) per tick** — years of tick data in seconds, not hours (no recompute-on-every-tick).
|
|
84
|
+
- **Backtest = live, value-identical across 10 languages** — no reimplementation drift, pinned by a shared golden corpus for the OHLCV path *and* every microstructure feed.
|
|
85
|
+
- **Microstructure backtesting** — replay the order book, trades, perpetual funding and open interest as strategy inputs, not just OHLCV. Most Python backtesters have no place to put them.
|
|
86
|
+
- **Realistic execution** — long/short, market/limit/stop orders, leverage and position caps, five sizing models, intrabar stop-loss / take-profit / trailing stops, maker/taker fees, three slippage models, perpetual funding, liquidation and execution latency.
|
|
87
|
+
- **Polyglot** — the same `StrategySpec` runs from Rust, Python, Node.js, WASM, C, C++, C#, Go, Java and R.
|
|
88
|
+
|
|
89
|
+
| Backtester | Languages | Engine | Strategy is | Book / funding inputs | Latest release |
|
|
90
|
+
|------------|-----------|--------|-------------|----------------------|----------------|
|
|
91
|
+
| **★ wickra-backtest** | **Rust · Python · Node.js · WASM · C · C++ · C# · Go · Java · R** | **event-driven, O(1)/bar** | **data (a JSON spec)** | **yes** | unreleased |
|
|
92
|
+
| nautilus_trader | Rust · Python | event-driven | code | yes | 2026-08 |
|
|
93
|
+
| vectorbt | Python | vectorised | code | — | 2026-07 |
|
|
94
|
+
| backtesting.py | Python | vectorised | code | — | 2026-07 |
|
|
95
|
+
| zipline-reloaded | Python | event-driven | code | — | 2025-07 |
|
|
96
|
+
| backtrader | Python | event-driven | code | — | 2023-04 |
|
|
97
|
+
|
|
98
|
+
Release dates are the latest published version on PyPI, checked when this table
|
|
99
|
+
was written; "—" means the feed is not a first-class strategy input, not that the
|
|
100
|
+
library is bad at what it does. nautilus_trader is the closest comparison and is
|
|
101
|
+
ahead of this project in places — it is a full trading platform with live venue
|
|
102
|
+
adapters, and it has shipped for years. The distinction here is narrower and
|
|
103
|
+
worth stating plainly: a strategy is **data rather than code**, so the same spec
|
|
104
|
+
runs unchanged from ten languages and a shared golden corpus pins every one of
|
|
105
|
+
them to the same report, byte for byte. No other engine in this table offers that
|
|
106
|
+
because none of them needs to.
|
|
107
|
+
|
|
108
|
+
## Status
|
|
109
|
+
|
|
110
|
+
**Alpha / work in progress.** The engine, the data-driven `StrategySpec`, the
|
|
111
|
+
full execution and cost model, the microstructure feeds and all ten language
|
|
112
|
+
bindings are implemented and tested; a shared [golden corpus](golden/) pins the
|
|
113
|
+
cross-language equality byte-for-byte. Not yet released to any registry.
|
|
114
|
+
|
|
115
|
+
## Documentation
|
|
116
|
+
|
|
117
|
+
- **[Strategy spec reference](docs/STRATEGY_SPEC.md)** — the full DSL: operands,
|
|
118
|
+
conditions, sizing, costs, slippage, risk, execution and the report shape.
|
|
119
|
+
- **[Cookbook](docs/COOKBOOK.md)** — six ready-to-run strategies (RSI mean
|
|
120
|
+
reversion, MACD trend, Bollinger breakout, Donchian breakout, funding carry,
|
|
121
|
+
order-book imbalance), each validated against the engine.
|
|
122
|
+
- **[Microstructure guide](docs/MICROSTRUCTURE.md)** — backtesting on the order
|
|
123
|
+
book, trades, perpetual funding and market breadth (the differentiator).
|
|
124
|
+
- **[Architecture](ARCHITECTURE.md)** — crates, data flow and design decisions.
|
|
125
|
+
- **[Benchmarks](BENCHMARKS.md)** — throughput methodology and caveats.
|
|
126
|
+
- **[Examples](examples/)** — runnable specs and a sample dataset.
|
|
127
|
+
- The JSON Schema for the spec is at
|
|
128
|
+
[`schema/strategy_spec.schema.json`](schema/strategy_spec.schema.json) and is
|
|
129
|
+
printed by `wkbt schema`.
|
|
130
|
+
|
|
131
|
+
## Quickstart
|
|
132
|
+
|
|
133
|
+
A strategy is **data** — a JSON spec. Run one over a candle file with the `wkbt` CLI:
|
|
134
|
+
|
|
135
|
+
```bash
|
|
136
|
+
cargo run --bin wkbt -- run --data examples/sample.csv --spec examples/ema-cross.json
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
```text
|
|
140
|
+
bars 80
|
|
141
|
+
trades 4
|
|
142
|
+
return -2.68%
|
|
143
|
+
pnl -268.21
|
|
144
|
+
sharpe -0.186
|
|
145
|
+
max dd 2.68%
|
|
146
|
+
win rate 0.0%
|
|
147
|
+
fees 37.50
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
A spec declares named indicators and entry/exit rules over them:
|
|
151
|
+
|
|
152
|
+
```json
|
|
153
|
+
{
|
|
154
|
+
"symbol": "BTCUSDT", "timeframe": "1h",
|
|
155
|
+
"indicators": { "ema_fast": { "type": "Ema", "params": [5] },
|
|
156
|
+
"ema_slow": { "type": "Ema", "params": [15] } },
|
|
157
|
+
"entry": { "cross_above": ["ema_fast", "ema_slow"] },
|
|
158
|
+
"exit": { "cross_below": ["ema_fast", "ema_slow"] },
|
|
159
|
+
"sizing": { "type": "fixed_fraction", "fraction": 0.95 },
|
|
160
|
+
"risk": { "trailing_stop_pct": 5.0 }
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
See the [cookbook](docs/COOKBOOK.md) and [`examples/`](examples/) for complete
|
|
165
|
+
strategies, and the [spec reference](docs/STRATEGY_SPEC.md) for the full grammar.
|
|
166
|
+
|
|
167
|
+
From Rust, the same thing is `wickra_backtest::run(&spec, &candles)`. For live
|
|
168
|
+
use, `StreamingBacktest::new(&spec, capital)` then `step(candle)` per bar feeds
|
|
169
|
+
the **same engine** one bar at a time — backtest and live are one code path. A
|
|
170
|
+
single `run_json` request bundles candles, the spec and any feeds, and is the
|
|
171
|
+
uniform entry point every binding wraps.
|
|
172
|
+
|
|
173
|
+
## Run the same spec in any language
|
|
174
|
+
|
|
175
|
+
Every binding takes the same OHLCV arrays (or a `run_json` request) and JSON spec
|
|
176
|
+
and returns the same report — byte-identical (a dict in Python). Each has a
|
|
177
|
+
quickstart:
|
|
178
|
+
|
|
179
|
+
| Binding | Install | Example |
|
|
180
|
+
|---------|---------|---------|
|
|
181
|
+
| Rust | `cargo add wickra-backtest` | [`examples/rust`](examples/rust/src/main.rs) |
|
|
182
|
+
| Python (PyO3) | `pip install wickra-backtest` | [`examples/python/backtest.py`](examples/python/backtest.py) |
|
|
183
|
+
| Node.js (napi-rs) | `npm install wickra-backtest` | [`examples/node/backtest.js`](examples/node/backtest.js) |
|
|
184
|
+
| Browser / WASM | `npm install wickra-backtest-wasm` | [`examples/wasm/backtest.cjs`](examples/wasm/backtest.cjs) |
|
|
185
|
+
| C / C++ (C ABI) | header + library, see [`bindings/c`](bindings/c/README.md) | [`examples/c/streaming.c`](examples/c/streaming.c) · [`cpp_smoke.cpp`](examples/c/cpp_smoke.cpp) |
|
|
186
|
+
| C# (C ABI) | `dotnet add package Wickra.Backtest`, see [`bindings/csharp`](bindings/csharp/README.md) | [`examples/csharp`](examples/csharp/Program.cs) |
|
|
187
|
+
| Go (cgo, C ABI) | `go get github.com/wickra-lib/wickra-backtest-go`, see [`bindings/go`](bindings/go/README.md) | [`examples/go`](examples/go/backtest.go) |
|
|
188
|
+
| Java (FFM, C ABI) | Maven Central `org.wickra:wickra-backtest`, see [`bindings/java`](bindings/java/README.md) | [`examples/java`](examples/java/src/main/java/org/wickra/backtest/examples/Backtest.java) |
|
|
189
|
+
| R (`.Call`, C ABI) | `R CMD INSTALL bindings/r`, see [`bindings/r`](bindings/r/README.md) | [`examples/r/backtest.R`](examples/r/backtest.R) |
|
|
190
|
+
|
|
191
|
+
Every example does the same thing in its own language: read the shared sample
|
|
192
|
+
data, run the series both ways, and fail if the two reports differ.
|
|
193
|
+
[`examples/README.md`](examples/README.md) is the cross-language index.
|
|
194
|
+
|
|
195
|
+
The C, C++, C#, Go, Java and R bindings all call through the same C ABI hub; the
|
|
196
|
+
[golden corpus](golden/) asserts every language produces the same report, for
|
|
197
|
+
both the plain OHLCV path and the order-book / trade / derivatives /
|
|
198
|
+
cross-section feed paths.
|
|
199
|
+
|
|
200
|
+
## Performance
|
|
201
|
+
|
|
202
|
+
O(1) per bar — about **1.7M bars/second** on one core (a year of 1-minute bars in
|
|
203
|
+
~0.3 s). See [BENCHMARKS.md](BENCHMARKS.md) for the methodology and caveats.
|
|
204
|
+
|
|
205
|
+
## Project layout
|
|
206
|
+
|
|
207
|
+
```
|
|
208
|
+
wickra-backtest/
|
|
209
|
+
├── crates/
|
|
210
|
+
│ ├── wickra-backtest-core/ engine: spec DSL, registry, rules, execution, portfolio, metrics, report
|
|
211
|
+
│ ├── wickra-backtest-data/ loaders (CSV / JSON / JSONL / Parquet) + resampling + Renko/Kagi/PnF
|
|
212
|
+
│ ├── wickra-backtest/ facade crate (re-exports the engine + runners)
|
|
213
|
+
│ ├── wickra-backtest-cli/ the `wkbt` command-line backtester
|
|
214
|
+
│ └── wickra-backtest-bench/ criterion throughput benchmarks
|
|
215
|
+
├── bindings/
|
|
216
|
+
│ ├── python/ PyO3 + maturin ├── csharp/ P/Invoke over the C ABI
|
|
217
|
+
│ ├── node/ napi-rs ├── go/ cgo over the C ABI
|
|
218
|
+
│ ├── wasm/ wasm-bindgen ├── java/ FFM over the C ABI
|
|
219
|
+
│ ├── c/ C ABI (cdylib/staticlib + generated header)
|
|
220
|
+
│ └── r/ .Call over the C ABI
|
|
221
|
+
├── golden/ shared cross-language parity corpus (cases + feed requests)
|
|
222
|
+
├── schema/ generated JSON Schema for the strategy spec
|
|
223
|
+
├── examples/ runnable strategies + a sample dataset
|
|
224
|
+
├── docs/ strategy spec reference + cookbook
|
|
225
|
+
└── fuzz/ cargo-fuzz targets (nightly)
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Building from source
|
|
229
|
+
|
|
230
|
+
```bash
|
|
231
|
+
# Rust core + tests + lints
|
|
232
|
+
cargo test --workspace --all-features
|
|
233
|
+
cargo clippy --workspace --all-targets --all-features -- -D warnings
|
|
234
|
+
cargo bench -p wickra-backtest-bench
|
|
235
|
+
|
|
236
|
+
# Python binding (requires a Rust toolchain + maturin)
|
|
237
|
+
cd bindings/python && maturin develop --release && pytest
|
|
238
|
+
|
|
239
|
+
# Node binding (requires @napi-rs/cli)
|
|
240
|
+
cd bindings/node && npm install && npm run build && npm test
|
|
241
|
+
|
|
242
|
+
# WASM binding (requires wasm-pack)
|
|
243
|
+
cd bindings/wasm && wasm-pack build --target nodejs --out-dir pkg && node --test tests/
|
|
244
|
+
|
|
245
|
+
# C ABI (cdylib + staticlib + generated header)
|
|
246
|
+
cargo build -p wickra-backtest-c --release
|
|
247
|
+
|
|
248
|
+
# C# binding (requires the .NET 8 SDK; links the C ABI above)
|
|
249
|
+
dotnet test bindings/csharp/Wickra.Backtest.Tests/Wickra.Backtest.Tests.csproj
|
|
250
|
+
|
|
251
|
+
# Go binding (requires a C compiler for cgo; links the C ABI above)
|
|
252
|
+
cd bindings/go && go test ./...
|
|
253
|
+
|
|
254
|
+
# Java binding (requires JDK 22+ and Maven; links the C ABI above)
|
|
255
|
+
mvn -f bindings/java test
|
|
256
|
+
|
|
257
|
+
# R binding (requires a C toolchain / Rtools; links the C ABI above)
|
|
258
|
+
WKBT_INC="$PWD/bindings/c/include" WKBT_LIB="$PWD/target/debug" R CMD INSTALL bindings/r
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
The Go, Java and R bindings load the C ABI shared library at run time; put
|
|
262
|
+
`target/debug` (or `target/release`) on the library path. Fuzzing requires a
|
|
263
|
+
nightly toolchain — see [`fuzz/`](fuzz/); the same never-panic invariants are
|
|
264
|
+
covered on stable by the property tests.
|
|
265
|
+
|
|
266
|
+
## Testing
|
|
267
|
+
|
|
268
|
+
Every layer is covered; the commands are in
|
|
269
|
+
[Building from source](#building-from-source).
|
|
270
|
+
|
|
271
|
+
- `wickra-backtest-core` — 113 unit tests: hand-computed round trips (entry at
|
|
272
|
+
the next open, exit on the signal after it), every sizing model, the cost and
|
|
273
|
+
slippage models, intrabar stops and liquidation, funding, the rule evaluator,
|
|
274
|
+
bounded history, and the feed requirements a spec declares. Plus five
|
|
275
|
+
integration suites: property tests, the shipped example specs, and the three
|
|
276
|
+
golden runners (batch, streaming, and the microstructure requests).
|
|
277
|
+
- `wickra-backtest-data` — 17 unit tests over CSV, JSONL, JSON-array and Binance
|
|
278
|
+
kline decoding, plus the resamplers.
|
|
279
|
+
- `bindings/c` — 12 Rust tests driving the ABI itself, including the streaming
|
|
280
|
+
handle's lifecycle and every error path, so a null or finished handle is
|
|
281
|
+
proven to be reported rather than dereferenced.
|
|
282
|
+
- `bindings/python` — 21 pytest cases: smoke, streaming, golden parity,
|
|
283
|
+
completeness of the module and class surface, and the feed path.
|
|
284
|
+
- `bindings/node` — 19 `node --test` cases, same shape.
|
|
285
|
+
- `bindings/wasm` — 8 `node --test` cases against the built package.
|
|
286
|
+
- `bindings/csharp` — 15 xUnit cases. `bindings/java` — 15 JUnit cases.
|
|
287
|
+
`bindings/go` — 15 `go test` cases. `bindings/r` — 3 script suites.
|
|
288
|
+
- `fuzz/` — five targets covering the whole untrusted-input surface: the spec
|
|
289
|
+
parser, the JSON request, the engine loop, the fill model and the data loader.
|
|
290
|
+
|
|
291
|
+
On top of those, **all ten languages** replay a shared, language-neutral golden
|
|
292
|
+
corpus — four OHLCV cases and five microstructure requests in `golden/` — and
|
|
293
|
+
assert equality with the Rust reference report. Since the streaming work, each
|
|
294
|
+
also replays the corpus **one bar at a time** and asserts the same report, so
|
|
295
|
+
the claim that a backtest and a live loop agree is pinned per language rather
|
|
296
|
+
than argued.
|
|
297
|
+
|
|
298
|
+
> **What "parity" means here, precisely.** The reports are compared
|
|
299
|
+
> **byte for byte**, not to a tolerance. That is possible because every binding
|
|
300
|
+
> calls the same Rust engine — the arithmetic is not reimplemented anywhere — and
|
|
301
|
+
> because the indicators the corpus names use only IEEE-754 arithmetic, which
|
|
302
|
+
> every conforming platform rounds identically. It is not free, though: a spec
|
|
303
|
+
> can name any indicator in the core, and some of those call a transcendental
|
|
304
|
+
> from the platform's math library (`ln`, `atan`, `exp` and friends). No
|
|
305
|
+
> mainstream libm rounds those correctly, and implementations differ in the last
|
|
306
|
+
> bit — the sibling indicator library measured a one-ulp difference on 24 of 67
|
|
307
|
+
> bars for a single indicator. A golden case built on one of those would have to
|
|
308
|
+
> compare to a relative tolerance instead. None currently does, and that is a
|
|
309
|
+
> property of the corpus worth keeping deliberately rather than by accident.
|
|
310
|
+
|
|
311
|
+
## Requirements
|
|
312
|
+
|
|
313
|
+
The minimum supported version per language. The same engine kernel runs behind
|
|
314
|
+
every binding; the C-ABI bindings that compile on install — Go (cgo) and R
|
|
315
|
+
(`.Call`) — also need a C compiler, and Java runs with
|
|
316
|
+
`--enable-native-access=ALL-UNNAMED`.
|
|
317
|
+
|
|
318
|
+
| Language | Package | Minimum supported |
|
|
319
|
+
|----------|-------------------------------------------|----------------------------|
|
|
320
|
+
| Rust | crates.io · `wickra-backtest` | 1.86 (MSRV) |
|
|
321
|
+
| Python | PyPI · `wickra-backtest` (abi3 wheel) | 3.9 (tested through 3.13) |
|
|
322
|
+
| Node.js | npm · `wickra-backtest` (N-API 8) | 22 (tested on 22 · 24 LTS) |
|
|
323
|
+
| WASM | npm · `wickra-backtest-wasm` | any modern JS engine |
|
|
324
|
+
| C | `wickra_backtest.h` + library (releases) | C99 compiler |
|
|
325
|
+
| C++ | the C ABI + optional `wickra_backtest.hpp` | C++14 compiler |
|
|
326
|
+
| C# | NuGet · `Wickra.Backtest` | .NET 8 (`net8.0`) |
|
|
327
|
+
| Go | module · `wickra-lib/wickra-backtest-go` | Go 1.23 (cgo) |
|
|
328
|
+
| Java | Maven Central · `org.wickra:wickra-backtest` | Java 22 (FFM / Panama) |
|
|
329
|
+
| R | r-universe · `wickrabacktest` | R ≥ 2.10 (Rtools on Win.) |
|
|
330
|
+
|
|
331
|
+
## Ecosystem
|
|
332
|
+
|
|
333
|
+
Part of the [Wickra](https://github.com/wickra-lib/wickra) family — each one a
|
|
334
|
+
data-driven core with a CLI and the same ten-language binding surface:
|
|
335
|
+
|
|
336
|
+
- [**wickra**](https://github.com/wickra-lib/wickra) — the core library: 514 O(1) streaming indicators across ten languages
|
|
337
|
+
- [**wickra-exchange**](https://github.com/wickra-lib/wickra-exchange) — unified market-data + execution across ten crypto exchanges
|
|
338
|
+
- [**wickra-backtest**](https://github.com/wickra-lib/wickra-backtest) — event-driven backtester over the Wickra core
|
|
339
|
+
- [**wickra-terminal**](https://github.com/wickra-lib/wickra-terminal) — the trading terminal: a TUI and a browser renderer over the stack
|
|
340
|
+
- [**wickra-screener**](https://github.com/wickra-lib/wickra-screener) — parallel multi-symbol screening over 514 streaming indicators
|
|
341
|
+
- [**wickra-xray**](https://github.com/wickra-lib/wickra-xray) — market-microstructure explorer: footprint, order-book heatmap, liquidation map, funding/OI divergence
|
|
342
|
+
- [**wickra-radar**](https://github.com/wickra-lib/wickra-radar) — perp-universe alert radar: OI delta, funding flip, book imbalance, liquidation clusters, OI/price divergence
|
|
343
|
+
- [**wickra-copilot**](https://github.com/wickra-lib/wickra-copilot) — local market copilot grounded in real order-book, liquidation and funding microstructure
|
|
344
|
+
- [**wickra-shazam**](https://github.com/wickra-lib/wickra-shazam) — match an asset's current microstructure fingerprint against its entire history
|
|
345
|
+
|
|
346
|
+
This project's own site is [backtest.wickra.org](https://backtest.wickra.org) and
|
|
347
|
+
its in-browser demo [backtest-live.wickra.org](https://backtest-live.wickra.org).
|
|
348
|
+
The indicator core it is built on documents itself at
|
|
349
|
+
[docs.wickra.org](https://docs.wickra.org).
|
|
350
|
+
|
|
351
|
+
## Contributing
|
|
352
|
+
|
|
353
|
+
Contributions are welcome — issues, bug reports, ideas and pull requests all land
|
|
354
|
+
at <https://github.com/wickra-lib/wickra-backtest>. See
|
|
355
|
+
[CONTRIBUTING.md](CONTRIBUTING.md) for the orientation: the engine lives in
|
|
356
|
+
`crates/wickra-backtest-core`, every binding under `bindings/<lang>` keeps the
|
|
357
|
+
golden-corpus parity invariant, and `cargo fmt --all` +
|
|
358
|
+
`cargo clippy --workspace --all-targets --all-features -- -D warnings` are CI
|
|
359
|
+
gates. For larger changes, open an issue first.
|
|
360
|
+
|
|
361
|
+
## Security
|
|
362
|
+
|
|
363
|
+
Found a security issue? **Please don't open a public issue.** Report it privately
|
|
364
|
+
via the repository's *Security* tab (*"Report a vulnerability"*) or email
|
|
365
|
+
**support@wickra.org**. Full policy: [SECURITY.md](SECURITY.md).
|
|
366
|
+
|
|
367
|
+
## License
|
|
368
|
+
|
|
369
|
+
Licensed under either of
|
|
370
|
+
|
|
371
|
+
- Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or
|
|
372
|
+
<http://www.apache.org/licenses/LICENSE-2.0>)
|
|
373
|
+
- MIT license ([LICENSE-MIT](LICENSE-MIT) or <http://opensource.org/licenses/MIT>)
|
|
374
|
+
|
|
375
|
+
at your option. Use it, fork it, modify it, redistribute it — commercially or
|
|
376
|
+
not — file issues, send pull requests; all welcome.
|
|
377
|
+
|
|
378
|
+
### Contribution
|
|
379
|
+
|
|
380
|
+
Unless you explicitly state otherwise, any contribution intentionally submitted
|
|
381
|
+
for inclusion in the work by you, as defined in the Apache-2.0 license, shall be
|
|
382
|
+
dual licensed as above, without any additional terms or conditions.
|
|
383
|
+
|
|
384
|
+
## Disclaimer
|
|
385
|
+
|
|
386
|
+
Not a trading system. Backtest results are deterministic transforms of the input
|
|
387
|
+
data — they are not financial advice and are not indicative of future
|
|
388
|
+
performance. Any use in a live trading context is at your own risk. The software
|
|
389
|
+
is provided **as is**, without warranty of any kind; see the license files for
|
|
390
|
+
the full terms.
|
|
391
|
+
|
|
392
|
+
---
|
|
393
|
+
|
|
394
|
+
<p align="center">
|
|
395
|
+
Built on <a href="https://github.com/wickra-lib/wickra">Wickra</a>. If it saved you time, ⭐ the repo.
|
|
396
|
+
</p>
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wickra-backtest-wasm",
|
|
3
|
+
"type": "module",
|
|
4
|
+
"collaborators": [
|
|
5
|
+
"kingchenc <support@wickra.org>"
|
|
6
|
+
],
|
|
7
|
+
"description": "WebAssembly bindings for the wickra-backtest streaming backtester — backtest in the browser.",
|
|
8
|
+
"version": "0.1.0",
|
|
9
|
+
"license": "MIT OR Apache-2.0",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/wickra-lib/wickra-backtest"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"wickra_backtest_wasm_bg.wasm",
|
|
16
|
+
"wickra_backtest_wasm.js",
|
|
17
|
+
"wickra_backtest_wasm_bg.js",
|
|
18
|
+
"wickra_backtest_wasm.d.ts",
|
|
19
|
+
"LICENSE-MIT",
|
|
20
|
+
"LICENSE-APACHE"
|
|
21
|
+
],
|
|
22
|
+
"main": "wickra_backtest_wasm.js",
|
|
23
|
+
"homepage": "https://github.com/wickra-lib/wickra-backtest",
|
|
24
|
+
"types": "wickra_backtest_wasm.d.ts",
|
|
25
|
+
"sideEffects": [
|
|
26
|
+
"./wickra_backtest_wasm.js",
|
|
27
|
+
"./snippets/*"
|
|
28
|
+
],
|
|
29
|
+
"author": "kingchenc <support@wickra.org>",
|
|
30
|
+
"bugs": {
|
|
31
|
+
"url": "https://github.com/wickra-lib/wickra-backtest/issues"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/* tslint:disable */
|
|
2
|
+
/* eslint-disable */
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* A backtest driven one bar at a time.
|
|
6
|
+
*
|
|
7
|
+
* `run` needs the whole series up front; this drives the same engine bar by
|
|
8
|
+
* bar, so a live loop and a backtest are the same code path -- point it at a
|
|
9
|
+
* websocket instead of an array and every value it reports was produced the way
|
|
10
|
+
* the backtest produced it.
|
|
11
|
+
*
|
|
12
|
+
* The method names match the Node binding deliberately. Both are consumed from
|
|
13
|
+
* JavaScript, and a developer moving between the npm package and this one
|
|
14
|
+
* should not have to relearn the surface; the reporting methods carry the
|
|
15
|
+
* `Json` suffix because, as in Node, they return JSON strings to parse.
|
|
16
|
+
*
|
|
17
|
+
* The handle owns its spec, so it carries no borrow across steps. `finishJson`
|
|
18
|
+
* consumes the run; `close` releases one without a report. Either leaves the
|
|
19
|
+
* object inert, and wasm-bindgen's generated `free` reclaims the memory.
|
|
20
|
+
*/
|
|
21
|
+
export class StreamingBacktest {
|
|
22
|
+
free(): void;
|
|
23
|
+
[Symbol.dispose](): void;
|
|
24
|
+
/**
|
|
25
|
+
* Drop the run without producing a report. Idempotent.
|
|
26
|
+
*/
|
|
27
|
+
close(): void;
|
|
28
|
+
/**
|
|
29
|
+
* The equity curve so far, as a JSON array.
|
|
30
|
+
*/
|
|
31
|
+
equityJson(): string;
|
|
32
|
+
/**
|
|
33
|
+
* Close any open position and return the report JSON. Ends the run.
|
|
34
|
+
*/
|
|
35
|
+
finishJson(): string;
|
|
36
|
+
/**
|
|
37
|
+
* The most recent equity point as JSON, or `null` before the first bar.
|
|
38
|
+
*/
|
|
39
|
+
latestEquityJson(): string;
|
|
40
|
+
/**
|
|
41
|
+
* Build from a JSON strategy spec and starting capital.
|
|
42
|
+
*/
|
|
43
|
+
constructor(spec_json: string, capital: number);
|
|
44
|
+
/**
|
|
45
|
+
* Advance by one OHLCV bar. `volume` defaults to 0 and `time` to the number
|
|
46
|
+
* of bars fed so far.
|
|
47
|
+
*/
|
|
48
|
+
step(open: number, high: number, low: number, close: number, volume?: number | null, time?: number | null): void;
|
|
49
|
+
/**
|
|
50
|
+
* Advance by one bar described as a request document:
|
|
51
|
+
* `{"candle": {...}, "feeds": {...}}`, where `feeds` optionally carries this
|
|
52
|
+
* bar's `reference` / `deriv` / `orderbook` / `trades` / `cross_section`.
|
|
53
|
+
* This is the only form that can drive a strategy reading a side feed.
|
|
54
|
+
*/
|
|
55
|
+
stepJson(step_json: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Whether the run has been finished or closed.
|
|
58
|
+
*/
|
|
59
|
+
readonly isFinished: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* The number of closed trades so far.
|
|
62
|
+
*/
|
|
63
|
+
readonly numTrades: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Run a backtest. Returns the report as a JSON string.
|
|
68
|
+
*/
|
|
69
|
+
export function run(open: Float64Array, high: Float64Array, low: Float64Array, close: Float64Array, volume: Float64Array, time: Float64Array, spec_json: string, capital: number): string;
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Run a backtest from a single JSON request bundling candles, spec and optional
|
|
73
|
+
* feeds. Returns the report as a JSON string.
|
|
74
|
+
*/
|
|
75
|
+
export function run_json(request_json: string): string;
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* The crate version.
|
|
79
|
+
*/
|
|
80
|
+
export function version(): string;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/* @ts-self-types="./wickra_backtest_wasm.d.ts" */
|
|
2
|
+
import * as wasm from "./wickra_backtest_wasm_bg.wasm";
|
|
3
|
+
import { __wbg_set_wasm } from "./wickra_backtest_wasm_bg.js";
|
|
4
|
+
|
|
5
|
+
__wbg_set_wasm(wasm);
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
StreamingBacktest, run, run_json, version
|
|
9
|
+
} from "./wickra_backtest_wasm_bg.js";
|
|
@@ -0,0 +1,470 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A backtest driven one bar at a time.
|
|
3
|
+
*
|
|
4
|
+
* `run` needs the whole series up front; this drives the same engine bar by
|
|
5
|
+
* bar, so a live loop and a backtest are the same code path -- point it at a
|
|
6
|
+
* websocket instead of an array and every value it reports was produced the way
|
|
7
|
+
* the backtest produced it.
|
|
8
|
+
*
|
|
9
|
+
* The method names match the Node binding deliberately. Both are consumed from
|
|
10
|
+
* JavaScript, and a developer moving between the npm package and this one
|
|
11
|
+
* should not have to relearn the surface; the reporting methods carry the
|
|
12
|
+
* `Json` suffix because, as in Node, they return JSON strings to parse.
|
|
13
|
+
*
|
|
14
|
+
* The handle owns its spec, so it carries no borrow across steps. `finishJson`
|
|
15
|
+
* consumes the run; `close` releases one without a report. Either leaves the
|
|
16
|
+
* object inert, and wasm-bindgen's generated `free` reclaims the memory.
|
|
17
|
+
*/
|
|
18
|
+
export class StreamingBacktest {
|
|
19
|
+
__destroy_into_raw() {
|
|
20
|
+
const ptr = this.__wbg_ptr;
|
|
21
|
+
this.__wbg_ptr = 0;
|
|
22
|
+
StreamingBacktestFinalization.unregister(this);
|
|
23
|
+
return ptr;
|
|
24
|
+
}
|
|
25
|
+
free() {
|
|
26
|
+
const ptr = this.__destroy_into_raw();
|
|
27
|
+
wasm.__wbg_streamingbacktest_free(ptr, 0);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Drop the run without producing a report. Idempotent.
|
|
31
|
+
*/
|
|
32
|
+
close() {
|
|
33
|
+
wasm.streamingbacktest_close(this.__wbg_ptr);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* The equity curve so far, as a JSON array.
|
|
37
|
+
* @returns {string}
|
|
38
|
+
*/
|
|
39
|
+
equityJson() {
|
|
40
|
+
let deferred2_0;
|
|
41
|
+
let deferred2_1;
|
|
42
|
+
try {
|
|
43
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
44
|
+
wasm.streamingbacktest_equityJson(retptr, this.__wbg_ptr);
|
|
45
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
46
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
47
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
48
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
49
|
+
var ptr1 = r0;
|
|
50
|
+
var len1 = r1;
|
|
51
|
+
if (r3) {
|
|
52
|
+
ptr1 = 0; len1 = 0;
|
|
53
|
+
throw takeObject(r2);
|
|
54
|
+
}
|
|
55
|
+
deferred2_0 = ptr1;
|
|
56
|
+
deferred2_1 = len1;
|
|
57
|
+
return getStringFromWasm0(ptr1, len1);
|
|
58
|
+
} finally {
|
|
59
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
60
|
+
wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Close any open position and return the report JSON. Ends the run.
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
finishJson() {
|
|
68
|
+
let deferred2_0;
|
|
69
|
+
let deferred2_1;
|
|
70
|
+
try {
|
|
71
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
72
|
+
wasm.streamingbacktest_finishJson(retptr, this.__wbg_ptr);
|
|
73
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
74
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
75
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
76
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
77
|
+
var ptr1 = r0;
|
|
78
|
+
var len1 = r1;
|
|
79
|
+
if (r3) {
|
|
80
|
+
ptr1 = 0; len1 = 0;
|
|
81
|
+
throw takeObject(r2);
|
|
82
|
+
}
|
|
83
|
+
deferred2_0 = ptr1;
|
|
84
|
+
deferred2_1 = len1;
|
|
85
|
+
return getStringFromWasm0(ptr1, len1);
|
|
86
|
+
} finally {
|
|
87
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
88
|
+
wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Whether the run has been finished or closed.
|
|
93
|
+
* @returns {boolean}
|
|
94
|
+
*/
|
|
95
|
+
get isFinished() {
|
|
96
|
+
const ret = wasm.streamingbacktest_isFinished(this.__wbg_ptr);
|
|
97
|
+
return ret !== 0;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* The most recent equity point as JSON, or `null` before the first bar.
|
|
101
|
+
* @returns {string}
|
|
102
|
+
*/
|
|
103
|
+
latestEquityJson() {
|
|
104
|
+
let deferred2_0;
|
|
105
|
+
let deferred2_1;
|
|
106
|
+
try {
|
|
107
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
108
|
+
wasm.streamingbacktest_latestEquityJson(retptr, this.__wbg_ptr);
|
|
109
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
110
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
111
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
112
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
113
|
+
var ptr1 = r0;
|
|
114
|
+
var len1 = r1;
|
|
115
|
+
if (r3) {
|
|
116
|
+
ptr1 = 0; len1 = 0;
|
|
117
|
+
throw takeObject(r2);
|
|
118
|
+
}
|
|
119
|
+
deferred2_0 = ptr1;
|
|
120
|
+
deferred2_1 = len1;
|
|
121
|
+
return getStringFromWasm0(ptr1, len1);
|
|
122
|
+
} finally {
|
|
123
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
124
|
+
wasm.__wbindgen_export3(deferred2_0, deferred2_1, 1);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Build from a JSON strategy spec and starting capital.
|
|
129
|
+
* @param {string} spec_json
|
|
130
|
+
* @param {number} capital
|
|
131
|
+
*/
|
|
132
|
+
constructor(spec_json, capital) {
|
|
133
|
+
try {
|
|
134
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
135
|
+
const ptr0 = passStringToWasm0(spec_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
136
|
+
const len0 = WASM_VECTOR_LEN;
|
|
137
|
+
wasm.streamingbacktest_new(retptr, ptr0, len0, capital);
|
|
138
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
139
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
140
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
141
|
+
if (r2) {
|
|
142
|
+
throw takeObject(r1);
|
|
143
|
+
}
|
|
144
|
+
this.__wbg_ptr = r0;
|
|
145
|
+
StreamingBacktestFinalization.register(this, this.__wbg_ptr, this);
|
|
146
|
+
return this;
|
|
147
|
+
} finally {
|
|
148
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* The number of closed trades so far.
|
|
153
|
+
* @returns {number}
|
|
154
|
+
*/
|
|
155
|
+
get numTrades() {
|
|
156
|
+
try {
|
|
157
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
158
|
+
wasm.streamingbacktest_numTrades(retptr, this.__wbg_ptr);
|
|
159
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
160
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
161
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
162
|
+
if (r2) {
|
|
163
|
+
throw takeObject(r1);
|
|
164
|
+
}
|
|
165
|
+
return r0 >>> 0;
|
|
166
|
+
} finally {
|
|
167
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Advance by one OHLCV bar. `volume` defaults to 0 and `time` to the number
|
|
172
|
+
* of bars fed so far.
|
|
173
|
+
* @param {number} open
|
|
174
|
+
* @param {number} high
|
|
175
|
+
* @param {number} low
|
|
176
|
+
* @param {number} close
|
|
177
|
+
* @param {number | null} [volume]
|
|
178
|
+
* @param {number | null} [time]
|
|
179
|
+
*/
|
|
180
|
+
step(open, high, low, close, volume, time) {
|
|
181
|
+
try {
|
|
182
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
183
|
+
wasm.streamingbacktest_step(retptr, this.__wbg_ptr, open, high, low, close, !isLikeNone(volume), isLikeNone(volume) ? 0 : volume, !isLikeNone(time), isLikeNone(time) ? 0 : time);
|
|
184
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
185
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
186
|
+
if (r1) {
|
|
187
|
+
throw takeObject(r0);
|
|
188
|
+
}
|
|
189
|
+
} finally {
|
|
190
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Advance by one bar described as a request document:
|
|
195
|
+
* `{"candle": {...}, "feeds": {...}}`, where `feeds` optionally carries this
|
|
196
|
+
* bar's `reference` / `deriv` / `orderbook` / `trades` / `cross_section`.
|
|
197
|
+
* This is the only form that can drive a strategy reading a side feed.
|
|
198
|
+
* @param {string} step_json
|
|
199
|
+
*/
|
|
200
|
+
stepJson(step_json) {
|
|
201
|
+
try {
|
|
202
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
203
|
+
const ptr0 = passStringToWasm0(step_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
204
|
+
const len0 = WASM_VECTOR_LEN;
|
|
205
|
+
wasm.streamingbacktest_stepJson(retptr, this.__wbg_ptr, ptr0, len0);
|
|
206
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
207
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
208
|
+
if (r1) {
|
|
209
|
+
throw takeObject(r0);
|
|
210
|
+
}
|
|
211
|
+
} finally {
|
|
212
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
if (Symbol.dispose) StreamingBacktest.prototype[Symbol.dispose] = StreamingBacktest.prototype.free;
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Run a backtest. Returns the report as a JSON string.
|
|
220
|
+
* @param {Float64Array} open
|
|
221
|
+
* @param {Float64Array} high
|
|
222
|
+
* @param {Float64Array} low
|
|
223
|
+
* @param {Float64Array} close
|
|
224
|
+
* @param {Float64Array} volume
|
|
225
|
+
* @param {Float64Array} time
|
|
226
|
+
* @param {string} spec_json
|
|
227
|
+
* @param {number} capital
|
|
228
|
+
* @returns {string}
|
|
229
|
+
*/
|
|
230
|
+
export function run(open, high, low, close, volume, time, spec_json, capital) {
|
|
231
|
+
let deferred9_0;
|
|
232
|
+
let deferred9_1;
|
|
233
|
+
try {
|
|
234
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
235
|
+
const ptr0 = passArrayF64ToWasm0(open, wasm.__wbindgen_export);
|
|
236
|
+
const len0 = WASM_VECTOR_LEN;
|
|
237
|
+
const ptr1 = passArrayF64ToWasm0(high, wasm.__wbindgen_export);
|
|
238
|
+
const len1 = WASM_VECTOR_LEN;
|
|
239
|
+
const ptr2 = passArrayF64ToWasm0(low, wasm.__wbindgen_export);
|
|
240
|
+
const len2 = WASM_VECTOR_LEN;
|
|
241
|
+
const ptr3 = passArrayF64ToWasm0(close, wasm.__wbindgen_export);
|
|
242
|
+
const len3 = WASM_VECTOR_LEN;
|
|
243
|
+
const ptr4 = passArrayF64ToWasm0(volume, wasm.__wbindgen_export);
|
|
244
|
+
const len4 = WASM_VECTOR_LEN;
|
|
245
|
+
const ptr5 = passArrayF64ToWasm0(time, wasm.__wbindgen_export);
|
|
246
|
+
const len5 = WASM_VECTOR_LEN;
|
|
247
|
+
const ptr6 = passStringToWasm0(spec_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
248
|
+
const len6 = WASM_VECTOR_LEN;
|
|
249
|
+
wasm.run(retptr, ptr0, len0, ptr1, len1, ptr2, len2, ptr3, len3, ptr4, len4, ptr5, len5, ptr6, len6, capital);
|
|
250
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
251
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
252
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
253
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
254
|
+
var ptr8 = r0;
|
|
255
|
+
var len8 = r1;
|
|
256
|
+
if (r3) {
|
|
257
|
+
ptr8 = 0; len8 = 0;
|
|
258
|
+
throw takeObject(r2);
|
|
259
|
+
}
|
|
260
|
+
deferred9_0 = ptr8;
|
|
261
|
+
deferred9_1 = len8;
|
|
262
|
+
return getStringFromWasm0(ptr8, len8);
|
|
263
|
+
} finally {
|
|
264
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
265
|
+
wasm.__wbindgen_export3(deferred9_0, deferred9_1, 1);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Run a backtest from a single JSON request bundling candles, spec and optional
|
|
271
|
+
* feeds. Returns the report as a JSON string.
|
|
272
|
+
* @param {string} request_json
|
|
273
|
+
* @returns {string}
|
|
274
|
+
*/
|
|
275
|
+
export function run_json(request_json) {
|
|
276
|
+
let deferred3_0;
|
|
277
|
+
let deferred3_1;
|
|
278
|
+
try {
|
|
279
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
280
|
+
const ptr0 = passStringToWasm0(request_json, wasm.__wbindgen_export, wasm.__wbindgen_export2);
|
|
281
|
+
const len0 = WASM_VECTOR_LEN;
|
|
282
|
+
wasm.run_json(retptr, ptr0, len0);
|
|
283
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
284
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
285
|
+
var r2 = getDataViewMemory0().getInt32(retptr + 4 * 2, true);
|
|
286
|
+
var r3 = getDataViewMemory0().getInt32(retptr + 4 * 3, true);
|
|
287
|
+
var ptr2 = r0;
|
|
288
|
+
var len2 = r1;
|
|
289
|
+
if (r3) {
|
|
290
|
+
ptr2 = 0; len2 = 0;
|
|
291
|
+
throw takeObject(r2);
|
|
292
|
+
}
|
|
293
|
+
deferred3_0 = ptr2;
|
|
294
|
+
deferred3_1 = len2;
|
|
295
|
+
return getStringFromWasm0(ptr2, len2);
|
|
296
|
+
} finally {
|
|
297
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
298
|
+
wasm.__wbindgen_export3(deferred3_0, deferred3_1, 1);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* The crate version.
|
|
304
|
+
* @returns {string}
|
|
305
|
+
*/
|
|
306
|
+
export function version() {
|
|
307
|
+
let deferred1_0;
|
|
308
|
+
let deferred1_1;
|
|
309
|
+
try {
|
|
310
|
+
const retptr = wasm.__wbindgen_add_to_stack_pointer(-16);
|
|
311
|
+
wasm.version(retptr);
|
|
312
|
+
var r0 = getDataViewMemory0().getInt32(retptr + 4 * 0, true);
|
|
313
|
+
var r1 = getDataViewMemory0().getInt32(retptr + 4 * 1, true);
|
|
314
|
+
deferred1_0 = r0;
|
|
315
|
+
deferred1_1 = r1;
|
|
316
|
+
return getStringFromWasm0(r0, r1);
|
|
317
|
+
} finally {
|
|
318
|
+
wasm.__wbindgen_add_to_stack_pointer(16);
|
|
319
|
+
wasm.__wbindgen_export3(deferred1_0, deferred1_1, 1);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
export function __wbg_Error_92b29b0548f8b746(arg0, arg1) {
|
|
323
|
+
const ret = Error(getStringFromWasm0(arg0, arg1));
|
|
324
|
+
return addHeapObject(ret);
|
|
325
|
+
}
|
|
326
|
+
export function __wbg___wbindgen_throw_344f42d3211c4765(arg0, arg1) {
|
|
327
|
+
throw new Error(getStringFromWasm0(arg0, arg1));
|
|
328
|
+
}
|
|
329
|
+
const StreamingBacktestFinalization = (typeof FinalizationRegistry === 'undefined')
|
|
330
|
+
? { register: () => {}, unregister: () => {} }
|
|
331
|
+
: new FinalizationRegistry(ptr => wasm.__wbg_streamingbacktest_free(ptr, 1));
|
|
332
|
+
|
|
333
|
+
function addHeapObject(obj) {
|
|
334
|
+
if (heap_next === heap.length) heap.push(heap.length + 1);
|
|
335
|
+
const idx = heap_next;
|
|
336
|
+
heap_next = heap[idx];
|
|
337
|
+
|
|
338
|
+
heap[idx] = obj;
|
|
339
|
+
return idx;
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
function dropObject(idx) {
|
|
343
|
+
if (idx < 1028) return;
|
|
344
|
+
heap[idx] = heap_next;
|
|
345
|
+
heap_next = idx;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
let cachedDataViewMemory0 = null;
|
|
349
|
+
function getDataViewMemory0() {
|
|
350
|
+
if (cachedDataViewMemory0 === null || cachedDataViewMemory0.buffer.detached === true || (cachedDataViewMemory0.buffer.detached === undefined && cachedDataViewMemory0.buffer !== wasm.memory.buffer)) {
|
|
351
|
+
cachedDataViewMemory0 = new DataView(wasm.memory.buffer);
|
|
352
|
+
}
|
|
353
|
+
return cachedDataViewMemory0;
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
let cachedFloat64ArrayMemory0 = null;
|
|
357
|
+
function getFloat64ArrayMemory0() {
|
|
358
|
+
if (cachedFloat64ArrayMemory0 === null || cachedFloat64ArrayMemory0.byteLength === 0) {
|
|
359
|
+
cachedFloat64ArrayMemory0 = new Float64Array(wasm.memory.buffer);
|
|
360
|
+
}
|
|
361
|
+
return cachedFloat64ArrayMemory0;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
function getStringFromWasm0(ptr, len) {
|
|
365
|
+
return decodeText(ptr >>> 0, len);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
let cachedUint8ArrayMemory0 = null;
|
|
369
|
+
function getUint8ArrayMemory0() {
|
|
370
|
+
if (cachedUint8ArrayMemory0 === null || cachedUint8ArrayMemory0.byteLength === 0) {
|
|
371
|
+
cachedUint8ArrayMemory0 = new Uint8Array(wasm.memory.buffer);
|
|
372
|
+
}
|
|
373
|
+
return cachedUint8ArrayMemory0;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
function getObject(idx) { return heap[idx]; }
|
|
377
|
+
|
|
378
|
+
let heap = new Array(1024).fill(undefined);
|
|
379
|
+
heap.push(undefined, null, true, false);
|
|
380
|
+
|
|
381
|
+
let heap_next = heap.length;
|
|
382
|
+
|
|
383
|
+
function isLikeNone(x) {
|
|
384
|
+
return x === undefined || x === null;
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
function passArrayF64ToWasm0(arg, malloc) {
|
|
388
|
+
const ptr = malloc(arg.length * 8, 8) >>> 0;
|
|
389
|
+
getFloat64ArrayMemory0().set(arg, ptr / 8);
|
|
390
|
+
WASM_VECTOR_LEN = arg.length;
|
|
391
|
+
return ptr;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
function passStringToWasm0(arg, malloc, realloc) {
|
|
395
|
+
if (realloc === undefined) {
|
|
396
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
397
|
+
const ptr = malloc(buf.length, 1) >>> 0;
|
|
398
|
+
getUint8ArrayMemory0().subarray(ptr, ptr + buf.length).set(buf);
|
|
399
|
+
WASM_VECTOR_LEN = buf.length;
|
|
400
|
+
return ptr;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
let len = arg.length;
|
|
404
|
+
let ptr = malloc(len, 1) >>> 0;
|
|
405
|
+
|
|
406
|
+
const mem = getUint8ArrayMemory0();
|
|
407
|
+
|
|
408
|
+
let offset = 0;
|
|
409
|
+
|
|
410
|
+
for (; offset < len; offset++) {
|
|
411
|
+
const code = arg.charCodeAt(offset);
|
|
412
|
+
if (code > 0x7F) break;
|
|
413
|
+
mem[ptr + offset] = code;
|
|
414
|
+
}
|
|
415
|
+
if (offset !== len) {
|
|
416
|
+
if (offset !== 0) {
|
|
417
|
+
arg = arg.slice(offset);
|
|
418
|
+
}
|
|
419
|
+
ptr = realloc(ptr, len, len = offset + arg.length * 3, 1) >>> 0;
|
|
420
|
+
const view = getUint8ArrayMemory0().subarray(ptr + offset, ptr + len);
|
|
421
|
+
const ret = cachedTextEncoder.encodeInto(arg, view);
|
|
422
|
+
|
|
423
|
+
offset += ret.written;
|
|
424
|
+
ptr = realloc(ptr, len, offset, 1) >>> 0;
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
WASM_VECTOR_LEN = offset;
|
|
428
|
+
return ptr;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function takeObject(idx) {
|
|
432
|
+
const ret = getObject(idx);
|
|
433
|
+
dropObject(idx);
|
|
434
|
+
return ret;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
let cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
438
|
+
cachedTextDecoder.decode();
|
|
439
|
+
const MAX_SAFARI_DECODE_BYTES = 2146435072;
|
|
440
|
+
let numBytesDecoded = 0;
|
|
441
|
+
function decodeText(ptr, len) {
|
|
442
|
+
numBytesDecoded += len;
|
|
443
|
+
if (numBytesDecoded >= MAX_SAFARI_DECODE_BYTES) {
|
|
444
|
+
cachedTextDecoder = new TextDecoder('utf-8', { ignoreBOM: true, fatal: true });
|
|
445
|
+
cachedTextDecoder.decode();
|
|
446
|
+
numBytesDecoded = len;
|
|
447
|
+
}
|
|
448
|
+
return cachedTextDecoder.decode(getUint8ArrayMemory0().subarray(ptr, ptr + len));
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
const cachedTextEncoder = new TextEncoder();
|
|
452
|
+
|
|
453
|
+
if (!('encodeInto' in cachedTextEncoder)) {
|
|
454
|
+
cachedTextEncoder.encodeInto = function (arg, view) {
|
|
455
|
+
const buf = cachedTextEncoder.encode(arg);
|
|
456
|
+
view.set(buf);
|
|
457
|
+
return {
|
|
458
|
+
read: arg.length,
|
|
459
|
+
written: buf.length
|
|
460
|
+
};
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
let WASM_VECTOR_LEN = 0;
|
|
465
|
+
|
|
466
|
+
|
|
467
|
+
let wasm;
|
|
468
|
+
export function __wbg_set_wasm(val) {
|
|
469
|
+
wasm = val;
|
|
470
|
+
}
|
|
Binary file
|