@d8x/perpetuals-sdk 0.0.44 → 0.0.46

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 (41) hide show
  1. package/README.md +12 -0
  2. package/abi/MockTokenSwap.json +186 -0
  3. package/config/defaultConfig.json +36 -10
  4. package/config/mockSwap.json +4 -0
  5. package/config/oldConfig.json +2 -1
  6. package/config/priceFeedConfig.json +18 -0
  7. package/dist/accountTrade.d.ts +1 -0
  8. package/dist/accountTrade.js +31 -7
  9. package/dist/d8XMath.d.ts +6 -0
  10. package/dist/d8XMath.js +19 -1
  11. package/dist/liquidatorTool.d.ts +6 -4
  12. package/dist/liquidatorTool.js +13 -7
  13. package/dist/marketData.d.ts +6 -11
  14. package/dist/marketData.js +59 -41
  15. package/dist/nodeSDKTypes.d.ts +33 -2
  16. package/dist/nodeSDKTypes.js +3 -18
  17. package/dist/orderReferrerTool.d.ts +17 -7
  18. package/dist/orderReferrerTool.js +43 -13
  19. package/dist/perpetualDataHandler.d.ts +46 -6
  20. package/dist/perpetualDataHandler.js +143 -16
  21. package/dist/perpetualEventHandler.d.ts +0 -3
  22. package/dist/perpetualEventHandler.js +0 -3
  23. package/dist/priceFeeds.d.ts +115 -0
  24. package/dist/priceFeeds.js +373 -0
  25. package/dist/triangulator.d.ts +27 -0
  26. package/dist/triangulator.js +107 -0
  27. package/dist/version.d.ts +1 -1
  28. package/dist/version.js +1 -1
  29. package/package.json +3 -2
  30. package/src/accountTrade.ts +31 -8
  31. package/src/d8XMath.ts +47 -22
  32. package/src/index.ts +2 -0
  33. package/src/liquidatorTool.ts +28 -8
  34. package/src/marketData.ts +99 -61
  35. package/src/nodeSDKTypes.ts +30 -3
  36. package/src/orderReferrerTool.ts +56 -13
  37. package/src/perpetualDataHandler.ts +149 -21
  38. package/src/perpetualEventHandler.ts +0 -3
  39. package/src/priceFeeds.ts +371 -0
  40. package/src/triangulator.ts +105 -0
  41. package/src/version.ts +1 -1
