@steerprotocol/strategy-utils 3.1.1 → 3.2.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.
Files changed (36) hide show
  1. package/.github/workflows/nodejs.yml +12 -12
  2. package/.vscode/settings.json +6 -0
  3. package/CHANGELOG.md +3 -45
  4. package/asconfig.json +11 -9
  5. package/assembly/index.ts +1 -0
  6. package/assembly/panoptic/host.ts +285 -0
  7. package/assembly/panoptic/index.ts +3 -0
  8. package/assembly/panoptic/methods.ts +64 -0
  9. package/assembly/panoptic/types.ts +911 -0
  10. package/assembly/utils/Math.ts +12 -17
  11. package/assembly/utils/MovingAverages.ts +26 -19
  12. package/assembly/utils/Ranges.ts +15 -15
  13. package/assembly/utils/UniswapLiquidityUtils.ts +48 -0
  14. package/assembly/utils/index.ts +1 -3
  15. package/assembly/utils/types/Position.ts +0 -3
  16. package/assembly/utils/types/Price.ts +54 -0
  17. package/assembly/utils/types/index.ts +1 -4
  18. package/index.js +3 -14
  19. package/package.json +10 -14
  20. package/scripts/build-docs.js +68 -0
  21. package/tests/fixtures/json-compat.ts +30 -0
  22. package/tests/fixtures/panoptic-consumer.ts +24 -0
  23. package/tests/index.test.ts +55 -227
  24. package/assembly/utils/CandleGenerator.ts +0 -60
  25. package/assembly/utils/MarketFeedAggregator.ts +0 -140
  26. package/assembly/utils/SlidingWindow.ts +0 -59
  27. package/assembly/utils/env.ts +0 -23
  28. package/assembly/utils/triggers.ts +0 -438
  29. package/assembly/utils/types/Candle.ts +0 -39
  30. package/assembly/utils/types/DataConnectorConfig.ts +0 -7
  31. package/assembly/utils/types/ExecutionContext.ts +0 -11
  32. package/assembly/utils/types/RawTradeData.ts +0 -14
  33. package/index.html +0 -10
  34. package/readme.md +0 -387
  35. package/tests/debug.wasm +0 -0
  36. package/tests/utils.ts +0 -607
