backtest-kit 1.0.2 → 1.0.3

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/README.md CHANGED
@@ -21,12 +21,12 @@ npm install
21
21
 
22
22
  ## Quick Start
23
23
 
24
- ### 1. Add Data Source (Candles)
24
+ ### 1. Add Data Source (Exchange)
25
25
 
26
26
  ```typescript
27
- import { addCandle } from "./src/function/add";
27
+ import { addExchange } from "./src/function/add";
28
28
 
29
- addCandle({
29
+ addExchange({
30
30
  getCandles: async (symbol, interval, since, limit) => {
31
31
  // Fetch candle data from your source (exchange API, database, etc.)
32
32
  return [
@@ -164,16 +164,17 @@ console.log(result.accumulator);
164
164
  ```
165
165
  src/
166
166
  ├── function/ # High-level API functions
167
- │ ├── add.ts # Add schemas (strategy, candle)
167
+ │ ├── add.ts # Add schemas (strategy, exchange)
168
168
  │ ├── backtest.ts # Backtesting functions
169
169
  │ ├── reduce.ts # Reduce pattern for accumulation
170
- └── run.ts # Real-time execution
170
+ ├── run.ts # Real-time execution
171
+ │ └── exchange.ts # Exchange data functions
171
172
  ├── client/ # Client implementations
172
- │ ├── ClientCandle.ts # Candle client with VWAP
173
+ │ ├── ClientExchange.ts # Exchange client with VWAP
173
174
  │ └── ClientStrategy.ts # Strategy client with signal lifecycle
174
175
  ├── interfaces/ # TypeScript interfaces
175
176
  │ ├── Strategy.interface.ts
176
- │ └── Candle.interface.ts
177
+ │ └── Exchange.interface.ts
177
178
  └── lib/ # Core library with DI
178
179
  ├── core/ # Dependency injection
179
180
  └── services/ # Services (schema, connection, public)
@@ -200,12 +201,18 @@ export const PERCENT_FEE = 0.1; // 0.1%
200
201
 
201
202
  ### Functions
202
203
 
203
- #### `addCandle(candleSchema: ICandleSchema)`
204
- Add candle data source.
204
+ #### `addExchange(exchangeSchema: IExchangeSchema)`
205
+ Add exchange data source for candles.
205
206
 
206
207
  #### `addStrategy(strategySchema: IStrategySchema)`
207
208
  Add trading strategy.
208
209
 
210
+ #### `getCandles(symbol, interval, limit): Promise<ICandleData[]>`
211
+ Get candle data from exchange.
212
+
213
+ #### `getAveragePrice(symbol): Promise<number>`
214
+ Get VWAP average price based on last 5 1m candles.
215
+
209
216
  #### `runBacktest(symbol: string, timeframes: Date[]): Promise<IBacktestResult>`
210
217
  Run backtest and return closed trades only.
211
218
 
package/build/index.cjs CHANGED
@@ -40,15 +40,15 @@ const contextServices$1 = {
40
40
  executionContextService: Symbol('executionContextService'),
41
41
  };
42
42
  const connectionServices$1 = {
43
- candleConnectionService: Symbol('candleConnectionService'),
43
+ exchangeConnectionService: Symbol('exchangeConnectionService'),
44
44
  strategyConnectionService: Symbol('strategyConnectionService'),
45
45
  };
46
46
  const schemaServices$1 = {
47
- candleSchemaService: Symbol('candleSchemaService'),
47
+ exchangeSchemaService: Symbol('exchangeSchemaService'),
48
48
  strategySchemaService: Symbol('strategySchemaService'),
49
49
  };
50
50
  const publicServices$1 = {
51
- candlePublicService: Symbol('candlePublicService'),
51
+ exchangePublicService: Symbol('exchangePublicService'),
52
52
  strategyPublicService: Symbol('strategyPublicService'),
53
53
  };
