|
1 | | -import { JSON } from "assemblyscript-json"; |
2 | | -import {Position, parsePrices, getTickFromPrice, trailingStop, renderULMResult, getTickSpacing} from "@steerprotocol/strategy-utils"; |
| 1 | +import { getTickFromPrice, getTickSpacing, renderULMResult } from "@steerprotocol/concentrated-liquidity-strategy/assembly"; |
| 2 | +import { parseCandles, Position, console, trailingStop } from "@steerprotocol/strategy-utils/assembly"; |
| 3 | +import { JSON } from "json-as/assembly"; |
3 | 4 |
|
4 | | - |
5 | | -let width: i32 = 600; |
6 | 5 | let percent: f32 = 0; |
7 | 6 | let poolFee: i32 = 0; |
8 | 7 |
|
| 8 | +@serializable |
| 9 | +class Config { |
| 10 | + poolFee: f32 = 0; |
| 11 | + percent: i32 = 0; |
| 12 | +} |
| 13 | + |
9 | 14 | export function initialize(config: string): void { |
10 | 15 | // Parse the config object |
11 | | - const configJson = <JSON.Obj>JSON.parse(config); |
12 | | - // Get our config variables |
13 | | - const _width = configJson.getInteger("binWidth"); |
14 | | - const _poolFee = configJson.getInteger("poolFee"); |
15 | | - const _percent = configJson.getValue("percent"); |
| 16 | + const configJson: Config = JSON.parse<Config>(config); |
| 17 | + |
16 | 18 | // Handle null case |
17 | | - if (_width == null || _percent == null || _poolFee == null) { |
| 19 | + if ( |
| 20 | + configJson.percent == 0 || |
| 21 | + configJson.poolFee == 0 |
| 22 | + ) { |
18 | 23 | throw new Error("Invalid configuration"); |
19 | 24 | } |
20 | 25 |
|
21 | | - // Handle percents presented as integers |
22 | | - if (_percent.isFloat) { |
23 | | - const f_percent = <JSON.Num>_percent |
24 | | - percent = f32(f_percent._num); |
25 | | - } |
26 | | - if (_percent.isInteger) { |
27 | | - const i_percent = <JSON.Integer>_percent |
28 | | - percent = f32(i_percent._num); |
29 | | - } |
30 | 26 | // Assign values to memory |
31 | | - width = i32(_width._num); |
32 | | - poolFee = i32(_poolFee._num); |
| 27 | + percent = f32(configJson.percent); |
| 28 | + poolFee = i32(configJson.poolFee); |
| 29 | +} |
| 30 | + |
| 31 | +function closestDivisibleNumber(num: number, divisor: number, floor: boolean): number { |
| 32 | + if (floor) return Math.floor(num / divisor) * divisor; |
| 33 | + return Math.ceil(num / divisor) * divisor; |
33 | 34 | } |
34 | 35 |
|
35 | 36 | export function execute(_prices: string): string { |
36 | 37 | // _prices will have the results of the dc, which is only candles here |
37 | | - const prices = parsePrices(_prices, 0); |
| 38 | + const prices = parseCandles(_prices); |
38 | 39 | // If we have no candles, skip action |
39 | | - if (prices.length == 0) {return `continue`} |
40 | | - // Get Trailing stop price |
41 | | - const trailingLimit = trailingStop(percent, prices) |
42 | | - // Calculate position |
43 | | - const positions = calculateBin(trailingLimit); |
44 | | - // Format and return result |
45 | | - return renderULMResult(positions); |
46 | | -} |
47 | | - |
48 | | - |
49 | | -function calculateBin(upper: f32): Position[] { |
50 | | - |
51 | | - // Calculate the upper tick based on the start of the stop |
52 | | - const upperTick: i32 = i32(Math.round(getTickFromPrice(upper))); |
53 | | - |
54 | | - // Get the spacing |
55 | | - const tickSpacing = getTickSpacing(poolFee); |
56 | | - |
57 | | - // Step down ticks until we reach an initializable tick |
58 | | - let _startTick: i32 = upperTick; |
59 | | - while (_startTick % tickSpacing !== 0) { |
60 | | - _startTick--; |
| 40 | + if (prices.length == 0) { |
| 41 | + return `continue`; |
61 | 42 | } |
62 | 43 |
|
63 | | - const positions: Array<Position> = []; |
64 | | - const position = new Position(_startTick - width, _startTick, 1); |
65 | | - positions.push(position); |
| 44 | + const lowerLimit = trailingStop(percent, prices); |
| 45 | + const upperLimit = prices[prices.length - 1].close; |
| 46 | + |
| 47 | + const upperTick = closestDivisibleNumber(i32(Math.round(getTickFromPrice(f32(upperLimit)))), getTickSpacing(poolFee), false); |
| 48 | + const lowerTick = closestDivisibleNumber(i32(Math.round(getTickFromPrice(f32(lowerLimit)))), getTickSpacing(poolFee), true); |
| 49 | + |
| 50 | + // Calculate position |
| 51 | + |
| 52 | + |
| 53 | + const positions = [new Position(i32(lowerTick), i32(upperTick), 100)]; |
66 | 54 |
|
67 | | - return positions |
| 55 | + // Format and return result |
| 56 | + return renderULMResult(positions, 10000); |
68 | 57 | } |
69 | 58 |
|
70 | | -export function config(): string{ |
| 59 | +export function config(): string { |
71 | 60 | return `{ |
72 | 61 | "$schema": "http://json-schema.org/draft-07/schema#", |
73 | 62 | "title": "Strategy Config", |
74 | 63 | "type": "object", |
| 64 | + "parameters": ["OHLC"], |
75 | 65 | "properties": { |
76 | 66 | "percent": { |
77 | | - "type": "number", |
78 | | - "description": "Percent for trailing stop order", |
79 | | - "default": 5.0 |
80 | | - }, |
| 67 | + "type": "number", |
| 68 | + "description": "Percent for trailing stop order (must be greater than pool fee)", |
| 69 | + "default": 5.0 |
| 70 | + }, |
81 | 71 | "poolFee": { |
82 | 72 | "description": "Pool fee percent for desired Uniswapv3 pool", |
83 | 73 | "enum" : [10000, 3000, 500, 100], |
84 | 74 | "enumNames": ["1%", "0.3%", "0.05%", "0.01%"] |
85 | | - }, |
86 | | - "binWidth": { |
87 | | - "type": "number", |
88 | | - "description": "Width for liquidity position, must be a multiple of pool tick spacing", |
89 | | - "default": 600 |
90 | 75 | } |
91 | 76 | }, |
92 | | - "required": ["percent", "binWidth", "poolFee"] |
| 77 | + "required": ["percent", "poolFee"] |
93 | 78 | }`; |
94 | 79 | } |
95 | 80 |
|
96 | 81 | export function version(): i32 { |
97 | 82 | return 1; |
98 | 83 | } |
99 | | - |
0 commit comments