@@ -1,438 +0,0 @@
1
- import { JSON } from "json-as";
2
- // import { Position } from "@steerprotocol/strategy-utils/assembly";
3
- import { Position } from "./types";
4
-
5
- // NOTE: Trigger functions return true when action should be taken, if false then the strategy can return 'continue' to skip exeuction
6
- // Implementation might look like the following:
7
- // const trigger = getTriggerStyle(configJson.triggerStyle)
8
- // const triggerObj = new TriggerConfigHelper(configJson.triggerWhenOver, configJson.tickPriceTrigger, configJson.percentageOfPositionRangeToTrigger, configJson.tickDistanceFromCenter, configJson.elapsedTendTime)
9
- // if (!shouldTriggerExecution(trigger, triggerObj, _positions, _currentTick, _timeSinceLastExecution)) return 'continue'
10
-
11
- // Gets active range from the output of LM.getPositions()
12
- export function parseActiveRange(_positions: string): Position {
13
- // _positions will be '[[#,#,#],[#,#,#],[#,#,#]]' presumably. lower, upper, weight
14
- // clean up our list by removing spaces and brackets
15
- let positions = _positions.replaceAll(' ','');
16
- positions = positions.replaceAll('[','');
17
- let rangeArray = positions.split(']',2);
18
- let startTick = rangeArray[0].split(',')[0];
19
- let endRange = rangeArray[1].split(']',2);
20
- const endTicks = endRange[0].split(',');
21
- let endTick = endTicks[endTicks.length-1]
22
- // if trailing comma
23
- if (endTick == '') endTick = endTicks[endTicks.length-2]
24
- const strArrays = [startTick, endTick];
25
- // check null
26
- if (strArrays[0] == '' || strArrays[0] == null || strArrays[1] == '' || strArrays[1] == null) {
27
- return new Position(0, 0, 0);
28
- }
29
- // else return normal
30
- const lowerTick = i32(parseInt(strArrays[0]));
31
- const upperTick = i32(parseInt(strArrays[1]));
32
- // weights shouldn't matter in this context, we just want the total active range
33
- return new Position(lowerTick, upperTick, 100);
34
- }
35
-
36
- export function emptyCurrentPosition(currentPosition: Position): boolean {
37
- // return true if current position ticks are not the same
38
- return currentPosition.startTick == currentPosition.endTick
39
- }
40
-
41
- // where rebalance width is how far off on either side the trigger should activate
42
- export function triggerFromDistance(currentPosition: Position, rebalanceWidth: i64, currentTick: i64): boolean {
43
- if (emptyCurrentPosition(currentPosition)) return true
44
- const centerPosition = (currentPosition.endTick + currentPosition.startTick) / 2
45
- const upperTrigger = centerPosition + (rebalanceWidth)
46
- const lowerTrigger = centerPosition - (rebalanceWidth)
47
- // In bounds? return false to continue (skip exec), returns true to execute
48
- return !((currentTick <= upperTrigger && currentTick >= lowerTrigger))
49
- }
50
-
51
- // tick moves % away from center of position range
52
- export function triggerFromPercentage(currentPosition: Position, rebalancePercentage: f64, currentTick: i64): boolean {
53
- if (emptyCurrentPosition(currentPosition)) return true
54
- const side = (currentPosition.endTick - currentPosition.startTick) / 2
55
- const centerPosition = (currentPosition.endTick + currentPosition.startTick) / 2
56
- const triggerDiff = i64(Math.round(f64(side) * rebalancePercentage))
57
- const upperTrigger = centerPosition + triggerDiff
58
- const lowerTrigger = centerPosition - triggerDiff
59
- // In bounds? return false to continue (skip exec), returns true to execute
60
- return !((currentTick <= upperTrigger && currentTick >= lowerTrigger))
61
- }
62
-
63
- // when current tick is no longer in the current position range
64
- export function triggerPositionsInactive(currentPosition: Position, currentTick: i64): boolean {
65
- // return JSON.stringify(currentPosition)
66
- if (emptyCurrentPosition(currentPosition)) return true
67
-
68
- return (!((currentTick <= currentPosition.endTick && currentTick >= currentPosition.startTick)))
69
- }
70
-
71
- // when tick goes over or under specified tick, trigger rebalance
72
- // export function triggerFromSpecifiedPrice(triggerTick: i64, currentTick: i64, triggerOver: boolean): boolean {
73
- // // if trigger over, return true when currentTick > triggerTick
74
- // if (triggerOver) {
75
- // return currentTick > triggerTick
76
- // }
77
- // // trigger under, if current price is less than trigger tick return true
78
- // return triggerTick > currentTick
79
- // }
80
-
81
- // when tick goes over or under current positions, trigger rebalance, only moves one way
82
- export function triggerPricePastPositions(currentPosition: Position, currentTick: i64, triggerOver: boolean): boolean {
83
- if (emptyCurrentPosition(currentPosition)) return true
84
- if (triggerOver) {
85
- // if the currentTick is over the endTick, rebal
86
- return currentTick > i64(currentPosition.endTick)
87
- }
88
- // if current tick is under start tick
89
- return currentTick < i64(currentPosition.startTick)
90
- }
91
-
92
- export const enum TriggerStyle {
93
- DistanceFromCenterOfPositions,
94
- PercentageChangeFromPositionRange,
95
- PositionsInactive,
96
- // SpecificPrice,
97
- PricePastPositions,
98
- None,
99
- }
100
-
101
- // export function TriggerStyleLookup(triggerStyle: TriggerStyle): string {
102
- // switch (triggerStyle) {
103
- // case TriggerStyle.DistanceFromCenterOfPositions:
104
- // return "Distance from center of position(s)";
105
- // case TriggerStyle.PercentageChangeFromPositionRange:
106
- // return "Percentage of position(s)";
107
- // case TriggerStyle.PositionsInactive:
108
- // return "Positions Inactive";
109
- // // case TriggerStyle.SpecificPrice:
110
- // // return "Specific Price";
111
- // case TriggerStyle.PricePastPositions:
112
- // return "Price moved past position(s)";
113
- // case TriggerStyle.None:
114
- // return 'None'
115
-
116
- // default:
117
- // throw new Error(`Unknown trigger style: ${triggerStyle}`);
118
- // }
119
- // }
120
-
121
- // export class DistanceFromCenterOfPositionsOptions {
122
- // tickDistanceFromCenter: i64 = 0;
123
- // requiredDataTypes: string[] = ['Liquidity Manager Positions', 'V3 Pool Current Tick'];
124
- // }
125
-
126
- // export class PercentageChangeFromPositionRangeOptions {
127
- // percentageOfPositionRangeToTrigger: f64 = 0.0;
128
- // requiredDataTypes: string[] = ['Liquidity Manager Positions', 'V3 Pool Current Tick'];
129
- // }
130
-
131
- // export class PositionsInactiveOptions {
132
- // requiredDataTypes: string[] = ['Liquidity Manager Positions', 'V3 Pool Current Tick'];
133
- // }
134
-
135
- // export class SpecificPriceOptions {
136
- // tickPriceTrigger: i64 = 0;
137
- // triggerWhenOver: boolean = false;
138
- // requiredDataTypes: string[] = ['V3 Pool Current Tick'];
139
- // }
140
-
141
- // export class PricePastPositionsOptions {
142
- // triggerWhenOver: boolean = false;
143
- // requiredDataTypes: string[] = ['Liquidity Manager Positions', 'V3 Pool Current Tick'];
144
- // }
145
-
146
- // @ts-ignore
147
- @json
148
- export class TriggerConfigHelper {
149
- // triggerType: string = "Price leaves active range";
150
- triggerWhenOver: boolean = false;
151
- tickPriceTrigger: i64 = 0;
152
- percentageOfPositionRangeToTrigger: f64 = 0.0;
153
- tickDistanceFromCenter: i64 = 0;
154
- elapsedTendTime: i64 = 0;
155
- constructor( t: boolean, tpt: i64, poptrr:f64, tdfc: i64, ett: i64) {
156
- if(t) this.triggerWhenOver = t
157
- if(tpt) this.tickPriceTrigger = tpt
158
- if(poptrr) this.percentageOfPositionRangeToTrigger = poptrr
159
- if(tdfc) this.tickDistanceFromCenter = tdfc
160
- if(ett) this.elapsedTendTime = ett
161
- }
162
- }
163
-
164
- export function getTriggerExpectedDataTypes(triggerStyle: string): string[] {
165
- // get tirggerStyle
166
- const style = getTriggerStyle(triggerStyle)
167
- // currently we use only this list
168
- const typicalTypes = ["Liquidity Manager Positions", "V3 Pool Current Tick", "Time Since Last Execution"]
169
- switch (style) {
170
- case TriggerStyle.DistanceFromCenterOfPositions:
171
- return typicalTypes;
172
- case TriggerStyle.PercentageChangeFromPositionRange:
173
- return typicalTypes;
174
- case TriggerStyle.PositionsInactive:
175
- return typicalTypes;
176
- // case TriggerStyle.SpecificPrice:
177
- // return "Specific Price";
178
- case TriggerStyle.PricePastPositions:
179
- return typicalTypes;
180
- case TriggerStyle.None:
181
- // return no data connectors for none
182
- return [];
183
-
184
- default:
185
- throw new Error(`Unknown trigger style: ${triggerStyle}`);
186
- }
187
- }
188
-
189
- // currently implemented as :: ulm positions [0], current tick [1], time since last execution [2]
190
- // as more types are added this logic path with be redone
191
- export function shouldTriggerExecution(
192
- _triggerStyle: string,
193
- triggerOptions: TriggerConfigHelper,
194
- dataConnector1: string,
195
- dataConnector2: string,
196
- dataConnector3: string) : boolean {
197
-
198
- // possible dc inputs
199
- let currentPositionRange: Position;
200
- let currentTick: i64;
201
- const triggerStyle = getTriggerStyle(_triggerStyle)
202
-
203
-
204
-
205
- let timeSinceLastExecution: i64;
206
-
207
- switch (triggerStyle) {
208
- case TriggerStyle.DistanceFromCenterOfPositions:
209
- // parse ulm positions [0], current tick [1]
210
- // @ts-ignore
211
- timeSinceLastExecution = i64(parseInt(dataConnector3))
212
- if (timeSinceLastExecution >= i64(triggerOptions.elapsedTendTime)) return true
213
- currentPositionRange = parseActiveRange(dataConnector1)
214
- currentTick = i64(parseInt(dataConnector2))
215
- return triggerFromDistance(currentPositionRange, triggerOptions.tickDistanceFromCenter, currentTick)
216
-
217
- case TriggerStyle.PercentageChangeFromPositionRange:
218
- // parse ulm positions [0], current tick [1]
219
- timeSinceLastExecution = i64(parseInt(dataConnector3))
220
- if (timeSinceLastExecution >= i64(triggerOptions.elapsedTendTime)) return true
221
- currentPositionRange= parseActiveRange(dataConnector1)
222
- currentTick = i64(parseInt(dataConnector2))
223
- return triggerFromPercentage(currentPositionRange, triggerOptions.percentageOfPositionRangeToTrigger, currentTick)
224
-
225
- case TriggerStyle.PositionsInactive:
226
- // parse ulm positions [0], current tick [1]
227
- timeSinceLastExecution = i64(parseInt(dataConnector3))
228
- if (timeSinceLastExecution >= i64(triggerOptions.elapsedTendTime)) return true
229
- currentPositionRange = parseActiveRange(dataConnector1)
230
- currentTick = i64(parseInt(dataConnector2))
231
- return triggerPositionsInactive(currentPositionRange, currentTick)
232
-
233
- case TriggerStyle.PricePastPositions:
234
- // parse ulm positions [0], current tick [1]
235
- timeSinceLastExecution = i64(parseInt(dataConnector3))
236
- if (timeSinceLastExecution >= i64(triggerOptions.elapsedTendTime)) return true
237
- currentPositionRange = parseActiveRange(dataConnector1)
238
- currentTick = i64(parseInt(dataConnector2))
239
- return triggerPricePastPositions(currentPositionRange, currentTick, triggerOptions.triggerWhenOver)
240
- default:
241
- return true
242
- }
243
- }
244
-
245
- export function getTriggerStyle(trigger: string): TriggerStyle {
246
- if (trigger === 'Current Price set distance from center of positions') {
247
- return TriggerStyle.DistanceFromCenterOfPositions;
248
- } else if (trigger === 'Price leaves active range') {
249
- return TriggerStyle.PositionsInactive;
250
- } else if (trigger === 'Price moves percentage of active range away') {
251
- return TriggerStyle.PercentageChangeFromPositionRange;
252
- } else if (trigger === 'Price moves one way past positions') {
253
- return TriggerStyle.PricePastPositions;
254
- } else {
255
- return TriggerStyle.None;
256
- }
257
- }
258
-
259
- function getTriggerName(trigger: TriggerStyle): string {
260
- if (trigger === TriggerStyle.DistanceFromCenterOfPositions) {
261
- return 'Current Price set distance from center of positions';
262
- } else if (trigger === TriggerStyle.PositionsInactive) {
263
- return 'Price leaves active range';
264
- } else if (trigger === TriggerStyle.PercentageChangeFromPositionRange) {
265
- return 'Price moves percentage of active range away';
266
- } else if (trigger === TriggerStyle.PricePastPositions) {
267
- return 'Price moves one way past positions';
268
- } else {
269
- return 'None';
270
- }
271
- }
272
-
273
- function expectedDataTypesHelper(strategyDataConnectors: string[], triggerStyle: string): string[] {
274
- // const style = getTriggerStyle(triggerStyle)
275
- return strategyDataConnectors.concat(getTriggerExpectedDataTypes(triggerStyle))
276
- // return `"expectedDataTypes": {
277
- // "hidden": true,
278
- // "type": "string",
279
- // "default": "${(fullDataTypes).toString()}",
280
- // "const": "${(fullDataTypes).toString()}"
281
- // }`
282
- }
283
-
284
- @serializable
285
- export class TriggerInfo {
286
- name: string = '';
287
- expectedDataTypes: string[] = [];
288
- constructor (_name: string, _expectedDataTypes: string[]) {
289
- this.name = _name
290
- this.expectedDataTypes = _expectedDataTypes
291
- }
292
- }
293
-
294
- export function triggerPropertyHelper(strategyDataTypes: string[], omit: TriggerStyle[] = []): string {
295
- // const triggerList = [
296
- // TriggerStyle.DistanceFromCenterOfPositions,
297
- // TriggerStyle.None,
298
- // TriggerStyle.PercentageChangeFromPositionRange,
299
- // TriggerStyle.PositionsInactive,
300
- // TriggerStyle.PricePastPositions,
301
- // ];
302
-
303
- const triggersObjects: TriggerInfo[] = [
304
- new TriggerInfo('Current Price set distance from center of positions', expectedDataTypesHelper(strategyDataTypes, 'Current Price set distance from center of positions')),
305
- new TriggerInfo('Price leaves active range', expectedDataTypesHelper(strategyDataTypes, 'Price leaves active range')),
306
- new TriggerInfo('Price moves percentage of active range away', expectedDataTypesHelper(strategyDataTypes, 'Price moves percentage of active range away')),
307
- new TriggerInfo('Price moves one way past positions', expectedDataTypesHelper(strategyDataTypes, 'Price moves one way past positions')),
308
- new TriggerInfo('None', expectedDataTypesHelper(strategyDataTypes, 'None')),
309
- ];
310
-
311
- const triggerStrings = [
312
- 'Current Price set distance from center of positions',
313
- 'Price leaves active range',
314
- 'Price moves percentage of active range away',
315
- 'Price moves one way past positions',
316
- 'None'
317
- ]
318
-
319
- return `"triggerStyle": {
320
- "enumNames": ${JSON.stringify(triggerStrings)},
321
- "enum": ${JSON.stringify(triggersObjects)},
322
- "title": "Logic to trigger new positions",
323
- "default": ${JSON.stringify(new TriggerInfo('None', expectedDataTypesHelper(strategyDataTypes, 'None')))}
324
- }`;
325
- }
326
-
327
- export function allOfTrigger(strategyDataTypes: string[]): string {
328
- return `{
329
- "if": {
330
- "properties": {
331
- "triggerStyle": {
332
- "const": ${JSON.stringify(new TriggerInfo('None', expectedDataTypesHelper(strategyDataTypes, 'None')))}
333
- }
334
- }
335
- },
336
- "then": {
337
- "required": []
338
- }
339
- },
340
- {
341
- "if": {
342
- "properties": {
343
- "triggerStyle": {
344
- "const": ${JSON.stringify(new TriggerInfo('Current Price set distance from center of positions', expectedDataTypesHelper(strategyDataTypes, 'Current Price set distance from center of positions')))}
345
- }
346
- }
347
- },
348
- "then": {
349
- "properties": {
350
- "tickDistanceFromCenter": {
351
- "type": "integer",
352
- "title": "Tick Distance",
353
- "description": "The number of ticks (basis points) from center price of positions to trigger setting new positions",
354
- "detailedDescription": "The static number of ticks from the center of the active range to trigger: if our position goes from 0-100, and we have a tick distance of 75, we will go out 75 ticks both ways from the center of our positions (50). This means we will skip execution only if the current tick is between -25 and 125. Future positions will determine where the center of the trigger range is located."
355
- },
356
- "elapsedTendTime": {
357
- "type": "number",
358
- "title": "Max time between tends",
359
- "description": "If trigger conditions have not been met for this period of time, the strategy will execute regardless of trigger logic to update vault accounting.",
360
- "default": 1209600
361
- }
362
- },
363
- "required": ["tickDistanceFromCenter", "elapsedTendTime"]
364
- }
365
- },
366
- {
367
- "if": {
368
- "properties": {
369
- "triggerStyle": {
370
- "const": ${JSON.stringify(new TriggerInfo('Price leaves active range', expectedDataTypesHelper(strategyDataTypes, 'Price leaves active range')),)}
371
- }
372
- }
373
- },
374
- "then": {
375
- "properties": {
376
- "elapsedTendTime": {
377
- "type": "number",
378
- "title": "Max time between tends",
379
- "description": "If trigger conditions have not been met for this period of time, the strategy will execute regardless of trigger logic to update vault accounting.",
380
- "default": 1209600
381
- }
382
- },
383
- "required": ["elapsedTendTime"]
384
- }
385
- },
386
- {
387
- "if": {
388
- "properties": {
389
- "triggerStyle": {
390
- "const": ${JSON.stringify(new TriggerInfo('Price moves percentage of active range away', expectedDataTypesHelper(strategyDataTypes, 'Price moves percentage of active range away')))}
391
- }
392
- }
393
- },
394
- "then": {
395
- "properties": {
396
- "percentageOfPositionRangeToTrigger": {
397
- "type": "number",
398
- "title": "Percentage of Range",
399
- "description": "The percentage of the range away to trigger new positions, 100% or 1 would be at the bounds of the range",
400
- "detailedDescription": "If you have a simple position ranging from ticks 0 - 100, and you set this value to 1, the trigger range will be the outer bounds. Using 0.5 would make the trigger range 25-75, 2 would make the range -50 - 150."
401
- },
402
- "elapsedTendTime": {
403
- "type": "number",
404
- "title": "Max time between tends",
405
- "description": "If trigger conditions have not been met for this period of time, the strategy will execute regardless of trigger logic to update vault accounting.",
406
- "default": 1209600
407
- }
408
- },
409
- "required": ["percentageOfPositionRangeToTrigger", "elapsedTendTime"]
410
- }
411
- },
412
- {
413
- "if": {
414
- "properties": {
415
- "triggerStyle": {
416
- "const": ${JSON.stringify(new TriggerInfo('Price moves one way past positions', expectedDataTypesHelper(strategyDataTypes, 'Price moves one way past positions')))}
417
- }
418
- }
419
- },
420
- "then": {
421
- "properties": {
422
- "triggerWhenOver": {
423
- "type": "boolean",
424
- "title": "Price Moves Higher",
425
- "description": "True for if the strategy should set new positions when the price (tick) is higher than the current positions, false for lower",
426
- "detailedDescription": "If our current position ranges from ticks 0 - 100, true will make our bundle execute only when the current tick is higher. Any other case (current tick less than 100) will result in a continue recommendation."
427
- },
428
- "elapsedTendTime": {
429
- "type": "number",
430
- "title": "Max time between tends",
431
- "description": "If trigger conditions have not been met for this period of time, the strategy will execute regardless of trigger logic to update vault accounting.",
432
- "default": 1209600
433
- }
434
- },
435
- "required": ["triggerWhenOver", "elapsedTendTime"]
436
- }
437
- }`;
438
- }
@@ -1,39 +0,0 @@
1
- import { JSON } from 'json-as/assembly';
2
-
3
- @serializable
4
- export class Candle {
5
- timestamp: i64 = 0;
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
-
12
- constructor(timestamp: i64, high: f64, low: f64, open: f64, close: f64, volume: f64) {
13
- this.timestamp = timestamp;
14
- this.high = high;
15
- this.low = low;
16
- this.open = open;
17
- this.close = close;
18
- this.volume = volume;
19
- }
20
-
21
- toString(): string {
22
- return JSON.stringify(this);
23
- }
24
- }
25
-
26
- /**
27
- *
28
- * @param _data data connector data array
29
- * @returns
30
- */
31
- export function parsePrices(_data: string): Array<Candle> {
32
- return parseCandles(_data);
33
- }
34
-
35
- export function parseCandles(_data: string): Array<Candle> {
36
- // Parse an object using the JSON object
37
- let parsed: Array<Candle> = JSON.parse<Array<Candle>>(_data);
38
- return parsed;
39
- }
@@ -1,7 +0,0 @@
1
- import { JSON } from 'json-as/assembly';
2
- import { ExecutionContext } from "./ExecutionContext";
3
-
4
- @serializable
5
- export class DataConnectorConfig {
6
- executionContext: ExecutionContext = new ExecutionContext();
7
- }
@@ -1,11 +0,0 @@
1
- import { JSON } from 'json-as/assembly';
2
-
3
- @serializable
4
- export class ExecutionContext {
5
- executionTimestamp: number = 0;
6
- epochLength: number = 0;
7
- epochTimestamp: i32 = 0;
8
- vaultAddress: string = "";
9
- blockTime: i32 = 0;
10
- blockNumber: i32 = 0;
11
- }
@@ -1,14 +0,0 @@
1
- import { JSON } from 'json-as/assembly';
2
-
3
- @serializable
4
- export class RawTradeData {
5
- timestamp: i32 = 0;
6
- price: f64 = 0.0;
7
- volume: f64 = 0.0;
8
-
9
- constructor(timestamp: i32, price: f64, volume: f64) {
10
- this.timestamp = timestamp;
11
- this.price = price;
12
- this.volume = volume;
13
- }
14
- }
package/index.html DELETED
@@ -1,10 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <script type="module">
5
- import { initialize, execute } from "./build/release.js";
6
- document.body.innerText = 'Implementation missing, please fix index.html';;
7
- </script>
8
- </head>
9
- <body></body>
10
- </html>