@steerprotocol/strategy-utils 2.1.9 → 2.2.1
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.
|
@@ -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
|
+
}
|
package/assembly/utils/Math.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export function _getMax(arr: Array<
|
|
1
|
+
export function _getMax(arr: Array<f64>): f64 {
|
|
2
2
|
let max = arr[0];
|
|
3
3
|
for (let i = 1; i < arr.length; i++) {
|
|
4
4
|
if (arr[i] > max) {
|
|
@@ -8,36 +8,41 @@ export function _getMax(arr: Array<f32>): f32 {
|
|
|
8
8
|
return max;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
export function getMax(a:
|
|
11
|
+
export function getMax(a: f64, b: f64): f64 {
|
|
12
12
|
if (a >= b) return a
|
|
13
13
|
return b
|
|
14
14
|
}
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
export function _normalDensity(std:
|
|
18
|
-
return
|
|
19
|
-
(
|
|
20
|
-
Math.sqrt(2 *
|
|
17
|
+
export function _normalDensity(std: f64, mean: f64, x: f64): f64 {
|
|
18
|
+
return f64(
|
|
19
|
+
(f64(Math.E) ** (((x - mean) / std) ** 2 / -2) / std) *
|
|
20
|
+
Math.sqrt(2 * f64(Math.PI))
|
|
21
21
|
);
|
|
22
22
|
}
|
|
23
23
|
|
|
24
|
-
export function _standardDeviation(list:
|
|
24
|
+
export function _standardDeviation(list:f64[]): f64 {
|
|
25
25
|
const mean = _mean(list)
|
|
26
|
-
const sqrdDiff:
|
|
26
|
+
const sqrdDiff: f64[] = []
|
|
27
27
|
for (let i = 0; i < list.length; i++){
|
|
28
28
|
sqrdDiff.push((list[i]-mean)*(list[i]-mean))
|
|
29
29
|
}
|
|
30
30
|
const variance = _mean(sqrdDiff)
|
|
31
31
|
const stddev = Math.sqrt(variance)
|
|
32
|
-
return
|
|
32
|
+
return f64(stddev)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
// SMA simple moving average
|
|
36
|
-
export function _mean(list:
|
|
36
|
+
export function _mean(list: f64[]): f64 {
|
|
37
37
|
const length = list.length;
|
|
38
|
-
let total:
|
|
38
|
+
let total: f64 = 0.0;
|
|
39
39
|
for (let i = 0; i < length; i++){
|
|
40
40
|
total += list[i]
|
|
41
41
|
}
|
|
42
|
-
return total /
|
|
42
|
+
return total / f64(length)
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function closestDivisibleNumber(num: number, divisor: number, floor: boolean): number {
|
|
46
|
+
if (floor) return Math.floor(num / divisor) * divisor;
|
|
47
|
+
return Math.ceil(num / divisor) * divisor;
|
|
43
48
|
}
|
|
@@ -1,18 +1,18 @@
|
|
|
1
1
|
export class SMA {
|
|
2
|
-
private readonly prices:
|
|
2
|
+
private readonly prices: f64[] = [];
|
|
3
3
|
private interval: i32 = 0;
|
|
4
|
-
private result:
|
|
5
|
-
private prevEMA:
|
|
4
|
+
private result: f64 = 0
|
|
5
|
+
// private prevEMA: f64;
|
|
6
6
|
|
|
7
7
|
constructor(interval: i32) {
|
|
8
8
|
this.interval = interval;
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
-
getResult():
|
|
11
|
+
getResult(): f64 {
|
|
12
12
|
return this.result;
|
|
13
13
|
}
|
|
14
14
|
|
|
15
|
-
update(price:
|
|
15
|
+
update(price: f64): void {
|
|
16
16
|
this.prices.push(price);
|
|
17
17
|
|
|
18
18
|
if (this.prices.length > this.interval) {
|
|
@@ -20,32 +20,32 @@ export class SMA {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
if (this.prices.length === this.interval) {
|
|
23
|
-
let result =
|
|
23
|
+
let result = f64(0);
|
|
24
24
|
for (let priceIndex = 0; priceIndex < this.prices.length; priceIndex++) {
|
|
25
25
|
result = result + this.prices[priceIndex];
|
|
26
26
|
}
|
|
27
|
-
this.result = result /
|
|
27
|
+
this.result = result / f64(this.prices.length || 1);
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export class EMA {
|
|
33
|
-
private readonly prices:
|
|
33
|
+
private readonly prices: f64[] = [];
|
|
34
34
|
private interval: i32 = 0;
|
|
35
|
-
private result:
|
|
35
|
+
private result: f64 = 0
|
|
36
36
|
private multiplier: i32;
|
|
37
|
-
private prevEMA:
|
|
37
|
+
private prevEMA: f64 = 0
|
|
38
38
|
|
|
39
39
|
constructor(interval: i32, multiplier: i32) {
|
|
40
40
|
this.interval = interval;
|
|
41
41
|
this.multiplier = multiplier;
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
-
getResult():
|
|
44
|
+
getResult(): f64 {
|
|
45
45
|
return this.result;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
|
-
update(price:
|
|
48
|
+
update(price: f64): void {
|
|
49
49
|
this.prices.push(price);
|
|
50
50
|
|
|
51
51
|
if (this.prices.length > this.interval) {
|
|
@@ -56,11 +56,11 @@ export class EMA {
|
|
|
56
56
|
this.prevEMA = price;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
let p1 = price * (
|
|
59
|
+
let p1 = price * (f64(this.multiplier) / (1 + f64(this.interval)));
|
|
60
60
|
let p2 =
|
|
61
|
-
this.prevEMA * (1 -
|
|
61
|
+
this.prevEMA * (1 - f64(this.multiplier) / (1 + f64(this.interval)));
|
|
62
62
|
|
|
63
|
-
this.result =
|
|
63
|
+
this.result = f64(p1 + p2);
|
|
64
64
|
this.prevEMA = this.result;
|
|
65
65
|
}
|
|
66
66
|
}
|
package/assembly/utils/Ranges.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { Candle } from "./types/Candle";
|
|
2
2
|
import { getMax, _mean, _getMax } from "./Math";
|
|
3
3
|
|
|
4
|
-
export function getAverageTrueRange(candles: Array<Candle>, interval: i32):
|
|
5
|
-
let rangeSum:
|
|
4
|
+
export function getAverageTrueRange(candles: Array<Candle>, interval: i32): f64 {
|
|
5
|
+
let rangeSum: f64 = 0;
|
|
6
6
|
for (let i = 1; i < candles.length; i++) {
|
|
7
7
|
const currentPrice = candles[i];
|
|
8
8
|
|
|
@@ -14,34 +14,34 @@ export function getAverageTrueRange(candles: Array<Candle>, interval: i32): f32
|
|
|
14
14
|
const range2 = Math.abs(currentLow - previousClose);
|
|
15
15
|
const range3 = Math.abs(currentHigh - currentLow);
|
|
16
16
|
|
|
17
|
-
let max = _getMax([
|
|
17
|
+
let max = _getMax([f64(range1), f64(range2), f64(range3)]);
|
|
18
18
|
rangeSum += max;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
return
|
|
22
|
+
return f64(rangeSum) / f64(candles.length - 1);
|
|
23
23
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
export function trailingStop(percent:
|
|
27
|
+
export function trailingStop(percent: f64, prices: Candle[]): f64 {
|
|
28
28
|
// Get the current price of the asset pair
|
|
29
29
|
const currentPrice = prices[prices.length - 1];
|
|
30
30
|
|
|
31
31
|
// Calculate the trailing stop price
|
|
32
|
-
const trailingStopPrice = currentPrice.close - (currentPrice.close *
|
|
32
|
+
const trailingStopPrice = currentPrice.close - (currentPrice.close * f64(percent / 100));
|
|
33
33
|
|
|
34
34
|
// Return the trailing stop price
|
|
35
|
-
return
|
|
35
|
+
return f64(trailingStopPrice);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function trueRange(price: Candle):
|
|
39
|
-
const trueRange = getMax(
|
|
38
|
+
export function trueRange(price: Candle): f64{
|
|
39
|
+
const trueRange = getMax(f64(price.high) - f64(price.low),getMax(f64(Math.abs(price.high-price.close)),f64(Math.abs(price.low-price.close))));
|
|
40
40
|
return trueRange;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function averageTrueRange(prices: Candle[]):
|
|
44
|
-
const trueRanges:
|
|
43
|
+
export function averageTrueRange(prices: Candle[]): f64 {
|
|
44
|
+
const trueRanges: f64[] = []
|
|
45
45
|
for (let i = 0; i < prices.length; i++) {
|
|
46
46
|
trueRanges.push(trueRange(prices[i]));
|
|
47
47
|
}
|
package/assembly/utils/env.ts
CHANGED
|
@@ -1 +1,2 @@
|
|
|
1
|
-
export declare function generateCandles(data: string, candleSize: string): string
|
|
1
|
+
export declare function generateCandles(data: string, candleSize: string): string
|
|
2
|
+
export declare function ccxt_fetchOHLCV(exchangeId: string, symbol: string, timeframe: string, limit: number, since: number): string
|
|
@@ -3,13 +3,13 @@ import { JSON } from 'json-as/assembly';
|
|
|
3
3
|
@serializable
|
|
4
4
|
export class Candle {
|
|
5
5
|
timestamp: i64 = 0;
|
|
6
|
-
high:
|
|
7
|
-
low:
|
|
8
|
-
open:
|
|
9
|
-
close:
|
|
10
|
-
volume:
|
|
6
|
+
high: f64 = 0.0;
|
|
7
|
+
low: f64 = 0.0;
|
|
8
|
+
open: f64 = 0.0;
|
|
9
|
+
close: f64 = 0.0;
|
|
10
|
+
volume: f64 = 0.0;
|
|
11
11
|
|
|
12
|
-
constructor(timestamp: i64, high:
|
|
12
|
+
constructor(timestamp: i64, high: f64, low: f64, open: f64, close: f64, volume: f64) {
|
|
13
13
|
this.timestamp = timestamp;
|
|
14
14
|
this.high = high;
|
|
15
15
|
this.low = low;
|