54
54
  const TYPES = {
@@ -71,11 +71,11 @@ const INTERVAL_MINUTES = {
71
71
  "6h": 360,
72
72
  "8h": 480,
73
73
  };
74
- class ClientCandle {
74
+ class ClientExchange {
75
75
  constructor(params) {
76
76
  this.params = params;
77
77
  this.getCandles = async (symbol, interval, limit) => {
78
- this.params.logger.debug(`ClientCandle getCandles`, {
78
+ this.params.logger.debug(`ClientExchange getCandles`, {
79
79
  symbol,
80
80
  interval,
81
81
  limit,
@@ -83,7 +83,7 @@ class ClientCandle {
83
83
  const step = INTERVAL_MINUTES[interval];
84
84
  const adjust = step * limit;
85
85
  if (!adjust) {
86
- throw new Error(`ClientCandle unknown time adjust for interval=${interval}`);
86
+ throw new Error(`ClientExchange unknown time adjust for interval=${interval}`);
87
87
  }
88
88
  const since = new Date(this.params.execution.context.when.getTime() - adjust * 60 * 1000);
89
89
  const data = await this.params.getCandles(symbol, interval, since, limit);
@@ -93,12 +93,12 @@ class ClientCandle {
93
93
  return data;
94
94
  };
95
95
  this.getAveragePrice = async (symbol) => {
96
- this.params.logger.debug(`ClientCandle getAveragePrice`, {
96
+ this.params.logger.debug(`ClientExchange getAveragePrice`, {
97
97
  symbol,
98
98
  });
99
99
  const candles = await this.getCandles(symbol, "1m", 5);
100
100
  if (candles.length === 0) {
101
- throw new Error(`ClientCandle getAveragePrice: no candles data for symbol=${symbol}`);
101
+ throw new Error(`ClientExchange getAveragePrice: no candles data for symbol=${symbol}`);
102
102
  }
103
103
  // VWAP (Volume Weighted Average Price)
104
104
  // Используем типичную цену (typical price) = (high + low + close) / 3
@@ -115,36 +115,66 @@ class ClientCandle {
115
115
  const vwap = sumPriceVolume / totalVolume;
116
116
  return vwap;
117
117
  };
118
+ this.formatQuantity = async (symbol, quantity) => {
119
+ this.params.logger.debug("binanceService formatQuantity", {
120
+ symbol,
121
+ quantity,
122
+ });
123
+ return await this.params.formatQuantity(symbol, quantity);
124
+ };
125
+ this.formatPrice = async (symbol, price) => {
126
+ this.params.logger.debug("binanceService formatPrice", {
127
+ symbol,
128
+ price,
129
+ });
130
+ return await this.params.formatPrice(symbol, price);
131
+ };
118
132
  }
119
133
  }
120
134
 
121
- class CandleConnectionService {
135
+ class ExchangeConnectionService {
122
136
  constructor() {
123
137
  this.loggerService = inject(TYPES.loggerService);
124
138
  this.executionContextService = inject(TYPES.executionContextService);
125
- this.candleSchemaService = inject(TYPES.candleSchemaService);
126
- this.getCandle = functoolsKit.memoize((symbol) => `${symbol}`, () => {
127
- const { getCandles, callbacks } = this.candleSchemaService.getSchema();
128
- return new ClientCandle({
139
+ this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
140
+ this.getExchange = functoolsKit.memoize((symbol) => `${symbol}`, () => {
141
+ const { getCandles, formatPrice, formatQuantity, callbacks } = this.exchangeSchemaService.getSchema();
142
+ return new ClientExchange({
129
143
  execution: this.executionContextService,
130
144
  logger: this.loggerService,
131
145
  getCandles,
146
+ formatPrice,
147
+ formatQuantity,
132
148
  callbacks,
133
149
  });
134
150
  });
135
151
  this.getCandles = async (symbol, interval, limit) => {
136
- this.loggerService.log("candleConnectionService getCandles", {
152
+ this.loggerService.log("exchangeConnectionService getCandles", {
137
153
  symbol,
138
154
  interval,
139
155
  limit,
140
156
  });
141
- return await this.getCandle(symbol).getCandles(symbol, interval, limit);
157
+ return await this.getExchange(symbol).getCandles(symbol, interval, limit);
142
158
  };
143
159
  this.getAveragePrice = async (symbol) => {
144
- this.loggerService.log("candleConnectionService getAveragePrice", {
160
+ this.loggerService.log("exchangeConnectionService getAveragePrice", {
161
+ symbol,
162
+ });
163
+ return await this.getExchange(symbol).getAveragePrice(symbol);
164
+ };
165
+ this.formatPrice = async (symbol, price) => {
166
+ this.loggerService.log("exchangeConnectionService getAveragePrice", {
145
167
  symbol,
168
+ price,
146
169
  });
147
- return await this.getCandle(symbol).getAveragePrice(symbol);
170
+ return await this.getExchange(symbol).formatPrice(symbol, price);
171
+ };
172
+ this.formatQuantity = async (symbol, quantity) => {
173
+ this.loggerService.log("exchangeConnectionService getAveragePrice", {
174
+ symbol,
175
+ quantity,
176
+ });
177
+ return await this.getExchange(symbol).formatQuantity(symbol, quantity);
148
178
  };
149
179
  }
150
180
  }
@@ -217,7 +247,7 @@ class ClientStrategy {
217
247
  const when = this.params.execution.context.when;
218
248
  const signal = this._pendingSignal;
219
249
  // Получаем среднюю цену
220
- const averagePrice = await this.params.candle.getAveragePrice(symbol);
250
+ const averagePrice = await this.params.exchange.getAveragePrice(symbol);
221
251
  this.params.logger.debug("ClientStrategy tick check", {
222
252
  symbol,
223
253
  averagePrice,
@@ -290,14 +320,14 @@ class StrategyConnectionService {
290
320
  this.loggerService = inject(TYPES.loggerService);
291
321
  this.executionContextService = inject(TYPES.executionContextService);
292
322
  this.strategySchemaService = inject(TYPES.strategySchemaService);
293
- this.candleConnectionService = inject(TYPES.candleConnectionService);
323
+ this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
294
324
  this.getStrategy = functoolsKit.memoize((symbol) => `${symbol}`, (symbol) => {
295
325
  const { getSignal, callbacks } = this.strategySchemaService.getSchema();
296
326
  return new ClientStrategy({
297
327
  symbol,
298
328
  execution: this.executionContextService,
299
329
  logger: this.loggerService,
300
- candle: this.candleConnectionService,
330
+ exchange: this.exchangeConnectionService,
301
331
  getSignal,
302
332
  callbacks,
303
333
  });
@@ -317,12 +347,12 @@ const ExecutionContextService = diScoped.scoped(class {
317
347
  }
318
348
  });
319
349
 
320
- class CandlePublicService {
350
+ class ExchangePublicService {
321
351
  constructor() {
322
352
  this.loggerService = inject(TYPES.loggerService);
323
- this.candleConnectionService = inject(TYPES.candleConnectionService);
353
+ this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
324
354
  this.getCandles = async (symbol, interval, limit, when, backtest) => {
325
- this.loggerService.log("candlePublicService getCandles", {
355
+ this.loggerService.log("exchangePublicService getCandles", {
326
356
  symbol,
327
357
  interval,
328
358
  limit,
@@ -330,20 +360,48 @@ class CandlePublicService {
330
360
  backtest,
331
361
  });
332
362
  return await ExecutionContextService.runInContext(async () => {
333
- return await this.candleConnectionService.getCandles(symbol, interval, limit);
363
+ return await this.exchangeConnectionService.getCandles(symbol, interval, limit);
334
364
  }, {
335
365
  when,
336
366
  backtest,
337
367
  });
338
368
  };
339
369
  this.getAveragePrice = async (symbol, when, backtest) => {
340
- this.loggerService.log("candlePublicService getAveragePrice", {
370
+ this.loggerService.log("exchangePublicService getAveragePrice", {
371
+ symbol,
372
+ when,
373
+ backtest,
374
+ });
375
+ return await ExecutionContextService.runInContext(async () => {
376
+ return await this.exchangeConnectionService.getAveragePrice(symbol);
377
+ }, {
378
+ when,
379
+ backtest,
380
+ });
381
+ };
382
+ this.formatPrice = async (symbol, price, when, backtest) => {
383
+ this.loggerService.log("exchangePublicService formatPrice", {
341
384
  symbol,
385
+ price,
342
386
  when,
343
387
  backtest,
344
388
  });
345
389
  return await ExecutionContextService.runInContext(async () => {
346
- return await this.candleConnectionService.getAveragePrice(symbol);
390
+ return await this.exchangeConnectionService.formatPrice(symbol, price);
391
+ }, {
392
+ when,
393
+ backtest,
394
+ });
395
+ };
396
+ this.formatQuantity = async (symbol, quantity, when, backtest) => {
397
+ this.loggerService.log("exchangePublicService formatQuantity", {
398
+ symbol,
399
+ quantity,
400
+ when,
401
+ backtest,
402
+ });
403
+ return await ExecutionContextService.runInContext(async () => {
404
+ return await this.exchangeConnectionService.formatQuantity(symbol, quantity);
347
405
  }, {
348
406
  when,
349
407
  backtest,
@@ -372,19 +430,19 @@ class StrategyPublicService {
372
430
  }
373
431
  }
374
432
 
375
- class CandleSchemaService {
433
+ class ExchangeSchemaService {
376
434
  constructor() {
377
435
  this.loggerService = inject(TYPES.loggerService);
378
436
  this.getSchema = () => {
379
- this.loggerService.log("candleSchemaService getSchema");
380
- if (!this._candleSchema) {
381
- throw new Error("CandleSchemaService no candle source provided");
437
+ this.loggerService.log("exchangeSchemaService getSchema");
438
+ if (!this._exchangeSchema) {
439
+ throw new Error("ExchangeSchemaService no exchange source provided");
382
440
  }
383
- return this._candleSchema;
441
+ return this._exchangeSchema;
384
442
  };
385
- this.addSchema = (candleSchema) => {
386
- this.loggerService.log("candleSchemaService addSchema");
387
- this._candleSchema = candleSchema;
443
+ this.addSchema = (exchangeSchema) => {
444
+ this.loggerService.log("exchangeSchemaService addSchema");
445
+ this._exchangeSchema = exchangeSchema;
388
446
  };
389
447
  }
390
448
  }
@@ -413,15 +471,15 @@ class StrategySchemaService {
413
471
  provide(TYPES.executionContextService, () => new ExecutionContextService());
414
472
  }
415
473
  {
416
- provide(TYPES.candleConnectionService, () => new CandleConnectionService());
474
+ provide(TYPES.exchangeConnectionService, () => new ExchangeConnectionService());
417
475
  provide(TYPES.strategyConnectionService, () => new StrategyConnectionService());
418
476
  }
419
477
  {
420
- provide(TYPES.candleSchemaService, () => new CandleSchemaService());
478
+ provide(TYPES.exchangeSchemaService, () => new ExchangeSchemaService());
421
479
  provide(TYPES.strategySchemaService, () => new StrategySchemaService());
422
480
  }
423
481
  {
424
- provide(TYPES.candlePublicService, () => new CandlePublicService());
482
+ provide(TYPES.exchangePublicService, () => new ExchangePublicService());
425
483
  provide(TYPES.strategyPublicService, () => new StrategyPublicService());
426
484
  }
427
485
 
@@ -432,15 +490,15 @@ const contextServices = {
432
490
  executionContextService: inject(TYPES.executionContextService),
433
491
  };
434
492
  const connectionServices = {
435
- candleConnectionService: inject(TYPES.candleConnectionService),
493
+ exchangeConnectionService: inject(TYPES.exchangeConnectionService),
436
494
  strategyConnectionService: inject(TYPES.strategyConnectionService),
437
495
  };
438
496
  const schemaServices = {
439
- candleSchemaService: inject(TYPES.candleSchemaService),
497
+ exchangeSchemaService: inject(TYPES.exchangeSchemaService),
440
498
  strategySchemaService: inject(TYPES.strategySchemaService),
441
499
  };
442
500
  const publicServices = {
443
- candlePublicService: inject(TYPES.candlePublicService),
501
+ exchangePublicService: inject(TYPES.exchangePublicService),
444
502
  strategyPublicService: inject(TYPES.strategyPublicService),
445
503
  };
446
504
  const backtest = {
@@ -455,8 +513,8 @@ init();
455
513
  function addStrategy(strategySchema) {
456
514
  backtest.strategySchemaService.addSchema(strategySchema);
457
515
  }
458
- function addCandle(candleSchema) {
459
- backtest.candleSchemaService.addSchema(candleSchema);
516
+ function addExchange(exchangeSchema) {
517
+ backtest.exchangeSchemaService.addSchema(exchangeSchema);
460
518
  }
461
519
 
462
520
  async function runBacktest(symbol, timeframes) {
@@ -573,16 +631,24 @@ function stopAll() {
573
631
  }
574
632
 
575
633
  async function getCandles(symbol, interval, limit) {
576
- return await backtest.candleConnectionService.getCandles(symbol, interval, limit);
634
+ return await backtest.exchangeConnectionService.getCandles(symbol, interval, limit);
577
635
  }
578
636
  async function getAveragePrice(symbol) {
579
- return await backtest.candleConnectionService.getAveragePrice(symbol);
637
+ return await backtest.exchangeConnectionService.getAveragePrice(symbol);
638
+ }
639
+ async function formatPrice(symbol, price) {
640
+ return await backtest.exchangeConnectionService.formatPrice(symbol, price);
641
+ }
642
+ async function formatQuantity(symbol, quantity) {
643
+ return await backtest.exchangeConnectionService.formatQuantity(symbol, quantity);
580
644
  }
581
645
 
582
646
  exports.ExecutionContextService = ExecutionContextService;
583
- exports.addCandle = addCandle;
647
+ exports.addExchange = addExchange;
584
648
  exports.addStrategy = addStrategy;
585
649
  exports.backtest = backtest;
650
+ exports.formatPrice = formatPrice;
651
+ exports.formatQuantity = formatQuantity;
586
652
  exports.getAveragePrice = getAveragePrice;
587
653
  exports.getCandles = getCandles;
588
654
  exports.reduce = reduce;
package/build/index.mjs CHANGED
@@ -38,15 +38,15 @@ const contextServices$1 = {
38
38
  executionContextService: Symbol('executionContextService'),
39
39
  };
40
40
  const connectionServices$1 = {
41
- candleConnectionService: Symbol('candleConnectionService'),
41
+ exchangeConnectionService: Symbol('exchangeConnectionService'),
42
42
  strategyConnectionService: Symbol('strategyConnectionService'),
43
43
  };
44
44
  const schemaServices$1 = {
45
- candleSchemaService: Symbol('candleSchemaService'),
45
+ exchangeSchemaService: Symbol('exchangeSchemaService'),
46
46
  strategySchemaService: Symbol('strategySchemaService'),
47
47
  };
48
48
  const publicServices$1 = {
49
- candlePublicService: Symbol('candlePublicService'),
49
+ exchangePublicService: Symbol('exchangePublicService'),
50
50
  strategyPublicService: Symbol('strategyPublicService'),
51
51
  };
52
52
  const TYPES = {
@@ -69,11 +69,11 @@ const INTERVAL_MINUTES = {
69
69
  "6h": 360,
70
70
  "8h": 480,
71
71
  };
72
- class ClientCandle {
72
+ class ClientExchange {
73
73
  constructor(params) {
74
74
  this.params = params;
75
75
  this.getCandles = async (symbol, interval, limit) => {
76
- this.params.logger.debug(`ClientCandle getCandles`, {
76
+ this.params.logger.debug(`ClientExchange getCandles`, {
77
77
  symbol,
78
78
  interval,
79
79
  limit,
@@ -81,7 +81,7 @@ class ClientCandle {
81
81
  const step = INTERVAL_MINUTES[interval];
82
82
  const adjust = step * limit;
83
83
  if (!adjust) {
84
- throw new Error(`ClientCandle unknown time adjust for interval=${interval}`);
84
+ throw new Error(`ClientExchange unknown time adjust for interval=${interval}`);
85
85
  }
86
86
  const since = new Date(this.params.execution.context.when.getTime() - adjust * 60 * 1000);
87
87
  const data = await this.params.getCandles(symbol, interval, since, limit);
@@ -91,12 +91,12 @@ class ClientCandle {
91
91
  return data;
92
92
  };
93
93
  this.getAveragePrice = async (symbol) => {
94
- this.params.logger.debug(`ClientCandle getAveragePrice`, {
94
+ this.params.logger.debug(`ClientExchange getAveragePrice`, {
95
95
  symbol,
96
96
  });
97
97
  const candles = await this.getCandles(symbol, "1m", 5);
98
98
  if (candles.length === 0) {
99
- throw new Error(`ClientCandle getAveragePrice: no candles data for symbol=${symbol}`);
99
+ throw new Error(`ClientExchange getAveragePrice: no candles data for symbol=${symbol}`);
100
100
  }
101
101
  // VWAP (Volume Weighted Average Price)
102
102
  // Используем типичную цену (typical price) = (high + low + close) / 3
@@ -113,36 +113,66 @@ class ClientCandle {
113
113
  const vwap = sumPriceVolume / totalVolume;
114
114
  return vwap;
115
115
  };
116
+ this.formatQuantity = async (symbol, quantity) => {
117
+ this.params.logger.debug("binanceService formatQuantity", {
118
+ symbol,
119
+ quantity,
120
+ });
121
+ return await this.params.formatQuantity(symbol, quantity);
122
+ };
123
+ this.formatPrice = async (symbol, price) => {
124
+ this.params.logger.debug("binanceService formatPrice", {
125
+ symbol,
126
+ price,
127
+ });
128
+ return await this.params.formatPrice(symbol, price);
129
+ };
116
130
  }
117
131
  }
118
132
 
119
- class CandleConnectionService {
133
+ class ExchangeConnectionService {
120
134
  constructor() {
121
135
  this.loggerService = inject(TYPES.loggerService);
122
136
  this.executionContextService = inject(TYPES.executionContextService);
123
- this.candleSchemaService = inject(TYPES.candleSchemaService);
124
- this.getCandle = memoize((symbol) => `${symbol}`, () => {
125
- const { getCandles, callbacks } = this.candleSchemaService.getSchema();
126
- return new ClientCandle({
137
+ this.exchangeSchemaService = inject(TYPES.exchangeSchemaService);
138
+ this.getExchange = memoize((symbol) => `${symbol}`, () => {
139
+ const { getCandles, formatPrice, formatQuantity, callbacks } = this.exchangeSchemaService.getSchema();
140
+ return new ClientExchange({
127
141
  execution: this.executionContextService,
128
142
  logger: this.loggerService,
129
143
  getCandles,
144
+ formatPrice,
145
+ formatQuantity,
130
146
  callbacks,
131
147
  });
132
148
  });
133
149
  this.getCandles = async (symbol, interval, limit) => {
134
- this.loggerService.log("candleConnectionService getCandles", {
150
+ this.loggerService.log("exchangeConnectionService getCandles", {
135
151
  symbol,
136
152
  interval,
137
153
  limit,
138
154
  });
139
- return await this.getCandle(symbol).getCandles(symbol, interval, limit);
155
+ return await this.getExchange(symbol).getCandles(symbol, interval, limit);
140
156
  };
141
157
  this.getAveragePrice = async (symbol) => {
142
- this.loggerService.log("candleConnectionService getAveragePrice", {
158
+ this.loggerService.log("exchangeConnectionService getAveragePrice", {
159
+ symbol,
160
+ });
161
+ return await this.getExchange(symbol).getAveragePrice(symbol);
162
+ };
163
+ this.formatPrice = async (symbol, price) => {
164
+ this.loggerService.log("exchangeConnectionService getAveragePrice", {
143
165
  symbol,
166
+ price,
144
167
  });
145
- return await this.getCandle(symbol).getAveragePrice(symbol);
168
+ return await this.getExchange(symbol).formatPrice(symbol, price);
169
+ };
170
+ this.formatQuantity = async (symbol, quantity) => {
171
+ this.loggerService.log("exchangeConnectionService getAveragePrice", {
172
+ symbol,
173
+ quantity,
174
+ });
175
+ return await this.getExchange(symbol).formatQuantity(symbol, quantity);
146
176
  };
147
177
  }
148
178
  }
@@ -215,7 +245,7 @@ class ClientStrategy {
215
245
  const when = this.params.execution.context.when;
216
246
  const signal = this._pendingSignal;
217
247
  // Получаем среднюю цену
218
- const averagePrice = await this.params.candle.getAveragePrice(symbol);
248
+ const averagePrice = await this.params.exchange.getAveragePrice(symbol);
219
249
  this.params.logger.debug("ClientStrategy tick check", {
220
250
  symbol,
221
251
  averagePrice,
@@ -288,14 +318,14 @@ class StrategyConnectionService {
288
318
  this.loggerService = inject(TYPES.loggerService);
289
319
  this.executionContextService = inject(TYPES.executionContextService);
290
320
  this.strategySchemaService = inject(TYPES.strategySchemaService);
291
- this.candleConnectionService = inject(TYPES.candleConnectionService);
321
+ this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
292
322
  this.getStrategy = memoize((symbol) => `${symbol}`, (symbol) => {
293
323
  const { getSignal, callbacks } = this.strategySchemaService.getSchema();
294
324
  return new ClientStrategy({
295
325
  symbol,
296
326
  execution: this.executionContextService,
297
327
  logger: this.loggerService,
298
- candle: this.candleConnectionService,
328
+ exchange: this.exchangeConnectionService,
299
329
  getSignal,
300
330
  callbacks,
301
331
  });
@@ -315,12 +345,12 @@ const ExecutionContextService = scoped(class {
315
345
  }
316
346
  });
317
347
 
318
- class CandlePublicService {
348
+ class ExchangePublicService {
319
349
  constructor() {
320
350
  this.loggerService = inject(TYPES.loggerService);
321
- this.candleConnectionService = inject(TYPES.candleConnectionService);
351
+ this.exchangeConnectionService = inject(TYPES.exchangeConnectionService);
322
352
  this.getCandles = async (symbol, interval, limit, when, backtest) => {
323
- this.loggerService.log("candlePublicService getCandles", {
353
+ this.loggerService.log("exchangePublicService getCandles", {
324
354
  symbol,
325
355
  interval,
326
356
  limit,
@@ -328,20 +358,48 @@ class CandlePublicService {
328
358
  backtest,
329
359
  });
330
360
  return await ExecutionContextService.runInContext(async () => {
331
- return await this.candleConnectionService.getCandles(symbol, interval, limit);
361
+ return await this.exchangeConnectionService.getCandles(symbol, interval, limit);
332
362
  }, {
333
363
  when,
334
364
  backtest,
335
365
  });
336
366
  };
337
367
  this.getAveragePrice = async (symbol, when, backtest) => {
338
- this.loggerService.log("candlePublicService getAveragePrice", {
368
+ this.loggerService.log("exchangePublicService getAveragePrice", {
369
+ symbol,
370
+ when,
371
+ backtest,
372
+ });
373
+ return await ExecutionContextService.runInContext(async () => {
374
+ return await this.exchangeConnectionService.getAveragePrice(symbol);
375
+ }, {
376
+ when,
377
+ backtest,
378
+ });
379
+ };
380
+ this.formatPrice = async (symbol, price, when, backtest) => {
381
+ this.loggerService.log("exchangePublicService formatPrice", {
339
382
  symbol,
383
+ price,
340
384
  when,
341
385
  backtest,
342
386
  });
343
387
  return await ExecutionContextService.runInContext(async () => {
344
- return await this.candleConnectionService.getAveragePrice(symbol);
388
+ return await this.exchangeConnectionService.formatPrice(symbol, price);
389
+ }, {
390
+ when,
391
+ backtest,
392
+ });
393
+ };
394
+ this.formatQuantity = async (symbol, quantity, when, backtest) => {
395
+ this.loggerService.log("exchangePublicService formatQuantity", {
396
+ symbol,
397
+ quantity,
398
+ when,
399
+ backtest,
400
+ });
401
+ return await ExecutionContextService.runInContext(async () => {
402
+ return await this.exchangeConnectionService.formatQuantity(symbol, quantity);
345
403
  }, {
346
404
  when,
347
405
  backtest,
@@ -370,19 +428,19 @@ class StrategyPublicService {
370
428
  }
371
429
  }
372
430
 
373
- class CandleSchemaService {
431
+ class ExchangeSchemaService {
374
432
  constructor() {
375
433
  this.loggerService = inject(TYPES.loggerService);
376
434
  this.getSchema = () => {
377
- this.loggerService.log("candleSchemaService getSchema");
378
- if (!this._candleSchema) {
379
- throw new Error("CandleSchemaService no candle source provided");
435
+ this.loggerService.log("exchangeSchemaService getSchema");
436
+ if (!this._exchangeSchema) {
437
+ throw new Error("ExchangeSchemaService no exchange source provided");
380
438
  }
381
- return this._candleSchema;
439
+ return this._exchangeSchema;
382
440
  };
383
- this.addSchema = (candleSchema) => {
384
- this.loggerService.log("candleSchemaService addSchema");
385
- this._candleSchema = candleSchema;
441
+ this.addSchema = (exchangeSchema) => {
442
+ this.loggerService.log("exchangeSchemaService addSchema");
443
+ this._exchangeSchema = exchangeSchema;
386
444
  };
387
445
  }
388
446
  }
@@ -411,15 +469,15 @@ class StrategySchemaService {
411
469
  provide(TYPES.executionContextService, () => new ExecutionContextService());
412
470
  }
413
471
  {
414
- provide(TYPES.candleConnectionService, () => new CandleConnectionService());
472
+ provide(TYPES.exchangeConnectionService, () => new ExchangeConnectionService());
415
473
  provide(TYPES.strategyConnectionService, () => new StrategyConnectionService());
416
474
  }
417
475
  {
418
- provide(TYPES.candleSchemaService, () => new CandleSchemaService());
476
+ provide(TYPES.exchangeSchemaService, () => new ExchangeSchemaService());
419
477
  provide(TYPES.strategySchemaService, () => new StrategySchemaService());
420
478
  }
421
479
  {
422
- provide(TYPES.candlePublicService, () => new CandlePublicService());
480
+ provide(TYPES.exchangePublicService, () => new ExchangePublicService());
423
481
  provide(TYPES.strategyPublicService, () => new StrategyPublicService());
424
482
  }
425
483
 
@@ -430,15 +488,15 @@ const contextServices = {
430
488
  executionContextService: inject(TYPES.executionContextService),
431
489
  };
432
490
  const connectionServices = {
433
- candleConnectionService: inject(TYPES.candleConnectionService),
491
+ exchangeConnectionService: inject(TYPES.exchangeConnectionService),
434
492
  strategyConnectionService: inject(TYPES.strategyConnectionService),
435
493
  };
436
494
  const schemaServices = {
437
- candleSchemaService: inject(TYPES.candleSchemaService),
495
+ exchangeSchemaService: inject(TYPES.exchangeSchemaService),
438
496
  strategySchemaService: inject(TYPES.strategySchemaService),
439
497
  };
440
498
  const publicServices = {
441
- candlePublicService: inject(TYPES.candlePublicService),
499
+ exchangePublicService: inject(TYPES.exchangePublicService),
442
500
  strategyPublicService: inject(TYPES.strategyPublicService),
443
501
  };
444
502
  const backtest = {
@@ -453,8 +511,8 @@ init();
453
511
  function addStrategy(strategySchema) {
454
512
  backtest.strategySchemaService.addSchema(strategySchema);
455
513
  }
456
- function addCandle(candleSchema) {
457
- backtest.candleSchemaService.addSchema(candleSchema);
514
+ function addExchange(exchangeSchema) {
515
+ backtest.exchangeSchemaService.addSchema(exchangeSchema);
458
516
  }
459
517
 
460
518
  async function runBacktest(symbol, timeframes) {
@@ -571,10 +629,16 @@ function stopAll() {
571
629
  }
572
630
 
573
631
  async function getCandles(symbol, interval, limit) {
574
- return await backtest.candleConnectionService.getCandles(symbol, interval, limit);
632
+ return await backtest.exchangeConnectionService.getCandles(symbol, interval, limit);
575
633
  }
576
634
  async function getAveragePrice(symbol) {
577
- return await backtest.candleConnectionService.getAveragePrice(symbol);
635
+ return await backtest.exchangeConnectionService.getAveragePrice(symbol);
636
+ }
637
+ async function formatPrice(symbol, price) {
638
+ return await backtest.exchangeConnectionService.formatPrice(symbol, price);
639
+ }
640
+ async function formatQuantity(symbol, quantity) {
641
+ return await backtest.exchangeConnectionService.formatQuantity(symbol, quantity);
578
642
  }
579
643
 
580
- export { ExecutionContextService, addCandle, addStrategy, backtest, getAveragePrice, getCandles, reduce, runBacktest, runBacktestGUI, startRun, stopAll, stopRun };
644
+ export { ExecutionContextService, addExchange, addStrategy, backtest, formatPrice, formatQuantity, getAveragePrice, getCandles, reduce, runBacktest, runBacktestGUI, startRun, stopAll, stopRun };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "backtest-kit",
3
- "version": "1.0.2",
3
+ "version": "1.0.3",
4
4
  "description": "A TypeScript library for trading system backtest",
5
5
  "author": {
6
6
  "name": "Petr Tripolsky",
package/types.d.ts CHANGED
@@ -46,19 +46,23 @@ interface ICandleData {
46
46
  close: number;
47
47
  volume: number;
48
48
  }
49
- interface ICandleParams extends ICandleSchema {
49
+ interface IExchangeParams extends IExchangeSchema {
50
50
  logger: ILogger;
51
51
  execution: TExecutionContextService;
52
52
  }
53
- interface ICandleCallbacks {
53
+ interface IExchangeCallbacks {
54
54
  onCandleData: (symbol: string, interval: CandleInterval, since: Date, limit: number, data: ICandleData[]) => void;
55
55
  }
56
- interface ICandleSchema {
56
+ interface IExchangeSchema {
57
57
  getCandles: (symbol: string, interval: CandleInterval, since: Date, limit: number) => Promise<ICandleData[]>;
58
- callbacks?: Partial<ICandleCallbacks>;
58
+ formatQuantity: (symbol: string, quantity: number) => Promise<string>;
59
+ formatPrice: (symbol: string, price: number) => Promise<string>;
60
+ callbacks?: Partial<IExchangeCallbacks>;
59
61
  }
60
- interface ICandle {
62
+ interface IExchange {
61
63
  getCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
64
+ formatQuantity: (symbol: string, quantity: number) => Promise<string>;
65
+ formatPrice: (symbol: string, price: number) => Promise<string>;
62
66
  getAveragePrice: (symbol: string) => Promise<number>;
63
67
  }
64
68
 
@@ -112,7 +116,7 @@ interface IStrategy {
112
116
  }
113
117
 
114
118
  declare function addStrategy(strategySchema: IStrategySchema): void;
115
- declare function addCandle(candleSchema: ICandleSchema): void;
119
+ declare function addExchange(exchangeSchema: IExchangeSchema): void;
116
120
 
117
121
  interface IBacktestResult {
118
122
  symbol: string;
@@ -139,6 +143,8 @@ declare function stopAll(): void;
139
143
 
140
144
  declare function getCandles(symbol: string, interval: CandleInterval, limit: number): Promise<ICandleData[]>;
141
145
  declare function getAveragePrice(symbol: string): Promise<number>;
146
+ declare function formatPrice(symbol: string, price: number): Promise<string>;
147
+ declare function formatQuantity(symbol: string, quantity: number): Promise<string>;
142
148
 
143
149
  declare class LoggerService implements ILogger {
144
150
  private _commonLogger;
@@ -148,27 +154,31 @@ declare class LoggerService implements ILogger {
148
154
  setLogger: (logger: ILogger) => void;
149
155
  }
150
156
 
151
- declare class ClientCandle implements ICandle {
152
- readonly params: ICandleParams;
153
- constructor(params: ICandleParams);
157
+ declare class ClientExchange implements IExchange {
158
+ readonly params: IExchangeParams;
159
+ constructor(params: IExchangeParams);
154
160
  getCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
155
161
  getAveragePrice: (symbol: string) => Promise<number>;
162
+ formatQuantity: (symbol: string, quantity: number) => Promise<string>;
163
+ formatPrice: (symbol: string, price: number) => Promise<string>;
156
164
  }
157
165
 
158
- declare class CandleConnectionService implements ICandle {
166
+ declare class ExchangeConnectionService implements IExchange {
159
167
  private readonly loggerService;
160
168
  private readonly executionContextService;
161
- private readonly candleSchemaService;
162
- getCandle: ((symbol: string) => ClientCandle) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientCandle>;
169
+ private readonly exchangeSchemaService;
170
+ getExchange: ((symbol: string) => ClientExchange) & functools_kit.IClearableMemoize<string> & functools_kit.IControlMemoize<string, ClientExchange>;
163
171
  getCandles: (symbol: string, interval: CandleInterval, limit: number) => Promise<ICandleData[]>;
164
172
  getAveragePrice: (symbol: string) => Promise<number>;
173
+ formatPrice: (symbol: string, price: number) => Promise<string>;
174
+ formatQuantity: (symbol: string, quantity: number) => Promise<string>;
165
175
  }
166
176
 
167
- declare class CandleSchemaService {
177
+ declare class ExchangeSchemaService {
168
178
  private readonly loggerService;
169
- private _candleSchema;
170
- getSchema: () => ICandleSchema;
171
- addSchema: (candleSchema: ICandleSchema) => void;
179
+ private _exchangeSchema;
180
+ getSchema: () => IExchangeSchema;
181
+ addSchema: (exchangeSchema: IExchangeSchema) => void;
172
182
  }
173
183
 
174
184
  declare class StrategySchemaService {
@@ -182,16 +192,18 @@ declare class StrategyConnectionService implements IStrategy {
182
192
  private readonly loggerService;
183
193
  private readonly executionContextService;
184
194
  private readonly strategySchemaService;
185
- private readonly candleConnectionService;
195
+ private readonly exchangeConnectionService;
186
196
  private getStrategy;
187
197
  tick: (symbol: string) => Promise<IStrategyTickResult>;
188
198
  }
189
199
 
190
- declare class CandlePublicService {
200
+ declare class ExchangePublicService {
191
201
  private readonly loggerService;
192
- private readonly candleConnectionService;
202
+ private readonly exchangeConnectionService;
193
203
  getCandles: (symbol: string, interval: CandleInterval, limit: number, when: Date, backtest: boolean) => Promise<ICandleData[]>;
194
204
  getAveragePrice: (symbol: string, when: Date, backtest: boolean) => Promise<number>;
205
+ formatPrice: (symbol: string, price: number, when: Date, backtest: boolean) => Promise<string>;
206
+ formatQuantity: (symbol: string, quantity: number, when: Date, backtest: boolean) => Promise<string>;
195
207
  }
196
208
 
197
209
  declare class StrategyPublicService {
@@ -201,11 +213,11 @@ declare class StrategyPublicService {
201
213
  }
202
214
 
203
215
  declare const backtest: {
204
- candlePublicService: CandlePublicService;
216
+ exchangePublicService: ExchangePublicService;
205
217
  strategyPublicService: StrategyPublicService;
206
- candleSchemaService: CandleSchemaService;
218
+ exchangeSchemaService: ExchangeSchemaService;
207
219
  strategySchemaService: StrategySchemaService;
208
- candleConnectionService: CandleConnectionService;
220
+ exchangeConnectionService: ExchangeConnectionService;
209
221
  strategyConnectionService: StrategyConnectionService;
210
222
  executionContextService: {
211
223
  readonly context: IExecutionContext;
@@ -213,4 +225,4 @@ declare const backtest: {
213
225
  loggerService: LoggerService;
214
226
  };
215
227
 
216
- export { type CandleInterval, ExecutionContextService, type ICandleData, type ICandleSchema, type ISignalData, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, addCandle, addStrategy, backtest, getAveragePrice, getCandles, reduce, runBacktest, runBacktestGUI, startRun, stopAll, stopRun };
228
+ export { type CandleInterval, ExecutionContextService, type ICandleData, type IExchangeSchema, type ISignalData, type IStrategyPnL, type IStrategySchema, type IStrategyTickResult, type IStrategyTickResultActive, type IStrategyTickResultClosed, type IStrategyTickResultIdle, type IStrategyTickResultOpened, addExchange, addStrategy, backtest, formatPrice, formatQuantity, getAveragePrice, getCandles, reduce, runBacktest, runBacktestGUI, startRun, stopAll, stopRun };