@@ -0,0 +1,371 @@
1
+ import {BigNumber} from "ethers";
2
+ import PerpetualDataHandler from "./perpetualDataHandler";
3
+ import Triangulator from "./triangulator";
4
+ import {PriceFeedConfig, PriceFeedSubmission, PriceFeedFormat} from "./nodeSDKTypes"
5
+ import {decNToFloat} from "./d8XMath";
6
+
7
+ /**
8
+ * This class communicates with the REST API that provides price-data that is
9
+ * to be submitted to the smart contracts for certain functions such as
10
+ * trader liquidations, trade executions, change of trader margin amount.
11
+ */
12
+ export default class PriceFeeds {
13
+ private config: PriceFeedConfig;
14
+ private feedEndpoints: Array<string>; //feedEndpoints[endpointId] = endpointstring
15
+ private feedInfo: Map<string, {symbol:string, endpointId: number}>; // priceFeedId -> symbol, endpointId
16
+ private dataHandler : PerpetualDataHandler;
17
+ // store triangulation paths given the price feeds
18
+ private triangulations : Map<string, [string[], boolean[]]>;
19
+ private THRESHOLD_MARKET_CLOSED_SEC = 15; // smallest lag for which we consider the market as being closed
20
+
21
+ constructor(dataHandler: PerpetualDataHandler, priceFeedConfigNetwork: string) {
22
+
23
+ let configs = <PriceFeedConfig[]>require("../config/priceFeedConfig.json");
24
+ this.config = PriceFeeds._selectConfig(configs, priceFeedConfigNetwork);
25
+ [this.feedInfo, this.feedEndpoints] = PriceFeeds._constructFeedInfo(this.config);
26
+ this.dataHandler = dataHandler;
27
+ this.triangulations = new Map<string, [string[], boolean[]]>();
28
+ }
29
+
30
+ /**
31
+ * Pre-processing of triangulations for symbols, given the price feeds
32
+ * @param symbols set of symbols we want to triangulate from price feeds
33
+ */
34
+ public initializeTriangulations(symbols: Set<string>) {
35
+ let feedSymbols = new Array<string>();
36
+ for(let [key, value] of this.feedInfo) {
37
+ feedSymbols.push(value.symbol);
38
+ }
39
+ for(let symbol of symbols.values()) {
40
+ let triangulation = Triangulator.triangulate(feedSymbols, symbol);
41
+ this.triangulations.set(symbol, triangulation);
42
+ }
43
+ }
44
+
45
+ /**
46
+ * Get required information to be able to submit a blockchain transaction with price-update
47
+ * such as trade execution, liquidation
48
+ * @param symbol symbol of perpetual, e.g., BTC-USD-MATIC
49
+ * @returns PriceFeedSubmission, index prices, market closed information
50
+ */
51
+ public async fetchFeedPriceInfoAndIndicesForPerpetual(symbol: string) :
52
+ Promise<{submission: PriceFeedSubmission, pxS2S3: [number,number], mktClosed: [boolean, boolean]}>
53
+ {
54
+ let indexSymbols = this.dataHandler.getIndexSymbols(symbol);
55
+ // fetch prices from required price-feeds (REST)
56
+ let submission : PriceFeedSubmission = await this.fetchLatestFeedPriceInfoForPerpetual(symbol);
57
+ // calculate index-prices from price-feeds
58
+ let [_idxPrices, _mktClosed] = this.calculateTriangulatedPricesFromFeedInfo(indexSymbols.filter((x)=>x!=''), submission);
59
+ let idxPrices : [number, number]= [_idxPrices[0], 0];
60
+ let mktClosed: [boolean, boolean]=[_mktClosed[0], false];
61
+ if (idxPrices.length>1) {
62
+ idxPrices[1] = _idxPrices[1];
63
+ mktClosed[1] = _mktClosed[1];
64
+ }
65
+ return {submission : submission, pxS2S3: idxPrices, mktClosed: mktClosed};
66
+ }
67
+
68
+ /**
69
+ * Get all prices/isMarketClosed for the provided symbols via
70
+ * "latest_price_feeds" and triangulation. Triangulation must be defined in config, unless
71
+ * it is a direct price feed.
72
+ * @returns map of feed-price symbol to price/isMarketClosed
73
+ */
74
+ public async fetchPrices(symbols: string[]) : Promise<Map<string, [number, boolean]>> {
75
+ let feedPrices = await this.fetchAllFeedPrices();
76
+ let [prices, mktClosed] = this.triangulatePricesFromFeedPrices(symbols, feedPrices);
77
+ let symMap = new Map<string, [number, boolean]>;
78
+ for(let k=0; k<symbols.length; k++) {
79
+ symMap.set(symbols[k], [prices[k], mktClosed[k]])
80
+ }
81
+ return symMap;
82
+ }
83
+
84
+ /**
85
+ * Get index prices and market closed information for the given perpetual
86
+ * @param symbol perpetual symbol such as ETH-USD-MATIC
87
+ * @returns
88
+ */
89
+ public async fetchPricesForPerpetual(symbol: string) : Promise<{idxPrices: number[], mktClosed: boolean[]}> {
90
+ let indexSymbols = this.dataHandler.getIndexSymbols(symbol).filter(x=>x!="");
91
+ // determine relevant price feeds
92
+ let feedSymbols = new Array<string>();
93
+ for(let sym of indexSymbols) {
94
+ if(sym!="") {
95
+ let triang : [string[], boolean[]] | undefined = this.triangulations.get(sym);
96
+ if (triang==undefined) {
97
+ // no triangulation defined, so symbol must be a feed (unless misconfigured)
98
+ feedSymbols.push(sym);
99
+ } else {
100
+ // push all required feeds to array
101
+ triang[0].map((feedSym)=> feedSymbols.push(feedSym));
102
+ }
103
+ }
104
+ }
105
+ // get all feed prices
106
+ let feedPrices = await this.fetchFeedPrices(feedSymbols);
107
+ // triangulate
108
+ let [prices, mktClosed] = this.triangulatePricesFromFeedPrices(indexSymbols, feedPrices);
109
+ // ensure we return an array of 2 in all cases
110
+ if (prices.length==1) {
111
+ prices.push(0);
112
+ mktClosed.push(false);
113
+ }
114
+ return {idxPrices: prices, mktClosed: mktClosed};
115
+ }
116
+
117
+ /**
118
+ * Fetch the provided feed prices and bool whether market is closed or open
119
+ * - requires the feeds to be defined in priceFeedConfig.json
120
+ * - if undefined, all feeds are queried
121
+ * @param symbols array of feed-price symbols (e.g., [btc-usd, eth-usd]) or undefined
122
+ * @returns mapping symbol-> [price, isMarketClosed]
123
+ */
124
+ public async fetchFeedPrices(symbols?: string[]): Promise<Map<string, [number, boolean]>> {
125
+ let queries = new Array<string>(this.feedEndpoints.length);
126
+ let symbolsOfEndpoint: string[][] = [];
127
+ for(let j=0;j<queries.length;j++) {
128
+ symbolsOfEndpoint.push([]);
129
+ }
130
+ for(let k=0; k<this.config.ids.length; k++) {
131
+ let currFeed = this.config.ids[k];
132
+ if(symbols!=undefined && !symbols.includes(currFeed.symbol)) {
133
+ continue;
134
+ }
135
+ // feedInfo: Map<string, {symbol:string, endpointId: number}>; // priceFeedId -> symbol, endpointId
136
+ let endpointId = this.feedInfo.get(currFeed.id)!.endpointId;
137
+ symbolsOfEndpoint[endpointId].push(currFeed.symbol);
138
+ if (queries[endpointId] == undefined) {
139
+ // each id can have a different endpoint, but we cluster
140
+ // the queries into one per endpoint
141
+ queries[endpointId] = this.feedEndpoints[endpointId] + "/latest_price_feeds?";
142
+ }
143
+ queries[endpointId] = queries[endpointId] + "ids[]=" + currFeed.id + "&";
144
+ }
145
+ let resultPrices = new Map<string, [number, boolean]>();
146
+ for(let k=0; k<queries.length; k++) {
147
+ if(queries[k]==undefined) {
148
+ continue;
149
+ }
150
+ let [id, pxInfo] : [string[], PriceFeedFormat[]] = await this.fetchPriceQuery(queries[k]);
151
+ let tsSecNow = Math.round(Date.now()/1000);
152
+ for(let j=0; j<pxInfo.length; j++) {
153
+ let price = decNToFloat(BigNumber.from(pxInfo[j].price), -pxInfo[j].expo);
154
+ let isMarketClosed = tsSecNow - pxInfo[j].publish_time > this.THRESHOLD_MARKET_CLOSED_SEC;
155
+ resultPrices.set(symbolsOfEndpoint[k][j], [price, isMarketClosed]);
156
+ }
157
+ }
158
+ return resultPrices;
159
+
160
+ }
161
+
162
+ /**
163
+ * Get all configured feed prices via "latest_price_feeds"
164
+ * @returns map of feed-price symbol to price/isMarketClosed
165
+ */
166
+ public async fetchAllFeedPrices() : Promise<Map<string, [number, boolean]>> {
167
+ return this.fetchFeedPrices();
168
+ }
169
+
170
+ /**
171
+ * Get the latest prices for a given perpetual from the offchain oracle
172
+ * networks
173
+ * @param symbol perpetual symbol of the form BTC-USD-MATIC
174
+ * @returns array of price feed updates that can be submitted to the smart contract
175
+ * and corresponding price information
176
+ */
177
+ public async fetchLatestFeedPriceInfoForPerpetual(symbol: string) : Promise<PriceFeedSubmission> {
178
+ let feedIds = this.dataHandler.getPriceIds(symbol);
179
+ let queries = new Array<string>(this.feedEndpoints.length);
180
+ // we need to preserve the order of the price feeds
181
+ let orderEndpointNumber = new Array<number>();
182
+ // count how many prices per endpoint
183
+ let idCountPriceFeeds = new Array<number>(this.feedEndpoints.length);
184
+ let symbols = new Array<string>();
185
+ for (let k = 0; k < feedIds.length; k++) {
186
+ let info = this.feedInfo.get(feedIds[k]);
187
+ if (info == undefined) {
188
+ throw new Error(`priceFeeds: config for symbol ${symbol} insufficient`);
189
+ }
190
+ let id = info.endpointId;
191
+ symbols.push(info.symbol);
192
+ if (queries[id] == undefined) {
193
+ // each id can have a different endpoint, but we cluster
194
+ // the queries into one per endpoint
195
+ queries[id] = this.feedEndpoints[id] + "/latest_vaas_px?";
196
+ idCountPriceFeeds[id] = 0;
197
+ }
198
+ queries[id] = queries[id] + "ids[]=" + feedIds[k] + "&";
199
+ orderEndpointNumber.push(id * 100 + idCountPriceFeeds[id]);
200
+ idCountPriceFeeds[id] = idCountPriceFeeds[id] + 1;
201
+ }
202
+
203
+ let data = await Promise.all(
204
+ queries.map(async (q) => {
205
+ if (q != undefined) {
206
+ return this.fetchVAAQuery(q);
207
+ } else {
208
+ return [[], []];
209
+ }
210
+ })
211
+ );
212
+
213
+ // re-order arrays so we preserve the order of the feeds
214
+ const priceFeedUpdates = new Array<string>();
215
+ const prices = new Array<number>();
216
+ const mktClosed = new Array<boolean>();
217
+ const timestamps = new Array<number>();
218
+ const tsSecNow = Math.round(Date.now()/1000);
219
+ for(let k=0; k<orderEndpointNumber.length; k++) {
220
+ let endpointId = Math.floor(orderEndpointNumber[k]/100);
221
+ let idWithinEndpoint = orderEndpointNumber[k]-100*endpointId;
222
+ priceFeedUpdates.push(data[endpointId][0][idWithinEndpoint]);
223
+ let pxInfo: PriceFeedFormat = data[endpointId][1][idWithinEndpoint];
224
+ let price = decNToFloat(BigNumber.from(pxInfo.price), -pxInfo.expo);
225
+ let isMarketClosed = tsSecNow - pxInfo.publish_time > this.THRESHOLD_MARKET_CLOSED_SEC;
226
+ mktClosed.push(isMarketClosed);
227
+ prices.push(price);
228
+ timestamps.push(pxInfo.publish_time);
229
+ }
230
+
231
+ return {"symbols": symbols, priceFeedVaas: priceFeedUpdates, prices: prices, isMarketClosed: mktClosed, timestamps: timestamps};
232
+ }
233
+
234
+ /**
235
+ * Extract pair-prices from underlying price feeds via triangulation
236
+ * The function either needs a direct price feed or a defined triangulation to succesfully
237
+ * return a triangulated price
238
+ * @param symbols array of pairs for which we want prices, e.g., [BTC-USDC, ETH-USD]
239
+ * @param feeds data obtained via fetchLatestFeedPriceInfo or fetchLatestFeedPrices
240
+ * @returns array of prices with same order as symbols
241
+ */
242
+ public calculateTriangulatedPricesFromFeedInfo(symbols: string[], feeds: PriceFeedSubmission) : [number[], boolean[]] {
243
+ let priceMap = new Map<string, [number,boolean]>();
244
+ for(let j=0; j<feeds.prices.length; j++) {
245
+ priceMap.set(feeds.symbols[j], [feeds.prices[j], feeds.isMarketClosed[j]]);
246
+ }
247
+ return this.triangulatePricesFromFeedPrices(symbols, priceMap);
248
+ }
249
+ /**
250
+ * Extract pair-prices from underlying price feeds via triangulation
251
+ * The function either needs a direct price feed or a defined triangulation to succesfully
252
+ * return a triangulated price
253
+ * @param symbols array of pairs for which we want prices, e.g., [BTC-USDC, ETH-USD]
254
+ * @param feeds data obtained via fetchLatestFeedPriceInfo or fetchLatestFeedPrices
255
+ * @returns array of prices with same order as symbols
256
+ */
257
+ public triangulatePricesFromFeedPrices(symbols: string[], feedPriceMap: Map<string, [number, boolean]>) : [number[], boolean[]] {
258
+ let prices = new Array<number>();
259
+ let mktClosed = new Array<boolean>();
260
+ for(let k=0; k<symbols.length; k++) {
261
+ let triangulation : [string[], boolean[]] | undefined = this.triangulations.get(symbols[k]);
262
+ if(triangulation==undefined) {
263
+ let feedPrice = feedPriceMap.get(symbols[k]);
264
+ if (feedPrice==undefined) {
265
+ throw new Error(`PriceFeeds: no triangulation defined for ${symbols[k]}`);
266
+ } else {
267
+ prices.push(feedPrice[0]);//price
268
+ mktClosed.push(feedPrice[1]);//market closed?
269
+ continue;
270
+ }
271
+ }
272
+ let [px, isMktClosed] : [number, boolean]= Triangulator.calculateTriangulatedPrice(triangulation, feedPriceMap);
273
+ prices.push(px);
274
+ mktClosed.push(isMktClosed);
275
+ }
276
+ return [prices, mktClosed];
277
+ }
278
+
279
+ /**
280
+ * Queries the REST endpoint and returns parsed VAA price data
281
+ * @param query query price-info from endpoint
282
+ * @returns vaa and price info
283
+ */
284
+ private async fetchVAAQuery(query: string) : Promise<[string[], PriceFeedFormat[]]> {
285
+ const headers = {headers: {'Content-Type': 'application/json'}};
286
+ let response = await fetch(query, headers);
287
+ if (!response.ok) {
288
+ throw new Error(`Failed to fetch posts (${response.status}): ${response.statusText}`);
289
+ }
290
+ let values = (await response.json()) as Array<[string, PriceFeedFormat]>;
291
+ const priceFeedUpdates = new Array<string>();
292
+ const px = new Array<PriceFeedFormat>();
293
+ for (let k = 0; k < values.length; k++) {
294
+ priceFeedUpdates.push("0x" + Buffer.from(values[k][0], "base64").toString("hex"));
295
+ px.push(values[k][1]);
296
+ }
297
+ return [priceFeedUpdates, px];
298
+ }
299
+
300
+ /**
301
+ * Queries the REST endpoint and returns parsed price data
302
+ * @param query query price-info from endpoint
303
+ * @returns vaa and price info
304
+ */
305
+ public async fetchPriceQuery(query: string) : Promise<[string[], PriceFeedFormat[]]> {
306
+ const headers = {headers: {'Content-Type': 'application/json'}};
307
+ let response = await fetch(query, headers);
308
+ if (!response.ok) {
309
+ throw new Error(`Failed to fetch posts (${response.status}): ${response.statusText}`);
310
+ }
311
+ let values = await response.json();
312
+ const priceFeedUpdates = new Array<string>();
313
+ const px = new Array<PriceFeedFormat>();
314
+ for (let k = 0; k < values.length; k++) {
315
+ priceFeedUpdates.push("0x" + Buffer.from(values[k].id, "base64").toString("hex"));
316
+ px.push(values[k].price as PriceFeedFormat);
317
+ }
318
+ return [priceFeedUpdates, px];
319
+ }
320
+
321
+ /**
322
+ * Searches for configuration for given network
323
+ * @param configs pricefeed configuration from json
324
+ * @param network e.g. testnet
325
+ * @returns selected configuration
326
+ */
327
+ static _selectConfig(configs: PriceFeedConfig[], network: string) : PriceFeedConfig {
328
+ let k=0;
329
+ while(k<configs.length) {
330
+ if (configs[k].network==network) {
331
+ return configs[k];
332
+ }
333
+ k=k+1;
334
+ }
335
+ throw new Error(`PriceFeeds: config not found for network ${network}`);
336
+ }
337
+
338
+ /**
339
+ * Wraps configuration into convenient data-structure
340
+ * @param config configuration for the selected network
341
+ * @returns feedInfo-map and endPoints-array
342
+ */
343
+ static _constructFeedInfo(config: PriceFeedConfig) : [Map<string, {symbol:string, endpointId: number}>, string[]]{
344
+ let feed = new Map<string, {symbol:string, endpointId: number}>();
345
+ let endpointId = -1;
346
+ let type="";
347
+ let feedEndpoints = new Array<string>();
348
+ for(let k=0; k<config.endpoints.length; k++) {
349
+ feedEndpoints.push(config.endpoints[k].endpoint);
350
+ }
351
+ for(let k=0; k<config.ids.length; k++) {
352
+ if (type!=config.ids[k].type) {
353
+ type = config.ids[k].type;
354
+ let j=0;
355
+ while(j<config.endpoints.length) {
356
+ if(config.endpoints[j].type == type) {
357
+ endpointId = j;
358
+ j=config.endpoints.length;
359
+ }
360
+ j++;
361
+ }
362
+ if(config.endpoints[endpointId].type!=type) {
363
+ throw new Error(`priceFeeds: no enpoint found for ${type} check priceFeedConfig`);
364
+ }
365
+ }
366
+ feed.set(config.ids[k].id, {symbol: config.ids[k].symbol.toUpperCase(), endpointId: endpointId });
367
+ }
368
+ return [feed, feedEndpoints];
369
+ }
370
+
371
+ }
@@ -0,0 +1,105 @@
1
+ export default class Triangulator {
2
+ /**
3
+ * Find all possible triangulation paths
4
+ * @param ccyBase array of base currencies ['BTC', 'ETH', 'MATIC', ...]
5
+ * @param ccyQuote array of quote currencies corresponding to base ['USD', 'USD', ...]
6
+ * @param pair pair we want to calculate, e.g. BTC-USDC
7
+ * @returns array of different paths for which multiplication leads to
8
+ * desired pair, e.g. [['MATIC-USD'], ['MATIC-ETH', 'ETH-USD']]
9
+ */
10
+ public static findPath(ccyBase: string[], ccyQuote: string[], pair: string): Array<Array<string>> {
11
+ let [base, quote] = pair.split("-");
12
+ let paths = new Array<Array<string>>();
13
+ for (let k = 0; k < ccyBase.length; k++) {
14
+ let currentPath: string[] = [];
15
+ if (ccyBase[k] == base) {
16
+ currentPath.push(ccyBase[k] + "-" + ccyQuote[k]);
17
+ if (ccyQuote[k] == quote) {
18
+ // we are done
19
+ paths.push(currentPath);
20
+ } else {
21
+ // find path for ccyQuote[k]-quote without the currency
22
+ let newBase = new Array<string>();
23
+ let newQuote = new Array<string>();
24
+ for (let j = 0; j < ccyBase.length; j++) {
25
+ if (j != k && ccyQuote[j] + "-" + ccyBase[j] != ccyBase[k] + "-" + ccyQuote[k]) {
26
+ newBase.push(ccyBase[j]);
27
+ newQuote.push(ccyQuote[j]);
28
+ }
29
+ }
30
+ let recPaths = Triangulator.findPath(newBase, newQuote, ccyQuote[k] + "-" + quote);
31
+ for (let j = 0; j < recPaths.length; j++) {
32
+ paths.push(currentPath.concat(recPaths[j]));
33
+ }
34
+ }
35
+ }
36
+ }
37
+ return paths;
38
+ }
39
+
40
+ /**
41
+ * Calculate the triangulated price from underlying prices
42
+ * @param triangulation triangulation of with symbol and is-inverted flag
43
+ * @param feedIdxPrices map of symbol to price
44
+ * @returns triangulated price (scalar), true if market closed or price unavailable
45
+ */
46
+ public static calculateTriangulatedPrice(triangulation: [string[], boolean[]], feedIdxPrices: Map<string, [number, boolean]>) : [number,boolean] {
47
+ let px = 1;
48
+ let isOpen = true;
49
+ for (let j = 0; j < triangulation[0].length; j++) {
50
+ let pxFeed : [number, boolean]|undefined = feedIdxPrices.get(triangulation[0][j]);
51
+ if (pxFeed == undefined) {
52
+ // no price available for given index
53
+ return [-1, true];
54
+ }
55
+ px = triangulation[1][j] ? px / pxFeed[0] : px * pxFeed[0];
56
+ let isClosed = pxFeed[1];
57
+ isOpen = isOpen && !isClosed;
58
+ }
59
+ return [px, !isOpen];
60
+ }
61
+
62
+ /**
63
+ * Finds shortest path and returns indices required and whether to divide or not
64
+ * @example triangulate BTC-USDC: [ [ 'BTC-USD', 'USDC-USD' ], [ false, true ] ]
65
+ * @param ccyArr array of available indices (e.g. from websocket)
66
+ * @param symbol symbol we are looking for (e.g. MATIC-USDC)
67
+ * @returns shortest path with given indices, array whether we have to divide (true) or multiply
68
+ * to arrive at the desired price
69
+ */
70
+ public static triangulate(ccyArr: string[], symbol: string): [string[], boolean[]] {
71
+ let ccyBase = ccyArr.map((x) => x.split("-")[0]);
72
+ let ccyQuote = ccyArr.map((x) => x.split("-")[1]);
73
+ let p = Triangulator.findPath(ccyBase.concat(ccyQuote), ccyQuote.concat(ccyBase), symbol);
74
+ if (p.length == 0) {
75
+ return [[], []];
76
+ }
77
+ let len = p.map((x) => x.length);
78
+ let minLen = 100;
79
+ let minIdx = -1;
80
+ for (let j = 0; j < p.length; j++) {
81
+ if (len[j] < minLen) {
82
+ minLen = len[j];
83
+ minIdx = j;
84
+ if (minLen == 1) {
85
+ break;
86
+ }
87
+ }
88
+ }
89
+ let path = p[minIdx];
90
+ let invert = new Array<boolean>();
91
+ let indexPath = new Array<string>();
92
+ for (let k = 0; k < path.length; k++) {
93
+ if (ccyArr.includes(path[k], 0)) {
94
+ indexPath.push(path[k]);
95
+ invert.push(false);
96
+ } else {
97
+ let inv = path[k].split("-");
98
+ indexPath.push(inv[1] + "-" + inv[0]);
99
+ invert.push(true);
100
+ }
101
+ }
102
+
103
+ return [indexPath, invert];
104
+ }
105
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export const D8X_SDK_VERSION = "0.0.43";
1
+ export const D8X_SDK_VERSION = "0.0.46";