@steerprotocol/strategy-utils 2.2.0 → 3.0.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/.github/workflows/nodejs.yml +9 -9
- package/CHANGELOG.md +28 -0
- package/assembly/utils/CandleGenerator.ts +60 -0
- package/assembly/utils/MarketFeedAggregator.ts +140 -0
- package/assembly/utils/Ranges.ts +1 -1
- package/assembly/utils/env.ts +23 -1
- package/assembly/utils/triggers.ts +469 -0
- package/index.js +11 -11
- package/package.json +6 -3
- package/readme.md +387 -0
- package/tests/debug.wasm +0 -0
- package/tests/index.test.ts +236 -26
- package/tests/utils.ts +607 -0
- package/.vscode/settings.json +0 -8
|
@@ -25,14 +25,14 @@ jobs:
|
|
|
25
25
|
if: steps.node-cache.outputs.cache-hit != 'true'
|
|
26
26
|
run: yarn
|
|
27
27
|
|
|
28
|
-
- name: Test to see if the project compiles
|
|
29
|
-
|
|
28
|
+
# - name: Test to see if the project compiles
|
|
29
|
+
# run: yarn asbuild
|
|
30
30
|
|
|
31
31
|
- name: Perform tests
|
|
32
32
|
run: yarn test
|
|
33
33
|
|
|
34
|
-
- name: Create the docs directory locally in CI
|
|
35
|
-
|
|
34
|
+
# - name: Create the docs directory locally in CI
|
|
35
|
+
# run: yarn docs
|
|
36
36
|
|
|
37
37
|
- name: Release
|
|
38
38
|
run: npx semantic-release
|
|
@@ -40,8 +40,8 @@ jobs:
|
|
|
40
40
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
41
41
|
NPM_TOKEN: ${{ secrets.SEMANTIC_RELEASE_BOT_NPM_TOKEN }}
|
|
42
42
|
|
|
43
|
-
- name: Deploy 🚀
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
43
|
+
# - name: Deploy 🚀
|
|
44
|
+
# uses: JamesIves/github-pages-deploy-action@4.1.4
|
|
45
|
+
# with:
|
|
46
|
+
# branch: gh-pages
|
|
47
|
+
# folder: docs
|
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
## [1.2.0-alpha.2](https://github.com/SteerProtocol/strategy-utils-assemblyscript/compare/v1.2.0-alpha.1...v1.2.0-alpha.2) (2023-08-17)
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
### Features
|
|
5
|
+
|
|
6
|
+
* :sparkles: Trigger Logic Utils ([dc1058c](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/dc1058c48db7b7f1ae6ad9de321a49023859cd1e))
|
|
7
|
+
* :zap: Latest spec for bundle interfacing ([c7287a5](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/c7287a5ef5510d67b9ec884d3529fc1bda14ac3d))
|
|
8
|
+
* :zap: Trigger logic and tests ([5105832](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/510583295e2f9b94d0091ff9944daf6c84eb9196))
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
### Bug Fixes
|
|
12
|
+
|
|
13
|
+
* :white_check_mark: Fix tests ([2944e90](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/2944e90dbf4e532c934dcef325ff023e863fcef7))
|
|
14
|
+
|
|
15
|
+
## [1.2.0-alpha.1](https://github.com/SteerProtocol/strategy-utils-assemblyscript/compare/v1.1.2-alpha.1...v1.2.0-alpha.1) (2023-08-15)
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
### Features
|
|
19
|
+
|
|
20
|
+
* :zap: f32 -> f64 conversion ([19c98cb](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/19c98cb82dab0057cec790de29188c20bab09f76))
|
|
21
|
+
* asyncify support ([45234ab](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/45234ab5edfc389580f09392e9b5c47ff2cbc1dc))
|
|
22
|
+
* Integrate ccxt into strategy-utils ([bbc67b5](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/bbc67b52462163581e020b7576cc5b176102952b))
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
### Bug Fixes
|
|
26
|
+
|
|
27
|
+
* array GC issues ([15a3a36](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/15a3a36b2779ac64341867ac4d8eabe6748bd55d))
|
|
28
|
+
|
|
1
29
|
### [1.1.2-alpha.1](https://github.com/SteerProtocol/strategy-utils-assemblyscript/compare/v1.1.1...v1.1.2-alpha.1) (2022-08-15)
|
|
2
30
|
|
|
3
31
|
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { Candle } from "./types/Candle";
|
|
2
|
+
import { RawTradeData } from "./types/RawTradeData";
|
|
3
|
+
|
|
4
|
+
// CandlestickConverter class provides a static method to convert raw trade data into OHLCV format
|
|
5
|
+
export class CandlestickConverter {
|
|
6
|
+
// convertToOHLCV method accepts raw trade data and a period (default is 3600 seconds or 1 hour)
|
|
7
|
+
// and returns the data in OHLCV format
|
|
8
|
+
static convertToOHLCV(rawData: RawTradeData[], period: i32 = 3600): Candle[] {
|
|
9
|
+
// If the raw data is empty, an error is thrown
|
|
10
|
+
if (rawData.length === 0) {
|
|
11
|
+
throw new Error("Input data is empty");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Array to hold the converted data
|
|
15
|
+
let ohlcvData: Candle[] = [];
|
|
16
|
+
|
|
17
|
+
// Initialize variables
|
|
18
|
+
let i: i32 = 0;
|
|
19
|
+
let currentTimestamp: i32 = Math.floor(rawData[0].timestamp / period) * period;
|
|
20
|
+
let open: f64 = rawData[0].price;
|
|
21
|
+
let high: f64 = rawData[0].price;
|
|
22
|
+
let low: f64 = rawData[0].price;
|
|
23
|
+
let close: f64 = rawData[0].price;
|
|
24
|
+
let volume: f64 = 0.0;
|
|
25
|
+
|
|
26
|
+
// Loop through all raw data
|
|
27
|
+
while (i < rawData.length) {
|
|
28
|
+
// For each period, set the initial values of open, high, low to the closing price of the last period
|
|
29
|
+
// and reset the volume to 0
|
|
30
|
+
open = close;
|
|
31
|
+
high = close;
|
|
32
|
+
low = close;
|
|
33
|
+
volume = 0.0;
|
|
34
|
+
|
|
35
|
+
// Process all raw data within the current period
|
|
36
|
+
while (i < rawData.length && rawData[i].timestamp < currentTimestamp + period) {
|
|
37
|
+
// Set the open price to the price of the first trade in the period
|
|
38
|
+
open = i == 0 ? rawData[i].price : open;
|
|
39
|
+
// Update high and low prices
|
|
40
|
+
high = Math.max(high, rawData[i].price);
|
|
41
|
+
low = Math.min(low, rawData[i].price);
|
|
42
|
+
// Update close price to the price of the last trade in the period
|
|
43
|
+
close = rawData[i].price;
|
|
44
|
+
// Update the total volume
|
|
45
|
+
volume += rawData[i].volume;
|
|
46
|
+
i++;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Create a new Candle object with the calculated OHLCV values and add it to the array
|
|
50
|
+
let ohlcv: Candle = new Candle(currentTimestamp, open, high, low, close, volume);
|
|
51
|
+
ohlcvData.push(ohlcv);
|
|
52
|
+
|
|
53
|
+
// Move to the next period
|
|
54
|
+
currentTimestamp += period;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Return the converted data
|
|
58
|
+
return ohlcvData;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
class Candle {
|
|
2
|
+
timestamp: f64;
|
|
3
|
+
open: f64;
|
|
4
|
+
high: f64;
|
|
5
|
+
close: f64;
|
|
6
|
+
low: f64;
|
|
7
|
+
volume: f64;
|
|
8
|
+
|
|
9
|
+
constructor(
|
|
10
|
+
timestamp: f64,
|
|
11
|
+
open: f64,
|
|
12
|
+
high: f64,
|
|
13
|
+
close: f64,
|
|
14
|
+
low: f64,
|
|
15
|
+
volume: f64
|
|
16
|
+
) {
|
|
17
|
+
this.timestamp = timestamp;
|
|
18
|
+
this.open = open;
|
|
19
|
+
this.high = high;
|
|
20
|
+
this.close = close;
|
|
21
|
+
this.low = low;
|
|
22
|
+
this.volume = volume;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class MarketFeedAggregator {
|
|
27
|
+
data: Array<Candle>;
|
|
28
|
+
|
|
29
|
+
constructor(data: Array<Candle>) {
|
|
30
|
+
this.data = data;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
geometricMean(): Candle {
|
|
34
|
+
let n: f64 = f64(this.data.length);
|
|
35
|
+
let logSum = new Candle(0, 0, 0, 0, 0, 0);
|
|
36
|
+
|
|
37
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
38
|
+
let candle = this.data[i];
|
|
39
|
+
logSum.timestamp += Mathf.log(candle.timestamp);
|
|
40
|
+
logSum.open += Mathf.log(candle.open);
|
|
41
|
+
logSum.high += Mathf.log(candle.high);
|
|
42
|
+
logSum.close += Mathf.log(candle.close);
|
|
43
|
+
logSum.low += Mathf.log(candle.low);
|
|
44
|
+
logSum.volume += Mathf.log(candle.volume);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return new Candle(
|
|
48
|
+
Mathf.exp(logSum.timestamp / n),
|
|
49
|
+
Mathf.exp(logSum.open / n),
|
|
50
|
+
Mathf.exp(logSum.high / n),
|
|
51
|
+
Mathf.exp(logSum.close / n),
|
|
52
|
+
Mathf.exp(logSum.low / n),
|
|
53
|
+
Mathf.exp(logSum.volume / n)
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
timeWeightedAveragePrice(): f64 {
|
|
58
|
+
let sumProduct: f64 = 0;
|
|
59
|
+
let sumTime: f64 = 0;
|
|
60
|
+
|
|
61
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
62
|
+
let candle = this.data[i];
|
|
63
|
+
let midPrice: f64 = (candle.high + candle.low) / 2;
|
|
64
|
+
sumProduct += midPrice * candle.timestamp;
|
|
65
|
+
sumTime += candle.timestamp;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
return sumProduct / sumTime;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
volumeWeightedAveragePrice(): f64 {
|
|
72
|
+
let sumProduct: f64 = 0;
|
|
73
|
+
let sumVolume: f64 = 0;
|
|
74
|
+
|
|
75
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
76
|
+
let candle = this.data[i];
|
|
77
|
+
let midPrice: f64 = (candle.high + candle.low) / 2;
|
|
78
|
+
sumProduct += midPrice * candle.volume;
|
|
79
|
+
sumVolume += candle.volume;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return sumProduct / sumVolume;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
weightedMean(weights: Candle): Candle {
|
|
86
|
+
let sumWeights: f64 = 0;
|
|
87
|
+
let sumProduct: Candle = new Candle(0, 0, 0, 0, 0, 0);
|
|
88
|
+
|
|
89
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
90
|
+
let candle = this.data[i];
|
|
91
|
+
sumProduct.timestamp += weights.timestamp * candle.timestamp;
|
|
92
|
+
sumProduct.open += weights.open * candle.open;
|
|
93
|
+
sumProduct.high += weights.high * candle.high;
|
|
94
|
+
sumProduct.close += weights.close * candle.close;
|
|
95
|
+
sumProduct.low += weights.low * candle.low;
|
|
96
|
+
sumProduct.volume += weights.volume * candle.volume;
|
|
97
|
+
|
|
98
|
+
sumWeights +=
|
|
99
|
+
weights.timestamp +
|
|
100
|
+
weights.open +
|
|
101
|
+
weights.high +
|
|
102
|
+
weights.close +
|
|
103
|
+
weights.low +
|
|
104
|
+
weights.volume;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return new Candle(
|
|
108
|
+
sumProduct.timestamp / sumWeights,
|
|
109
|
+
sumProduct.open / sumWeights,
|
|
110
|
+
sumProduct.high / sumWeights,
|
|
111
|
+
sumProduct.close / sumWeights,
|
|
112
|
+
sumProduct.low / sumWeights,
|
|
113
|
+
sumProduct.volume / sumWeights
|
|
114
|
+
);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
arithmeticMean(): Candle {
|
|
118
|
+
let n: f64 = f64(this.data.length);
|
|
119
|
+
let sum: Candle = new Candle(0, 0, 0, 0, 0, 0);
|
|
120
|
+
|
|
121
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
122
|
+
let candle = this.data[i];
|
|
123
|
+
sum.timestamp += candle.timestamp;
|
|
124
|
+
sum.open += candle.open;
|
|
125
|
+
sum.high += candle.high;
|
|
126
|
+
sum.close += candle.close;
|
|
127
|
+
sum.low += candle.low;
|
|
128
|
+
sum.volume += candle.volume;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return new Candle(
|
|
132
|
+
sum.timestamp / n,
|
|
133
|
+
sum.open / n,
|
|
134
|
+
sum.high / n,
|
|
135
|
+
sum.close / n,
|
|
136
|
+
sum.low / n,
|
|
137
|
+
sum.volume / n
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
}
|
package/assembly/utils/Ranges.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Candle } from "./types/Candle";
|
|
2
2
|
import { getMax, _mean, _getMax } from "./Math";
|
|
3
3
|
|
|
4
|
-
export function getAverageTrueRange(candles: Array<Candle
|
|
4
|
+
export function getAverageTrueRange(candles: Array<Candle>): f64 {
|
|
5
5
|
let rangeSum: f64 = 0;
|
|
6
6
|
for (let i = 1; i < candles.length; i++) {
|
|
7
7
|
const currentPrice = candles[i];
|
package/assembly/utils/env.ts
CHANGED
|
@@ -1 +1,23 @@
|
|
|
1
|
-
export declare function generateCandles(data: string, candleSize: string): string
|
|
1
|
+
export declare function generateCandles(data: string, candleSize: string): string;
|
|
2
|
+
@external("env", "ccxt_fetchOHLCV")
|
|
3
|
+
declare function _ccxt_fetchOHLCV(exchangeId: string, symbol: string, timeframe: string, limit: number, since: number): StaticArray<StaticArray<f64>>;
|
|
4
|
+
|
|
5
|
+
// Required for asyncify
|
|
6
|
+
// @ts-ignore: Global should exist here
|
|
7
|
+
@global let __ASYNCIFY_INITIALIZED = false;
|
|
8
|
+
@external("env", "_initAsyncify")
|
|
9
|
+
declare function _initAsyncify(asyncify_data_ptr: usize, stack_pointer: usize): void;
|
|
10
|
+
|
|
11
|
+
export function ccxt_fetchOHLCV(exchangeId: string, symbol: string, timeframe: string, limit: number, since: number): StaticArray<StaticArray<f64>> {
|
|
12
|
+
if (!__ASYNCIFY_INITIALIZED) {
|
|
13
|
+
// We need to initialize space for Asyncify to work.
|
|
14
|
+
// Asyncify will create a full - duplex communication channel through this bit of memory.
|
|
15
|
+
// For every asyncify-enabled function, make sure to add this
|
|
16
|
+
// memory.data() reserves a section of data that is not touched by the Garbage Collector
|
|
17
|
+
// We can only do this once or else we will cause a memory leak and eventual overflow
|
|
18
|
+
// It will not grow past the stack pointer which is where real data starts.
|
|
19
|
+
_initAsyncify(memory.data(8, 16), __stack_pointer);
|
|
20
|
+
__ASYNCIFY_INITIALIZED = true;
|
|
21
|
+
}
|
|
22
|
+
return _ccxt_fetchOHLCV(exchangeId, symbol, timeframe, limit, since);
|
|
23
|
+
}
|