@steerprotocol/strategy-utils 1.1.1 → 2.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/CHANGELOG.md +15 -0
- package/assembly/utils/MovingAverages.ts +8 -15
- package/assembly/utils/Ranges.ts +11 -11
- package/assembly/utils/UniswapLiquidityUtils.ts +46 -34
- package/assembly/utils/types/Candle.ts +41 -0
- package/assembly/utils/types/index.ts +1 -1
- package/index.js +1 -1
- package/package.json +4 -3
- package/tests/index.test.ts +8 -3
- package/assembly/utils/types/Price.ts +0 -60
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
### [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
|
+
|
|
3
|
+
|
|
4
|
+
### Bug Fixes
|
|
5
|
+
|
|
6
|
+
* :ambulance: Specific Index for parse prices ([0940640](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/09406407c7cb12bca2585976f3269414d9be731b))
|
|
7
|
+
|
|
8
|
+
### [1.1.1-alpha.1](https://github.com/SteerProtocol/strategy-utils-assemblyscript/compare/v1.1.0...v1.1.1-alpha.1) (2022-07-26)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
### Bug Fixes
|
|
13
|
+
|
|
14
|
+
* **devops:** :bug: Fix tests ([a2bb4d6](https://github.com/SteerProtocol/strategy-utils-assemblyscript/commit/a2bb4d6bdc5e3dc8bbcf6be6f26c74b8167675a7))
|
|
15
|
+
|
|
1
16
|
## [1.1.0](https://github.com/SteerProtocol/strategy-utils-assemblyscript/compare/v1.0.0...v1.1.0) (2022-07-26)
|
|
2
17
|
|
|
3
18
|
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
// WARNING: classes will initially be allocated 1 page of memory, you will likely need to add size or flatten these classes
|
|
4
1
|
export class SMA {
|
|
5
2
|
private readonly prices: f32[] = [];
|
|
6
3
|
private interval: i32 = 0;
|
|
@@ -15,7 +12,6 @@ export class SMA {
|
|
|
15
12
|
return this.result;
|
|
16
13
|
}
|
|
17
14
|
|
|
18
|
-
|
|
19
15
|
update(price: f32): void {
|
|
20
16
|
this.prices.push(price);
|
|
21
17
|
|
|
@@ -26,11 +22,10 @@ export class SMA {
|
|
|
26
22
|
if (this.prices.length === this.interval) {
|
|
27
23
|
let result = f32(0);
|
|
28
24
|
for (let priceIndex = 0; priceIndex < this.prices.length; priceIndex++) {
|
|
29
|
-
result = result + this.prices[priceIndex]
|
|
25
|
+
result = result + this.prices[priceIndex];
|
|
30
26
|
}
|
|
31
27
|
this.result = result / f32(this.prices.length || 1);
|
|
32
28
|
}
|
|
33
|
-
|
|
34
29
|
}
|
|
35
30
|
}
|
|
36
31
|
|
|
@@ -39,8 +34,7 @@ export class EMA {
|
|
|
39
34
|
private interval: i32 = 0;
|
|
40
35
|
private result: f32;
|
|
41
36
|
private multiplier: i32;
|
|
42
|
-
private prevEMA
|
|
43
|
-
|
|
37
|
+
private prevEMA: f32;
|
|
44
38
|
|
|
45
39
|
constructor(interval: i32, multiplier: i32) {
|
|
46
40
|
this.interval = interval;
|
|
@@ -56,18 +50,17 @@ export class EMA {
|
|
|
56
50
|
|
|
57
51
|
if (this.prices.length > this.interval) {
|
|
58
52
|
this.prices.shift();
|
|
59
|
-
}
|
|
53
|
+
} // remove oldest price
|
|
60
54
|
|
|
61
55
|
if (!this.prevEMA) {
|
|
62
56
|
this.prevEMA = price;
|
|
63
57
|
}
|
|
64
58
|
|
|
59
|
+
let p1 = price * (f32(this.multiplier) / (1 + f32(this.interval)));
|
|
60
|
+
let p2 =
|
|
61
|
+
this.prevEMA * (1 - f32(this.multiplier) / (1 + f32(this.interval)));
|
|
65
62
|
|
|
66
|
-
|
|
67
|
-
let p2 = this.prevEMA * (1 - (f32(this.multiplier) / (1 + f32(this.interval))))
|
|
68
|
-
|
|
69
|
-
this.result = f32(p1 + p2)
|
|
63
|
+
this.result = f32(p1 + p2);
|
|
70
64
|
this.prevEMA = this.result;
|
|
71
|
-
|
|
72
65
|
}
|
|
73
|
-
}
|
|
66
|
+
}
|
package/assembly/utils/Ranges.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { Candle } from "./types/Candle";
|
|
2
2
|
import { getMax, _mean, _getMax } from "./Math";
|
|
3
3
|
|
|
4
|
-
export function getAverageTrueRange(
|
|
4
|
+
export function getAverageTrueRange(candles: Array<Candle>, interval: i32): f32 {
|
|
5
5
|
let rangeSum: f32 = 0;
|
|
6
|
-
for (let i = 1; i <
|
|
7
|
-
const currentPrice =
|
|
6
|
+
for (let i = 1; i < candles.length; i++) {
|
|
7
|
+
const currentPrice = candles[i];
|
|
8
8
|
|
|
9
9
|
const currentHigh = currentPrice.high
|
|
10
10
|
const currentLow = currentPrice.low
|
|
11
|
-
const previousClose =
|
|
11
|
+
const previousClose = candles[i - 1].close
|
|
12
12
|
|
|
13
13
|
const range1 = Math.abs(currentHigh - previousClose);
|
|
14
14
|
const range2 = Math.abs(currentLow - previousClose);
|
|
@@ -19,12 +19,12 @@ export function getAverageTrueRange(prices: Array<Price>, interval: i32): f32 {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
|
|
22
|
-
return f32(rangeSum) / f32(
|
|
22
|
+
return f32(rangeSum) / f32(candles.length - 1);
|
|
23
23
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
|
|
27
|
-
export function trailingStop(percent: f32, prices:
|
|
27
|
+
export function trailingStop(percent: f32, prices: Candle[]): f32 {
|
|
28
28
|
// Get the current price of the asset pair
|
|
29
29
|
const currentPrice = prices[prices.length - 1];
|
|
30
30
|
|
|
@@ -32,15 +32,15 @@ export function getAverageTrueRange(prices: Array<Price>, interval: i32): f32 {
|
|
|
32
32
|
const trailingStopPrice = currentPrice.close - (currentPrice.close * f32(percent / 100));
|
|
33
33
|
|
|
34
34
|
// Return the trailing stop price
|
|
35
|
-
return trailingStopPrice;
|
|
35
|
+
return f32(trailingStopPrice);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
export function trueRange(price:
|
|
39
|
-
const trueRange = getMax(price.high - price.low,getMax(f32(Math.abs(price.high-price.close)),f32(Math.abs(price.low-price.close))));
|
|
38
|
+
export function trueRange(price: Candle): f32{
|
|
39
|
+
const trueRange = getMax(f32(price.high) - f32(price.low),getMax(f32(Math.abs(price.high-price.close)),f32(Math.abs(price.low-price.close))));
|
|
40
40
|
return trueRange;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
-
export function averageTrueRange(prices:
|
|
43
|
+
export function averageTrueRange(prices: Candle[]): f32 {
|
|
44
44
|
const trueRanges: f32[] = []
|
|
45
45
|
for (let i = 0; i < prices.length; i++) {
|
|
46
46
|
trueRanges.push(trueRange(prices[i]));
|
|
@@ -1,48 +1,60 @@
|
|
|
1
|
-
import {Position } from "./types";
|
|
1
|
+
import { Position } from "./types";
|
|
2
2
|
|
|
3
|
-
export function getTickSpacing(poolFee: i32): i32{
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
3
|
+
export function getTickSpacing(poolFee: i32): i32 {
|
|
4
|
+
let spacing = 0;
|
|
5
|
+
switch (poolFee) {
|
|
6
|
+
case 100:
|
|
7
|
+
spacing = 1;
|
|
8
|
+
break;
|
|
9
|
+
case 500:
|
|
10
|
+
spacing = 10;
|
|
11
|
+
break;
|
|
12
|
+
case 3000:
|
|
13
|
+
spacing = 60;
|
|
14
|
+
break;
|
|
15
|
+
default:
|
|
16
|
+
spacing = 200;
|
|
17
|
+
}
|
|
18
|
+
return spacing;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
// Function shaped for making positions with the UniLiquidityManager contract for ease
|
|
22
|
-
export function renderULMResult(
|
|
22
|
+
export function renderULMResult(
|
|
23
|
+
positions: Array<Position>,
|
|
24
|
+
totalLiquidity1e4: number
|
|
25
|
+
): string {
|
|
26
|
+
// Construct necessary object
|
|
27
|
+
const lowerTicks: Array<i32> = [];
|
|
28
|
+
const upperTicks: Array<i32> = [];
|
|
29
|
+
const weights: Array<i32> = [];
|
|
23
30
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
31
|
+
for (let i = 0; i < positions.length; i++) {
|
|
32
|
+
lowerTicks.push(positions[i].startTick);
|
|
33
|
+
upperTicks.push(positions[i].endTick);
|
|
34
|
+
weights.push(positions[i].weight);
|
|
35
|
+
}
|
|
28
36
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
upperTicks.push(positions[i].endTick)
|
|
32
|
-
weights.push(positions[i].weight)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
return `{"functionName":"tend(uint256,(int24[],int24[],uint16[]),bytes)",
|
|
37
|
+
return (
|
|
38
|
+
`{"functionName":"tend(uint256,(int24[],int24[],uint16[]),bytes)",
|
|
36
39
|
"typesArray":["uint256","tuple(int24[],int24[],uint16[])","bytes"],
|
|
37
|
-
"valuesArray":[
|
|
40
|
+
"valuesArray":[` +
|
|
41
|
+
totalLiquidity1e4.toString() +
|
|
42
|
+
`}, [[` +
|
|
43
|
+
lowerTicks.toString() +
|
|
44
|
+
"],[" +
|
|
45
|
+
upperTicks.toString() +
|
|
46
|
+
"],[" +
|
|
47
|
+
weights.toString() +
|
|
48
|
+
`]], "0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff000000000000000000000000ffffffffffffffffffffffffffffffffffffffff"]
|
|
38
49
|
}`
|
|
39
|
-
|
|
50
|
+
);
|
|
51
|
+
// The bytes value here is a placeholder for encoding that gets replaced with time-sensitive data upon execution. It will actually be the swap amount for rebalancing (int256) and slippage limit (uint160)
|
|
40
52
|
}
|
|
41
53
|
|
|
42
54
|
// TODO: Might need to be rewritten for assets
|
|
43
55
|
// Price must be in the native token
|
|
44
56
|
// token0 for token1
|
|
45
57
|
export function getTickFromPrice(price: f32): f32 {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
}
|
|
58
|
+
const tick = Math.log(price) / Math.log(f32(1.0001));
|
|
59
|
+
return f32(tick);
|
|
60
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import * as JSON from '@serial-as/json'
|
|
2
|
+
import {console} from '../console'
|
|
3
|
+
|
|
4
|
+
@serializable
|
|
5
|
+
export class Candle{
|
|
6
|
+
timestamp: i64 = 0;
|
|
7
|
+
high: number = 0.0;
|
|
8
|
+
low: number = 0.0;
|
|
9
|
+
open: number = 0.0;
|
|
10
|
+
close: number = 0.0;
|
|
11
|
+
|
|
12
|
+
constructor(timestamp: i64, high: number, low: number, open: number, close: number) {
|
|
13
|
+
this.timestamp = timestamp;
|
|
14
|
+
this.high = high;
|
|
15
|
+
this.low = low;
|
|
16
|
+
this.open = open;
|
|
17
|
+
this.close = close;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
toString(): string {
|
|
21
|
+
return JSON.stringify(this);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
*
|
|
27
|
+
* @param _data data connector data array
|
|
28
|
+
* @returns
|
|
29
|
+
*/
|
|
30
|
+
export function parsePrices(_data: string): Array<Candle> {
|
|
31
|
+
return parseCandles(_data);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function parseCandles(_data: string): Array<Candle> {
|
|
35
|
+
// Parse an object using the JSON object
|
|
36
|
+
let parsed: Array<Candle> = JSON.parse<Array<Candle>>(_data);
|
|
37
|
+
|
|
38
|
+
console.log('parsed: ' + parsed.toString())
|
|
39
|
+
|
|
40
|
+
return parsed;
|
|
41
|
+
}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './Position';
|
|
2
|
-
export * from './
|
|
2
|
+
export * from './Candle';
|
package/index.js
CHANGED
|
@@ -9,6 +9,6 @@ const imports = {
|
|
|
9
9
|
}
|
|
10
10
|
};
|
|
11
11
|
|
|
12
|
-
const asBindInstance = AsBind.instantiateSync(fs.readFileSync(__dirname + "/build/
|
|
12
|
+
const asBindInstance = AsBind.instantiateSync(fs.readFileSync(__dirname + "/build/untouched.wasm"), imports)
|
|
13
13
|
|
|
14
14
|
module.exports = asBindInstance.exports;
|
package/package.json
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@steerprotocol/strategy-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "Strategy utilities library for Steer Protocol strategies",
|
|
5
5
|
"main": "assembly/index.ts",
|
|
6
6
|
"types": "assembly/index.ts",
|
|
7
7
|
"scripts": {
|
|
8
8
|
"test": "yarn jest",
|
|
9
|
-
"asbuild:untouched": "asc assembly/index.ts --target debug --exportRuntime --transform as-bind",
|
|
10
|
-
"asbuild:optimized": "asc assembly/index.ts --target release --exportRuntime --transform as-bind",
|
|
9
|
+
"asbuild:untouched": "asc assembly/index.ts --target debug --exportRuntime --transform as-bind --transform @serial-as/transform",
|
|
10
|
+
"asbuild:optimized": "asc assembly/index.ts --target release --exportRuntime --transform as-bind --transform @serial-as/transform",
|
|
11
11
|
"asbuild": "npm run asbuild:untouched && npm run asbuild:optimized",
|
|
12
12
|
"docs": "typedoc --tsconfig ./tsconfig.json",
|
|
13
13
|
"strategy": "yarn asbuild && yarn test",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@babel/preset-typescript": "^7.14.5",
|
|
22
22
|
"@semantic-release/changelog": "^5.0.1",
|
|
23
23
|
"@semantic-release/git": "^9.0.0",
|
|
24
|
+
"@serial-as/json": "1.0.2",
|
|
24
25
|
"@steerprotocol/base-strategy": "^0.0.2",
|
|
25
26
|
"as-bind": "^0.8.0",
|
|
26
27
|
"assemblyscript-json": "^1.1.0",
|
package/tests/index.test.ts
CHANGED
|
@@ -3,21 +3,26 @@ const myModule = require("../index");
|
|
|
3
3
|
describe("WASM Module", () => {
|
|
4
4
|
describe("keltnerChannels", () => {
|
|
5
5
|
it("Price should ", async () => {
|
|
6
|
-
const price = new myModule.
|
|
6
|
+
const price = new myModule.Candle("123",1,2,3,4);
|
|
7
7
|
expect(price.high).toBe(1);
|
|
8
8
|
expect(price.low).toBe(2);
|
|
9
9
|
expect(price.open).toBe(3);
|
|
10
10
|
expect(price.close).toBe(4);
|
|
11
11
|
});
|
|
12
12
|
it("True average should compute properly", async () => {
|
|
13
|
-
const prices = [new myModule.
|
|
13
|
+
const prices = [new myModule.Candle("12342343",1,2,3,4),new myModule.Candle("12342343",2,3,4,5)];
|
|
14
14
|
const trueAverage = myModule.getAverageTrueRange(prices, 1);
|
|
15
15
|
expect(trueAverage).toBe(2);
|
|
16
16
|
});
|
|
17
17
|
it("True average should support decimals ", async () => {
|
|
18
|
-
const prices = [new myModule.
|
|
18
|
+
const prices = [new myModule.Candle("12342343",6.1,4.2,2.5,1.11),new myModule.Candle("12342343",41,32,41,15)];
|
|
19
19
|
const trueAverage = myModule.getAverageTrueRange(prices, 1);
|
|
20
20
|
expect(trueAverage).toBe(39.88999938964844)
|
|
21
21
|
});
|
|
22
|
+
it("Should parse candles ", async () => {
|
|
23
|
+
const candles = '[{"timestamp":"12312312123","high":6.1,"low":4.2,"open":2.5,"close":1.11},{"timestamp":"12312312123","high":41.1,"low":32.1,"open":41.2,"close":15.1}]'
|
|
24
|
+
const candlesArray = myModule.parseCandles(candles);
|
|
25
|
+
expect(candlesArray.length).toBe(2)
|
|
26
|
+
});
|
|
22
27
|
});
|
|
23
28
|
});
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import { JSON } from "assemblyscript-json";
|
|
2
|
-
|
|
3
|
-
export class Price {
|
|
4
|
-
constructor(
|
|
5
|
-
public high: f32,
|
|
6
|
-
public low: f32,
|
|
7
|
-
public open: f32,
|
|
8
|
-
public close: f32
|
|
9
|
-
) {}
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function parsePrices(_prices: String): Array<Price> {
|
|
13
|
-
const prices = _prices;
|
|
14
|
-
// Parse an object using the JSON object
|
|
15
|
-
let jsonObj: JSON.Obj = <JSON.Obj>JSON.parse(prices);
|
|
16
|
-
const result: Array<Price> = [];
|
|
17
|
-
|
|
18
|
-
const data_arr = <JSON.Arr>jsonObj.getArr("data");
|
|
19
|
-
if (data_arr == null) {throw new Error()};
|
|
20
|
-
// First and only data result for this strategy is the candles
|
|
21
|
-
const val = <JSON.Arr>data_arr._arr[0]
|
|
22
|
-
if (val != null) {
|
|
23
|
-
if (val.isArr) {
|
|
24
|
-
const pricesArray = (<JSON.Arr>val).valueOf();
|
|
25
|
-
|
|
26
|
-
for (let priceIndex = 0; priceIndex < pricesArray.length; priceIndex++) {
|
|
27
|
-
const price = pricesArray[priceIndex];
|
|
28
|
-
|
|
29
|
-
const candle = <JSON.Obj>JSON.parse(price.toString());
|
|
30
|
-
|
|
31
|
-
if (candle.isObj) {
|
|
32
|
-
const cl = candle.getValue("close");
|
|
33
|
-
const hi = candle.getValue("high");
|
|
34
|
-
const lo = candle.getValue("low");
|
|
35
|
-
const op = candle.getValue("open");
|
|
36
|
-
|
|
37
|
-
if (cl && hi && lo && op) {
|
|
38
|
-
//@ts-ignore
|
|
39
|
-
const close = f32(Number.parseFloat(cl.toString()));
|
|
40
|
-
//@ts-ignore
|
|
41
|
-
const high = f32(Number.parseFloat(hi.toString()));
|
|
42
|
-
//@ts-ignore
|
|
43
|
-
const low = f32(Number.parseFloat(lo.toString()));
|
|
44
|
-
//@ts-ignore
|
|
45
|
-
const open = f32(Number.parseFloat(op.toString()));
|
|
46
|
-
|
|
47
|
-
const obj: Price = new Price(high, low, open, close);
|
|
48
|
-
|
|
49
|
-
result.push(obj);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
return result;
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
return [];
|
|
57
|
-
} else {
|
|
58
|
-
return [];
|
|
59
|
-
}
|
|
60
|
-
}
|