@pioneer-platform/markets 8.12.0 → 8.15.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 +13 -0
- package/METRICS_MONITORING.md +285 -0
- package/PRICING_IMPROVEMENTS.md +327 -0
- package/lib/api-metrics-reporter.d.ts +20 -0
- package/lib/api-metrics-reporter.js +356 -0
- package/lib/ccxt-pricing.d.ts +34 -0
- package/lib/ccxt-pricing.js +421 -0
- package/lib/index.js +784 -145
- package/lib/metrics-logger.d.ts +72 -0
- package/lib/metrics-logger.js +435 -0
- package/lib/refill-scheduler.d.ts +22 -0
- package/lib/refill-scheduler.js +226 -0
- package/lib/token-bucket-manager.d.ts +47 -0
- package/lib/token-bucket-manager.js +342 -0
- package/lib/token-bucket.d.ts +87 -0
- package/lib/token-bucket.js +341 -0
- package/package.json +6 -3
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { Db } from 'mongodb';
|
|
2
|
+
export interface TokenBucketConfig {
|
|
3
|
+
apiName: string;
|
|
4
|
+
capacity: number;
|
|
5
|
+
refillAmount: number;
|
|
6
|
+
refillInterval: number;
|
|
7
|
+
tokensPerRequest: number;
|
|
8
|
+
costPerToken: number;
|
|
9
|
+
monthlyBudget: number;
|
|
10
|
+
}
|
|
11
|
+
export interface TokenBucketDocument {
|
|
12
|
+
apiName: string;
|
|
13
|
+
capacity: number;
|
|
14
|
+
currentTokens: number;
|
|
15
|
+
lastRefill: Date;
|
|
16
|
+
nextRefill: Date;
|
|
17
|
+
refillAmount: number;
|
|
18
|
+
refillInterval: number;
|
|
19
|
+
tokensPerRequest: number;
|
|
20
|
+
costPerToken: number;
|
|
21
|
+
monthlyCost: number;
|
|
22
|
+
monthlyBudget: number;
|
|
23
|
+
enabled: boolean;
|
|
24
|
+
exhausted: boolean;
|
|
25
|
+
exhaustedAt?: Date;
|
|
26
|
+
createdAt: Date;
|
|
27
|
+
updatedAt: Date;
|
|
28
|
+
}
|
|
29
|
+
export declare class TokenBucket {
|
|
30
|
+
private config;
|
|
31
|
+
private db;
|
|
32
|
+
private collection;
|
|
33
|
+
private cachedTokens;
|
|
34
|
+
private lastSync;
|
|
35
|
+
private syncInterval;
|
|
36
|
+
constructor(config: TokenBucketConfig, db: Db);
|
|
37
|
+
/**
|
|
38
|
+
* Initialize bucket (call on startup)
|
|
39
|
+
*/
|
|
40
|
+
initialize(): Promise<void>;
|
|
41
|
+
/**
|
|
42
|
+
* Try to consume tokens (returns true if successful)
|
|
43
|
+
*/
|
|
44
|
+
tryConsume(count?: number): Promise<boolean>;
|
|
45
|
+
/**
|
|
46
|
+
* Get current token count
|
|
47
|
+
*/
|
|
48
|
+
getTokenCount(): Promise<number>;
|
|
49
|
+
/**
|
|
50
|
+
* Get bucket status
|
|
51
|
+
*/
|
|
52
|
+
getStatus(): Promise<{
|
|
53
|
+
available: number;
|
|
54
|
+
capacity: number;
|
|
55
|
+
exhausted: boolean;
|
|
56
|
+
nextRefill: Date;
|
|
57
|
+
utilizationRate: number;
|
|
58
|
+
}>;
|
|
59
|
+
/**
|
|
60
|
+
* Force refill (admin use or scheduled job)
|
|
61
|
+
*/
|
|
62
|
+
refill(): Promise<void>;
|
|
63
|
+
/**
|
|
64
|
+
* Check if refill is needed (call on startup and periodically)
|
|
65
|
+
*/
|
|
66
|
+
private checkAndRefill;
|
|
67
|
+
/**
|
|
68
|
+
* Sync cached tokens to database
|
|
69
|
+
*/
|
|
70
|
+
private syncToDB;
|
|
71
|
+
/**
|
|
72
|
+
* Sync from database to cache
|
|
73
|
+
*/
|
|
74
|
+
private syncFromDB;
|
|
75
|
+
/**
|
|
76
|
+
* Get next midnight UTC
|
|
77
|
+
*/
|
|
78
|
+
private getNextMidnightUTC;
|
|
79
|
+
/**
|
|
80
|
+
* Add tokens (for testing or manual adjustments)
|
|
81
|
+
*/
|
|
82
|
+
addTokens(count: number): Promise<void>;
|
|
83
|
+
/**
|
|
84
|
+
* Get remaining tokens without consuming
|
|
85
|
+
*/
|
|
86
|
+
getRemainingTokens(): Promise<number>;
|
|
87
|
+
}
|
|
@@ -0,0 +1,341 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/*
|
|
3
|
+
Token Bucket Rate Limiting System
|
|
4
|
+
|
|
5
|
+
Implements per-API token bucket rate limiting with:
|
|
6
|
+
- Daily token budgets (refill at midnight UTC)
|
|
7
|
+
- MongoDB persistence (survives restarts)
|
|
8
|
+
- In-memory caching (fast checks)
|
|
9
|
+
- Cost tracking
|
|
10
|
+
*/
|
|
11
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
12
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
13
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
14
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
15
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
16
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
17
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
18
|
+
});
|
|
19
|
+
};
|
|
20
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
21
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
22
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
23
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
24
|
+
function step(op) {
|
|
25
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
26
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
27
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
28
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
29
|
+
switch (op[0]) {
|
|
30
|
+
case 0: case 1: t = op; break;
|
|
31
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
32
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
33
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
34
|
+
default:
|
|
35
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
36
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
37
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
38
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
39
|
+
if (t[2]) _.ops.pop();
|
|
40
|
+
_.trys.pop(); continue;
|
|
41
|
+
}
|
|
42
|
+
op = body.call(thisArg, _);
|
|
43
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
44
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
48
|
+
exports.TokenBucket = void 0;
|
|
49
|
+
var log = require('@pioneer-platform/loggerdog')();
|
|
50
|
+
var TAG = ' | TokenBucket | ';
|
|
51
|
+
var TokenBucket = /** @class */ (function () {
|
|
52
|
+
function TokenBucket(config, db) {
|
|
53
|
+
this.syncInterval = 5000; // Sync to DB every 5 seconds
|
|
54
|
+
this.config = config;
|
|
55
|
+
this.db = db;
|
|
56
|
+
this.collection = db.collection('api_token_buckets');
|
|
57
|
+
this.cachedTokens = 0;
|
|
58
|
+
this.lastSync = new Date();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Initialize bucket (call on startup)
|
|
62
|
+
*/
|
|
63
|
+
TokenBucket.prototype.initialize = function () {
|
|
64
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
65
|
+
var tag, bucket, newBucket;
|
|
66
|
+
return __generator(this, function (_a) {
|
|
67
|
+
switch (_a.label) {
|
|
68
|
+
case 0:
|
|
69
|
+
tag = TAG + ' | initialize | ';
|
|
70
|
+
return [4 /*yield*/, this.collection.findOne({ apiName: this.config.apiName })];
|
|
71
|
+
case 1:
|
|
72
|
+
bucket = _a.sent();
|
|
73
|
+
if (!!bucket) return [3 /*break*/, 3];
|
|
74
|
+
// Create new bucket
|
|
75
|
+
log.info(tag, "Creating new token bucket for ".concat(this.config.apiName));
|
|
76
|
+
newBucket = {
|
|
77
|
+
apiName: this.config.apiName,
|
|
78
|
+
capacity: this.config.capacity,
|
|
79
|
+
currentTokens: this.config.capacity, // Start full
|
|
80
|
+
lastRefill: new Date(),
|
|
81
|
+
nextRefill: this.getNextMidnightUTC(),
|
|
82
|
+
refillAmount: this.config.refillAmount,
|
|
83
|
+
refillInterval: this.config.refillInterval,
|
|
84
|
+
tokensPerRequest: this.config.tokensPerRequest,
|
|
85
|
+
costPerToken: this.config.costPerToken,
|
|
86
|
+
monthlyCost: 0,
|
|
87
|
+
monthlyBudget: this.config.monthlyBudget,
|
|
88
|
+
enabled: true,
|
|
89
|
+
exhausted: false,
|
|
90
|
+
createdAt: new Date(),
|
|
91
|
+
updatedAt: new Date()
|
|
92
|
+
};
|
|
93
|
+
return [4 /*yield*/, this.collection.insertOne(newBucket)];
|
|
94
|
+
case 2:
|
|
95
|
+
_a.sent();
|
|
96
|
+
return [3 /*break*/, 5];
|
|
97
|
+
case 3:
|
|
98
|
+
// Check if refill is needed
|
|
99
|
+
return [4 /*yield*/, this.checkAndRefill()];
|
|
100
|
+
case 4:
|
|
101
|
+
// Check if refill is needed
|
|
102
|
+
_a.sent();
|
|
103
|
+
_a.label = 5;
|
|
104
|
+
case 5: return [4 /*yield*/, this.collection.findOne({ apiName: this.config.apiName })];
|
|
105
|
+
case 6:
|
|
106
|
+
// Load current tokens into memory
|
|
107
|
+
bucket = _a.sent();
|
|
108
|
+
this.cachedTokens = bucket.currentTokens;
|
|
109
|
+
log.info(tag, "Initialized ".concat(this.config.apiName, ": ").concat(this.cachedTokens, "/").concat(this.config.capacity, " tokens"));
|
|
110
|
+
return [2 /*return*/];
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
};
|
|
115
|
+
/**
|
|
116
|
+
* Try to consume tokens (returns true if successful)
|
|
117
|
+
*/
|
|
118
|
+
TokenBucket.prototype.tryConsume = function () {
|
|
119
|
+
return __awaiter(this, arguments, void 0, function (count) {
|
|
120
|
+
var tag;
|
|
121
|
+
if (count === void 0) { count = 1; }
|
|
122
|
+
return __generator(this, function (_a) {
|
|
123
|
+
switch (_a.label) {
|
|
124
|
+
case 0:
|
|
125
|
+
tag = TAG + ' | tryConsume | ';
|
|
126
|
+
if (!(this.cachedTokens >= count)) return [3 /*break*/, 3];
|
|
127
|
+
this.cachedTokens -= count;
|
|
128
|
+
if (!(Date.now() - this.lastSync.getTime() > this.syncInterval)) return [3 /*break*/, 2];
|
|
129
|
+
return [4 /*yield*/, this.syncToDB()];
|
|
130
|
+
case 1:
|
|
131
|
+
_a.sent();
|
|
132
|
+
_a.label = 2;
|
|
133
|
+
case 2: return [2 /*return*/, true];
|
|
134
|
+
case 3:
|
|
135
|
+
// Cache might be stale, check DB
|
|
136
|
+
return [4 /*yield*/, this.syncFromDB()];
|
|
137
|
+
case 4:
|
|
138
|
+
// Cache might be stale, check DB
|
|
139
|
+
_a.sent();
|
|
140
|
+
if (!(this.cachedTokens >= count)) return [3 /*break*/, 6];
|
|
141
|
+
this.cachedTokens -= count;
|
|
142
|
+
return [4 /*yield*/, this.syncToDB()];
|
|
143
|
+
case 5:
|
|
144
|
+
_a.sent();
|
|
145
|
+
return [2 /*return*/, true];
|
|
146
|
+
case 6:
|
|
147
|
+
// Not enough tokens
|
|
148
|
+
log.warn(tag, "".concat(this.config.apiName, " bucket exhausted! ").concat(this.cachedTokens, "/").concat(count, " needed"));
|
|
149
|
+
// Mark as exhausted in DB
|
|
150
|
+
return [4 /*yield*/, this.collection.updateOne({ apiName: this.config.apiName }, {
|
|
151
|
+
$set: {
|
|
152
|
+
exhausted: true,
|
|
153
|
+
exhaustedAt: new Date(),
|
|
154
|
+
updatedAt: new Date()
|
|
155
|
+
}
|
|
156
|
+
})];
|
|
157
|
+
case 7:
|
|
158
|
+
// Mark as exhausted in DB
|
|
159
|
+
_a.sent();
|
|
160
|
+
return [2 /*return*/, false];
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
/**
|
|
166
|
+
* Get current token count
|
|
167
|
+
*/
|
|
168
|
+
TokenBucket.prototype.getTokenCount = function () {
|
|
169
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
170
|
+
return __generator(this, function (_a) {
|
|
171
|
+
switch (_a.label) {
|
|
172
|
+
case 0: return [4 /*yield*/, this.syncFromDB()];
|
|
173
|
+
case 1:
|
|
174
|
+
_a.sent();
|
|
175
|
+
return [2 /*return*/, this.cachedTokens];
|
|
176
|
+
}
|
|
177
|
+
});
|
|
178
|
+
});
|
|
179
|
+
};
|
|
180
|
+
/**
|
|
181
|
+
* Get bucket status
|
|
182
|
+
*/
|
|
183
|
+
TokenBucket.prototype.getStatus = function () {
|
|
184
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
185
|
+
var bucket;
|
|
186
|
+
return __generator(this, function (_a) {
|
|
187
|
+
switch (_a.label) {
|
|
188
|
+
case 0: return [4 /*yield*/, this.collection.findOne({ apiName: this.config.apiName })];
|
|
189
|
+
case 1:
|
|
190
|
+
bucket = _a.sent();
|
|
191
|
+
if (!bucket) {
|
|
192
|
+
throw new Error("Bucket not found: ".concat(this.config.apiName));
|
|
193
|
+
}
|
|
194
|
+
return [2 /*return*/, {
|
|
195
|
+
available: bucket.currentTokens,
|
|
196
|
+
capacity: bucket.capacity,
|
|
197
|
+
exhausted: bucket.exhausted,
|
|
198
|
+
nextRefill: bucket.nextRefill,
|
|
199
|
+
utilizationRate: 1 - (bucket.currentTokens / bucket.capacity)
|
|
200
|
+
}];
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
};
|
|
205
|
+
/**
|
|
206
|
+
* Force refill (admin use or scheduled job)
|
|
207
|
+
*/
|
|
208
|
+
TokenBucket.prototype.refill = function () {
|
|
209
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
210
|
+
var tag;
|
|
211
|
+
return __generator(this, function (_a) {
|
|
212
|
+
switch (_a.label) {
|
|
213
|
+
case 0:
|
|
214
|
+
tag = TAG + ' | refill | ';
|
|
215
|
+
return [4 /*yield*/, this.collection.updateOne({ apiName: this.config.apiName }, {
|
|
216
|
+
$set: {
|
|
217
|
+
currentTokens: this.config.refillAmount,
|
|
218
|
+
lastRefill: new Date(),
|
|
219
|
+
nextRefill: this.getNextMidnightUTC(),
|
|
220
|
+
exhausted: false,
|
|
221
|
+
exhaustedAt: undefined,
|
|
222
|
+
updatedAt: new Date()
|
|
223
|
+
}
|
|
224
|
+
})];
|
|
225
|
+
case 1:
|
|
226
|
+
_a.sent();
|
|
227
|
+
this.cachedTokens = this.config.refillAmount;
|
|
228
|
+
log.info(tag, "Refilled ".concat(this.config.apiName, " to ").concat(this.config.refillAmount, " tokens"));
|
|
229
|
+
return [2 /*return*/];
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Check if refill is needed (call on startup and periodically)
|
|
236
|
+
*/
|
|
237
|
+
TokenBucket.prototype.checkAndRefill = function () {
|
|
238
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
239
|
+
var bucket;
|
|
240
|
+
return __generator(this, function (_a) {
|
|
241
|
+
switch (_a.label) {
|
|
242
|
+
case 0: return [4 /*yield*/, this.collection.findOne({ apiName: this.config.apiName })];
|
|
243
|
+
case 1:
|
|
244
|
+
bucket = _a.sent();
|
|
245
|
+
if (!bucket)
|
|
246
|
+
return [2 /*return*/];
|
|
247
|
+
if (!(new Date() >= bucket.nextRefill)) return [3 /*break*/, 3];
|
|
248
|
+
return [4 /*yield*/, this.refill()];
|
|
249
|
+
case 2:
|
|
250
|
+
_a.sent();
|
|
251
|
+
_a.label = 3;
|
|
252
|
+
case 3: return [2 /*return*/];
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
};
|
|
257
|
+
/**
|
|
258
|
+
* Sync cached tokens to database
|
|
259
|
+
*/
|
|
260
|
+
TokenBucket.prototype.syncToDB = function () {
|
|
261
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
262
|
+
return __generator(this, function (_a) {
|
|
263
|
+
switch (_a.label) {
|
|
264
|
+
case 0: return [4 /*yield*/, this.collection.updateOne({ apiName: this.config.apiName }, {
|
|
265
|
+
$set: {
|
|
266
|
+
currentTokens: this.cachedTokens,
|
|
267
|
+
updatedAt: new Date()
|
|
268
|
+
}
|
|
269
|
+
})];
|
|
270
|
+
case 1:
|
|
271
|
+
_a.sent();
|
|
272
|
+
this.lastSync = new Date();
|
|
273
|
+
return [2 /*return*/];
|
|
274
|
+
}
|
|
275
|
+
});
|
|
276
|
+
});
|
|
277
|
+
};
|
|
278
|
+
/**
|
|
279
|
+
* Sync from database to cache
|
|
280
|
+
*/
|
|
281
|
+
TokenBucket.prototype.syncFromDB = function () {
|
|
282
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
283
|
+
var bucket;
|
|
284
|
+
return __generator(this, function (_a) {
|
|
285
|
+
switch (_a.label) {
|
|
286
|
+
case 0: return [4 /*yield*/, this.collection.findOne({ apiName: this.config.apiName })];
|
|
287
|
+
case 1:
|
|
288
|
+
bucket = _a.sent();
|
|
289
|
+
if (bucket) {
|
|
290
|
+
this.cachedTokens = bucket.currentTokens;
|
|
291
|
+
this.lastSync = new Date();
|
|
292
|
+
}
|
|
293
|
+
return [2 /*return*/];
|
|
294
|
+
}
|
|
295
|
+
});
|
|
296
|
+
});
|
|
297
|
+
};
|
|
298
|
+
/**
|
|
299
|
+
* Get next midnight UTC
|
|
300
|
+
*/
|
|
301
|
+
TokenBucket.prototype.getNextMidnightUTC = function () {
|
|
302
|
+
var now = new Date();
|
|
303
|
+
var midnight = new Date(Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate() + 1, // Next day
|
|
304
|
+
0, 0, 0, 0));
|
|
305
|
+
return midnight;
|
|
306
|
+
};
|
|
307
|
+
/**
|
|
308
|
+
* Add tokens (for testing or manual adjustments)
|
|
309
|
+
*/
|
|
310
|
+
TokenBucket.prototype.addTokens = function (count) {
|
|
311
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
312
|
+
return __generator(this, function (_a) {
|
|
313
|
+
switch (_a.label) {
|
|
314
|
+
case 0:
|
|
315
|
+
this.cachedTokens = Math.min(this.cachedTokens + count, this.config.capacity);
|
|
316
|
+
return [4 /*yield*/, this.syncToDB()];
|
|
317
|
+
case 1:
|
|
318
|
+
_a.sent();
|
|
319
|
+
return [2 /*return*/];
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
};
|
|
324
|
+
/**
|
|
325
|
+
* Get remaining tokens without consuming
|
|
326
|
+
*/
|
|
327
|
+
TokenBucket.prototype.getRemainingTokens = function () {
|
|
328
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
329
|
+
return __generator(this, function (_a) {
|
|
330
|
+
switch (_a.label) {
|
|
331
|
+
case 0: return [4 /*yield*/, this.syncFromDB()];
|
|
332
|
+
case 1:
|
|
333
|
+
_a.sent();
|
|
334
|
+
return [2 /*return*/, this.cachedTokens];
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
});
|
|
338
|
+
};
|
|
339
|
+
return TokenBucket;
|
|
340
|
+
}());
|
|
341
|
+
exports.TokenBucket = TokenBucket;
|
package/package.json
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pioneer-platform/markets",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.15.0",
|
|
4
4
|
"main": "./lib/index.js",
|
|
5
5
|
"types": "./lib/index.d.ts",
|
|
6
6
|
"dependencies": {
|
|
7
7
|
"@pioneer-platform/default-redis": "^8.11.7",
|
|
8
8
|
"@pioneer-platform/loggerdog": "^8.11.0",
|
|
9
9
|
"@pioneer-platform/pioneer-coins": "^9.11.0",
|
|
10
|
-
"@pioneer-platform/pioneer-discovery": "^8.
|
|
10
|
+
"@pioneer-platform/pioneer-discovery": "^8.14.1",
|
|
11
11
|
"@pioneer-platform/pioneer-types": "^8.11.0",
|
|
12
12
|
"@pioneer-platform/pro-token": "^0.9.0",
|
|
13
13
|
"@shapeshiftoss/caip": "^9.0.0-alpha.0",
|
|
14
14
|
"axios": "^1.6.0",
|
|
15
15
|
"axios-retry": "^3.2.0",
|
|
16
16
|
"bottleneck": "^2.19.5",
|
|
17
|
-
"
|
|
17
|
+
"ccxt": "^4.5.18",
|
|
18
|
+
"dotenv": "^8.2.0",
|
|
19
|
+
"node-schedule": "^2.1.1"
|
|
18
20
|
},
|
|
19
21
|
"scripts": {
|
|
20
22
|
"npm": "pnpm i",
|
|
@@ -27,6 +29,7 @@
|
|
|
27
29
|
"devDependencies": {
|
|
28
30
|
"@types/jest": "^25.2.3",
|
|
29
31
|
"@types/node": "^18.16.0",
|
|
32
|
+
"@types/node-schedule": "^2.1.8",
|
|
30
33
|
"@types/source-map-support": "^0.5.3",
|
|
31
34
|
"jest": "^26.4.2",
|
|
32
35
|
"onchange": "^7.0.2",
|