@quantform/core 0.7.12 → 0.7.17

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 (56) hide show
  1. package/lib/backtest/index.d.ts +7 -0
  2. package/lib/backtest/index.d.ts.map +1 -0
  3. package/lib/backtest/index.js +22 -0
  4. package/lib/backtest/use-backtest-options.d.ts +15 -0
  5. package/lib/backtest/use-backtest-options.d.ts.map +1 -0
  6. package/lib/backtest/use-backtest-options.js +20 -0
  7. package/lib/backtest/use-backtest-query-buffer.d.ts +15 -0
  8. package/lib/backtest/use-backtest-query-buffer.d.ts.map +1 -0
  9. package/lib/backtest/use-backtest-query-buffer.js +52 -0
  10. package/lib/backtest/use-backtest-query-cursor.d.ts +30 -0
  11. package/lib/backtest/use-backtest-query-cursor.d.ts.map +1 -0
  12. package/lib/backtest/use-backtest-query-cursor.js +43 -0
  13. package/lib/backtest/use-backtest-scheduler.d.ts +28 -0
  14. package/lib/backtest/use-backtest-scheduler.d.ts.map +1 -0
  15. package/lib/backtest/use-backtest-scheduler.js +77 -0
  16. package/lib/backtest/use-backtest-storage.d.ts +20 -0
  17. package/lib/backtest/use-backtest-storage.d.ts.map +1 -0
  18. package/lib/backtest/use-backtest-storage.js +63 -0
  19. package/lib/backtest/use-backtest-sync.d.ts +3 -0
  20. package/lib/backtest/use-backtest-sync.d.ts.map +1 -0
  21. package/lib/backtest/use-backtest-sync.js +16 -0
  22. package/lib/backtest/use-backtest.d.ts +23 -0
  23. package/lib/backtest/use-backtest.d.ts.map +1 -0
  24. package/lib/backtest/use-backtest.js +14 -0
  25. package/lib/backtest/when-backtest-finished.d.ts +2 -0
  26. package/lib/backtest/when-backtest-finished.d.ts.map +1 -0
  27. package/lib/backtest/when-backtest-finished.js +15 -0
  28. package/lib/cli/index.js +0 -9
  29. package/lib/cli/replay.js +1 -1
  30. package/lib/index.d.ts +2 -0
  31. package/lib/index.d.ts.map +1 -1
  32. package/lib/index.js +2 -0
  33. package/lib/uri.d.ts +11 -0
  34. package/lib/uri.d.ts.map +1 -0
  35. package/lib/uri.js +15 -0
  36. package/lib/use-timestamp.d.ts.map +1 -1
  37. package/lib/use-timestamp.js +2 -2
  38. package/package.json +1 -1
  39. package/src/backtest/index.ts +6 -0
  40. package/src/backtest/use-backtest-options.ts +23 -0
  41. package/src/backtest/use-backtest-query-buffer.ts +50 -0
  42. package/src/backtest/use-backtest-query-cursor.ts +40 -0
  43. package/src/backtest/use-backtest-scheduler.ts +98 -0
  44. package/src/backtest/use-backtest-storage.ts +79 -0
  45. package/src/backtest/use-backtest-sync.ts +19 -0
  46. package/src/backtest/use-backtest.ts +26 -0
  47. package/src/backtest/when-backtest-finished.ts +20 -0
  48. package/src/cli/index.ts +0 -10
  49. package/src/cli/replay.ts +1 -1
  50. package/src/index.ts +2 -0
  51. package/src/uri.ts +22 -0
  52. package/src/use-timestamp.ts +3 -2
  53. package/lib/cli/pull.d.ts +0 -2
  54. package/lib/cli/pull.d.ts.map +0 -1
  55. package/lib/cli/pull.js +0 -28
  56. package/src/cli/pull.ts +0 -15
@@ -0,0 +1,7 @@
1
+ export * from './use-backtest';
2
+ export * from './use-backtest-options';
3
+ export * from './use-backtest-scheduler';
4
+ export * from './use-backtest-storage';
5
+ export * from './use-backtest-sync';
6
+ export * from './when-backtest-finished';
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/backtest/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,wBAAwB,CAAC;AACvC,cAAc,0BAA0B,CAAC;AACzC,cAAc,wBAAwB,CAAC;AACvC,cAAc,qBAAqB,CAAC;AACpC,cAAc,0BAA0B,CAAC"}
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./use-backtest"), exports);
18
+ __exportStar(require("./use-backtest-options"), exports);
19
+ __exportStar(require("./use-backtest-scheduler"), exports);
20
+ __exportStar(require("./use-backtest-storage"), exports);
21
+ __exportStar(require("./use-backtest-sync"), exports);
22
+ __exportStar(require("./when-backtest-finished"), exports);
@@ -0,0 +1,15 @@
1
+ import { Dependency } from '../module';
2
+ type BacktestOptions = {
3
+ from: number;
4
+ to: number;
5
+ };
6
+ /**
7
+ *
8
+ */
9
+ export declare function backtestOptions(options: BacktestOptions): Dependency;
10
+ /**
11
+ * Will return current backtest execution options.
12
+ */
13
+ export declare const useBacktestOptions: () => BacktestOptions;
14
+ export {};
15
+ //# sourceMappingURL=use-backtest-options.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-backtest-options.d.ts","sourceRoot":"","sources":["../../src/backtest/use-backtest-options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAc,MAAM,aAAa,CAAC;AAIrD,KAAK,eAAe,GAAG;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF;;GAEG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,UAAU,CAKpE;AAED;;GAEG;AACH,eAAO,MAAM,kBAAkB,uBAAoD,CAAC"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useBacktestOptions = exports.backtestOptions = void 0;
4
+ const module_1 = require("../module");
5
+ const injectionToken = Symbol('backtest-options');
6
+ /**
7
+ *
8
+ */
9
+ function backtestOptions(options) {
10
+ return {
11
+ provide: injectionToken,
12
+ useValue: options
13
+ };
14
+ }
15
+ exports.backtestOptions = backtestOptions;
16
+ /**
17
+ * Will return current backtest execution options.
18
+ */
19
+ const useBacktestOptions = () => (0, module_1.useContext)(injectionToken);
20
+ exports.useBacktestOptions = useBacktestOptions;
@@ -0,0 +1,15 @@
1
+ import { BacktestStorage } from './use-backtest';
2
+ export declare const useBacktestQueryBuffer: <T>(storage: BacktestStorage<T>) => {
3
+ size(): number;
4
+ peek(): {
5
+ timestamp: number;
6
+ payload: T;
7
+ };
8
+ dequeue(): {
9
+ timestamp: number;
10
+ payload: T;
11
+ };
12
+ completed(): boolean;
13
+ fetchNextPage(): Promise<void>;
14
+ };
15
+ //# sourceMappingURL=use-backtest-query-buffer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-backtest-query-buffer.d.ts","sourceRoot":"","sources":["../../src/backtest/use-backtest-query-buffer.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,eAAO,MAAM,sBAAsB;;;mBAGJ,MAAM;;;;mBAAN,MAAM;;;;;CAwCnC,CAAC"}
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.useBacktestQueryBuffer = void 0;
13
+ const replay_1 = require("../replay");
14
+ const storage_1 = require("../storage");
15
+ const with_memo_1 = require("../with-memo");
16
+ exports.useBacktestQueryBuffer = (0, with_memo_1.withMemo)((storage) => {
17
+ const { from, to } = (0, replay_1.useReplayOptions)();
18
+ let page = [];
19
+ let index = 0;
20
+ let completed = false;
21
+ let count = 0;
22
+ return {
23
+ size() {
24
+ return page.length - index;
25
+ },
26
+ peek() {
27
+ return page[index];
28
+ },
29
+ dequeue() {
30
+ return page[index++];
31
+ },
32
+ completed() {
33
+ return completed;
34
+ },
35
+ fetchNextPage() {
36
+ return __awaiter(this, void 0, void 0, function* () {
37
+ if (completed) {
38
+ return;
39
+ }
40
+ index = 0;
41
+ page = yield storage.query({
42
+ where: { timestamp: (0, storage_1.between)(from, to) },
43
+ limit: 10000,
44
+ offset: count,
45
+ orderBy: 'ASC'
46
+ });
47
+ count += page.length;
48
+ completed = page.length === 0;
49
+ });
50
+ }
51
+ };
52
+ });
@@ -0,0 +1,30 @@
1
+ import { BacktestStorage } from './use-backtest';
2
+ export declare const useBacktestQueryCursor: () => {
3
+ get<T>(query: BacktestStorage<T>): {
4
+ size(): number;
5
+ peek(): {
6
+ timestamp: number;
7
+ payload: T;
8
+ };
9
+ dequeue(): {
10
+ timestamp: number;
11
+ payload: T;
12
+ };
13
+ completed(): boolean;
14
+ fetchNextPage(): Promise<void>;
15
+ };
16
+ cursor(): Promise<{
17
+ size(): number;
18
+ peek(): {
19
+ timestamp: number;
20
+ payload: any;
21
+ };
22
+ dequeue(): {
23
+ timestamp: number;
24
+ payload: any;
25
+ };
26
+ completed(): boolean;
27
+ fetchNextPage(): Promise<void>;
28
+ } | undefined>;
29
+ };
30
+ //# sourceMappingURL=use-backtest-query-cursor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-backtest-query-cursor.d.ts","sourceRoot":"","sources":["../../src/backtest/use-backtest-query-cursor.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAGjD,eAAO,MAAM,sBAAsB;;;;;;;;;;;;;;;;;;;;;;;;;;;CAkCjC,CAAC"}
@@ -0,0 +1,43 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.useBacktestQueryCursor = void 0;
13
+ const with_memo_1 = require("../with-memo");
14
+ const use_backtest_query_buffer_1 = require("./use-backtest-query-buffer");
15
+ exports.useBacktestQueryCursor = (0, with_memo_1.withMemo)(() => {
16
+ const cursors = Array.of();
17
+ return {
18
+ get(query) {
19
+ const buffer = (0, use_backtest_query_buffer_1.useBacktestQueryBuffer)(query);
20
+ cursors.push(buffer);
21
+ return buffer;
22
+ },
23
+ cursor() {
24
+ return __awaiter(this, void 0, void 0, function* () {
25
+ let current;
26
+ for (const cursor of cursors) {
27
+ if (cursor.completed()) {
28
+ continue;
29
+ }
30
+ if (cursor.size() == 0) {
31
+ yield cursor.fetchNextPage();
32
+ }
33
+ if (cursor.peek()) {
34
+ if (!current || current.peek().timestamp > cursor.peek().timestamp) {
35
+ current = cursor;
36
+ }
37
+ }
38
+ }
39
+ return current;
40
+ });
41
+ }
42
+ };
43
+ });
@@ -0,0 +1,28 @@
1
+ import { Observable } from 'rxjs';
2
+ import { BacktestStorage } from './use-backtest';
3
+ export declare const useBacktestScheduler: () => {
4
+ stream: Observable<[{
5
+ size(): number;
6
+ peek(): {
7
+ timestamp: number;
8
+ payload: any;
9
+ };
10
+ dequeue(): {
11
+ timestamp: number;
12
+ payload: any;
13
+ };
14
+ completed(): boolean;
15
+ fetchNextPage(): Promise<void>;
16
+ }, {
17
+ timestamp: number;
18
+ payload: any;
19
+ }]>;
20
+ timestamp(): number;
21
+ stop(): void;
22
+ tryContinue: () => void;
23
+ watch<T>(query: BacktestStorage<T>): Observable<{
24
+ timestamp: number;
25
+ payload: T;
26
+ }>;
27
+ };
28
+ //# sourceMappingURL=use-backtest-scheduler.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-backtest-scheduler.d.ts","sourceRoot":"","sources":["../../src/backtest/use-backtest-scheduler.ts"],"names":[],"mappings":"AAAA,OAAO,EAAsB,UAAU,EAAW,MAAM,MAAM,CAAC;AAI/D,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAKjD,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;mBASiC,MAAM;iBAAW,GAAG;;;;;;mBA+DrB,MAAM;;;CAgBrE,CAAC"}
@@ -0,0 +1,77 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.useBacktestScheduler = void 0;
13
+ const rxjs_1 = require("rxjs");
14
+ const with_memo_1 = require("../with-memo");
15
+ const use_backtest_options_1 = require("./use-backtest-options");
16
+ const use_backtest_query_cursor_1 = require("./use-backtest-query-cursor");
17
+ exports.useBacktestScheduler = (0, with_memo_1.withMemo)(() => {
18
+ const { from } = (0, use_backtest_options_1.useBacktestOptions)();
19
+ const { get, cursor } = (0, use_backtest_query_cursor_1.useBacktestQueryCursor)();
20
+ let timestamp = from;
21
+ let stopAcquire = 1;
22
+ let processing = false;
23
+ const stream$ = new rxjs_1.Subject();
24
+ const processNext = () => __awaiter(void 0, void 0, void 0, function* () {
25
+ const storage = yield cursor();
26
+ if (!storage || !storage.peek()) {
27
+ stream$.complete();
28
+ return false;
29
+ }
30
+ const sample = storage.dequeue();
31
+ timestamp = sample.timestamp;
32
+ stream$.next([storage, sample]);
33
+ return true;
34
+ });
35
+ const next = () => __awaiter(void 0, void 0, void 0, function* () {
36
+ if (processing) {
37
+ return;
38
+ }
39
+ processing = true;
40
+ while (stopAcquire === 0) {
41
+ if (!(yield processNext())) {
42
+ break;
43
+ }
44
+ yield new Promise(it => setImmediate(it));
45
+ }
46
+ processing = false;
47
+ });
48
+ const tryContinue = () => {
49
+ if (stopAcquire === 0) {
50
+ return;
51
+ }
52
+ stopAcquire--;
53
+ if (stopAcquire === 0) {
54
+ next();
55
+ }
56
+ };
57
+ return {
58
+ stream: stream$.asObservable(),
59
+ timestamp() {
60
+ return timestamp;
61
+ },
62
+ stop() {
63
+ stopAcquire++;
64
+ },
65
+ tryContinue,
66
+ watch(query) {
67
+ const storage = get(query);
68
+ return (0, rxjs_1.defer)(() => {
69
+ tryContinue();
70
+ return stream$.pipe((0, rxjs_1.filter)(([cur]) => cur === storage), (0, rxjs_1.map)(([, it]) => ({
71
+ timestamp: it.timestamp,
72
+ payload: it.payload
73
+ })));
74
+ });
75
+ }
76
+ };
77
+ });
@@ -0,0 +1,20 @@
1
+ import { InferQueryObject, Query, QueryObject, QueryObjectType } from '../storage';
2
+ import { Uri } from '../uri';
3
+ import { BacktestStorage } from './use-backtest';
4
+ export type BacktestStorageQuery<V> = {
5
+ sync: <T extends QueryObjectType<K>, K extends QueryObject>(query: Query<InferQueryObject<T>> & {
6
+ where: {
7
+ timestamp: {
8
+ min: number;
9
+ max: number;
10
+ };
11
+ };
12
+ }, storage: {
13
+ save: (objects: {
14
+ timestamp: number;
15
+ payload: V;
16
+ }[]) => Promise<void>;
17
+ }) => Promise<void>;
18
+ };
19
+ export declare function useBacktestStorage<V, P extends Record<string, string | number>>(uri: Uri<P>, { sync }: BacktestStorageQuery<V>): BacktestStorage<V>;
20
+ //# sourceMappingURL=use-backtest-storage.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-backtest-storage.d.ts","sourceRoot":"","sources":["../../src/backtest/use-backtest-storage.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,gBAAgB,EAChB,KAAK,EACL,WAAW,EACX,eAAe,EAGhB,MAAM,cAAc,CAAC;AACtB,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAE/B,OAAO,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAEjD,MAAM,MAAM,oBAAoB,CAAC,CAAC,IAAI;IACpC,IAAI,EAAE,CAAC,CAAC,SAAS,eAAe,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,WAAW,EACxD,KAAK,EAAE,KAAK,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,GAAG;QAClC,KAAK,EAAE;YAAE,SAAS,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAC;gBAAC,GAAG,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAC;KACpD,EACD,OAAO,EAAE;QAAE,IAAI,EAAE,CAAC,OAAO,EAAE;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,OAAO,EAAE,CAAC,CAAA;SAAE,EAAE,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;KAAE,KAC/E,OAAO,CAAC,IAAI,CAAC,CAAC;CACpB,CAAC;AASF,wBAAgB,kBAAkB,CAAC,CAAC,EAAE,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAC7E,GAAG,EAAE,GAAG,CAAC,CAAC,CAAC,EACX,EAAE,IAAI,EAAE,EAAE,oBAAoB,CAAC,CAAC,CAAC,GAChC,eAAe,CAAC,CAAC,CAAC,CA8CpB"}
@@ -0,0 +1,63 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.useBacktestStorage = void 0;
13
+ const storage_1 = require("../storage");
14
+ const storageIndexObject = storage_1.Storage.createObject('index://range', {
15
+ timestamp: 'number',
16
+ uri: 'string',
17
+ min: 'number',
18
+ max: 'number'
19
+ });
20
+ function useBacktestStorage(uri, { sync }) {
21
+ const storage = (0, storage_1.useStorage)(['backtest']);
22
+ const storageObjectKey = uri.query;
23
+ const storageObject = storage_1.Storage.createObject(storageObjectKey, {
24
+ timestamp: 'number',
25
+ payload: 'string'
26
+ });
27
+ return {
28
+ query(query) {
29
+ return __awaiter(this, void 0, void 0, function* () {
30
+ const [index] = yield storage.query(storageIndexObject, {
31
+ limit: 1,
32
+ where: { uri: (0, storage_1.eq)(storageObjectKey) }
33
+ });
34
+ const { min, max } = query.where.timestamp;
35
+ if (!index || min < index.min || max > index.max) {
36
+ yield sync(query, {
37
+ save(objects) {
38
+ return __awaiter(this, void 0, void 0, function* () {
39
+ yield storage.save(storageObject, objects.map(it => ({
40
+ timestamp: it.timestamp,
41
+ payload: JSON.stringify(it.payload)
42
+ })));
43
+ });
44
+ }
45
+ });
46
+ yield storage.save(storageIndexObject, [
47
+ {
48
+ timestamp: 0,
49
+ max,
50
+ min,
51
+ uri: storageObjectKey
52
+ }
53
+ ]);
54
+ }
55
+ return (yield storage.query(storageObject, query)).map(it => ({
56
+ timestamp: it.timestamp,
57
+ payload: JSON.parse(it.payload)
58
+ }));
59
+ });
60
+ }
61
+ };
62
+ }
63
+ exports.useBacktestStorage = useBacktestStorage;
@@ -0,0 +1,3 @@
1
+ import { Observable } from 'rxjs';
2
+ export declare function useBacktestSync<T>(input: Observable<T>): Observable<T>;
3
+ //# sourceMappingURL=use-backtest-sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-backtest-sync.d.ts","sourceRoot":"","sources":["../../src/backtest/use-backtest-sync.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,UAAU,EAAE,MAAM,MAAM,CAAC;AAM5C,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,UAAU,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAYtE"}
@@ -0,0 +1,16 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useBacktestSync = void 0;
4
+ const rxjs_1 = require("rxjs");
5
+ const use_execution_mode_1 = require("../use-execution-mode");
6
+ const use_backtest_scheduler_1 = require("./use-backtest-scheduler");
7
+ function useBacktestSync(input) {
8
+ const { isReplay } = (0, use_execution_mode_1.useExecutionMode)();
9
+ if (!isReplay) {
10
+ return input;
11
+ }
12
+ const { stop, tryContinue } = (0, use_backtest_scheduler_1.useBacktestScheduler)();
13
+ stop();
14
+ return input.pipe((0, rxjs_1.finalize)(() => tryContinue()));
15
+ }
16
+ exports.useBacktestSync = useBacktestSync;
@@ -0,0 +1,23 @@
1
+ import { Observable } from 'rxjs';
2
+ import { Query, QueryObject } from '../storage';
3
+ export interface BacktestStorage<V> {
4
+ query(query: Query<QueryObject> & {
5
+ where: {
6
+ timestamp: {
7
+ min: number;
8
+ max: number;
9
+ };
10
+ };
11
+ }): Promise<{
12
+ timestamp: number;
13
+ payload: V;
14
+ }[]>;
15
+ }
16
+ export declare function useBacktest<T>(input: Observable<{
17
+ timestamp: number;
18
+ payload: T;
19
+ }>, query: BacktestStorage<T>): Observable<{
20
+ timestamp: number;
21
+ payload: T;
22
+ }>;
23
+ //# sourceMappingURL=use-backtest.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-backtest.d.ts","sourceRoot":"","sources":["../../src/backtest/use-backtest.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,MAAM,CAAC;AAElC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAKlD,MAAM,WAAW,eAAe,CAAC,CAAC;IAChC,KAAK,CACH,KAAK,EAAE,KAAK,CAAC,WAAW,CAAC,GAAG;QAAE,KAAK,EAAE;YAAE,SAAS,EAAE;gBAAE,GAAG,EAAE,MAAM,CAAC;gBAAC,GAAG,EAAE,MAAM,CAAA;aAAE,CAAA;SAAE,CAAA;KAAE,GACjF,OAAO,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC,CAAA;KAAE,EAAE,CAAC,CAAC;CACjD;AAED,wBAAgB,WAAW,CAAC,CAAC,EAC3B,KAAK,EAAE,UAAU,CAAC;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,CAAC,CAAA;CAAE,CAAC,EACpD,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC;eADM,MAAM;aAAW,CAAC;GAWlD"}
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useBacktest = void 0;
4
+ const use_execution_mode_1 = require("../use-execution-mode");
5
+ const use_backtest_scheduler_1 = require("./use-backtest-scheduler");
6
+ function useBacktest(input, query) {
7
+ const { isReplay } = (0, use_execution_mode_1.useExecutionMode)();
8
+ if (isReplay) {
9
+ const { watch } = (0, use_backtest_scheduler_1.useBacktestScheduler)();
10
+ return watch(query);
11
+ }
12
+ return input;
13
+ }
14
+ exports.useBacktest = useBacktest;
@@ -0,0 +1,2 @@
1
+ export declare function whenBacktestFinished(): import("rxjs").Observable<boolean>;
2
+ //# sourceMappingURL=when-backtest-finished.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"when-backtest-finished.d.ts","sourceRoot":"","sources":["../../src/backtest/when-backtest-finished.ts"],"names":[],"mappings":"AAMA,wBAAgB,oBAAoB,uCAanC"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.whenBacktestFinished = void 0;
4
+ const rxjs_1 = require("rxjs");
5
+ const use_execution_mode_1 = require("../use-execution-mode");
6
+ const use_backtest_scheduler_1 = require("./use-backtest-scheduler");
7
+ function whenBacktestFinished() {
8
+ const { isReplay } = (0, use_execution_mode_1.useExecutionMode)();
9
+ if (!isReplay) {
10
+ return new rxjs_1.Subject().asObservable();
11
+ }
12
+ const { stream } = (0, use_backtest_scheduler_1.useBacktestScheduler)();
13
+ return (0, rxjs_1.from)(stream).pipe((0, rxjs_1.last)(), (0, rxjs_1.map)(() => true));
14
+ }
15
+ exports.whenBacktestFinished = whenBacktestFinished;
package/lib/cli/index.js CHANGED
@@ -19,7 +19,6 @@ const node_watch_1 = __importDefault(require("node-watch"));
19
19
  const build_1 = __importDefault(require("../cli/build"));
20
20
  const live_1 = __importDefault(require("../cli/live"));
21
21
  const paper_1 = __importDefault(require("../cli/paper"));
22
- const pull_1 = __importDefault(require("../cli/pull"));
23
22
  const replay_1 = __importDefault(require("../cli/replay"));
24
23
  commander_1.program
25
24
  .command('build')
@@ -52,14 +51,6 @@ commander_1.program
52
51
  .option('-t, --to <to>', 'date to')
53
52
  .option('-w', 'watch mode')
54
53
  .action(replay_1.default);
55
- commander_1.program
56
- .command('pull')
57
- .description('pulls instrument historical data to storage')
58
- .argument('<name>', 'strategy to execute')
59
- .argument('<instrument>', 'instrument to import')
60
- .option('-f, --from <from>', 'date from')
61
- .option('-t, --to <to>', 'date to')
62
- .action(pull_1.default);
63
54
  commander_1.program.name('quantform').description('quantform tools');
64
55
  if (process.argv.length < 3) {
65
56
  commander_1.program.help();
package/lib/cli/replay.js CHANGED
@@ -24,7 +24,7 @@ function default_1(name, options) {
24
24
  return;
25
25
  }
26
26
  const from = options.from ? new Date(options.from).getTime() : 0;
27
- const to = options.to ? new Date(options.to).getTime() : Number.MAX_VALUE;
27
+ const to = options.to ? new Date(options.to).getTime() : 1893452400; // 01-01-2030;
28
28
  const script = new script_1.Script(name, [
29
29
  session_1.useSession.options({ id: (_a = options.id) !== null && _a !== void 0 ? _a : Date.now().toString() }),
30
30
  (0, replay_1.replayOptions)({ from, to }),
package/lib/index.d.ts CHANGED
@@ -18,4 +18,6 @@ export * from './with-memo';
18
18
  export * from './session';
19
19
  export * from './strategy';
20
20
  export * from './operators';
21
+ export * from './backtest';
22
+ export * from './uri';
21
23
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,aAAa,CAAC;AAC5B,cAAc,cAAc,CAAC;AAC7B,cAAc,aAAa,CAAC;AAC5B,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,uBAAuB,CAAC;AACtC,cAAc,WAAW,CAAC;AAC1B,cAAc,yBAAyB,CAAC;AACxC,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,aAAa,CAAC;AAC5B,cAAc,iBAAiB,CAAC;AAChC,cAAc,kBAAkB,CAAC;AACjC,cAAc,mBAAmB,CAAC;AAClC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,cAAc,CAAC;AAC7B,cAAc,eAAe,CAAC;AAC9B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,eAAe,CAAC;AAC9B,cAAc,UAAU,CAAC"}
package/lib/index.js CHANGED
@@ -34,3 +34,5 @@ __exportStar(require("./with-memo"), exports);
34
34
  __exportStar(require("./session"), exports);
35
35
  __exportStar(require("./strategy"), exports);
36
36
  __exportStar(require("./operators"), exports);
37
+ __exportStar(require("./backtest"), exports);
38
+ __exportStar(require("./uri"), exports);
package/lib/uri.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ export type Uri<T> = {
2
+ base: `${string}://${string}`;
3
+ params: T;
4
+ query: string;
5
+ };
6
+ export declare function uri<T extends Record<string, string | number>>(base: `${string}://${string}`, params: T): {
7
+ base: `${string}://${string}`;
8
+ params: T;
9
+ query: string;
10
+ };
11
+ //# sourceMappingURL=uri.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"uri.d.ts","sourceRoot":"","sources":["../src/uri.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,GAAG,CAAC,CAAC,IAAI;IACnB,IAAI,EAAE,GAAG,MAAM,MAAM,MAAM,EAAE,CAAC;IAC9B,MAAM,EAAE,CAAC,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,wBAAgB,GAAG,CAAC,CAAC,SAAS,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC,EAC3D,IAAI,EAAE,GAAG,MAAM,MAAM,MAAM,EAAE,EAC7B,MAAM,EAAE,CAAC;;;;EAaV"}
package/lib/uri.js ADDED
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.uri = void 0;
4
+ function uri(base, params) {
5
+ const search = new URLSearchParams();
6
+ for (const [k, v] of Object.entries(params)) {
7
+ search.set(k, String(v));
8
+ }
9
+ return {
10
+ base,
11
+ params,
12
+ query: `${base}?${search.toString()}`
13
+ };
14
+ }
15
+ exports.uri = uri;
@@ -1 +1 @@
1
- {"version":3,"file":"use-timestamp.d.ts","sourceRoot":"","sources":["../src/use-timestamp.ts"],"names":[],"mappings":"AAGA,wBAAgB,YAAY,WAQ3B"}
1
+ {"version":3,"file":"use-timestamp.d.ts","sourceRoot":"","sources":["../src/use-timestamp.ts"],"names":[],"mappings":"AAIA,wBAAgB,YAAY,WAQ3B"}
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.useTimestamp = void 0;
4
- const replay_1 = require("./replay");
5
4
  const use_execution_mode_1 = require("./use-execution-mode");
5
+ const backtest_1 = require("./backtest");
6
6
  function useTimestamp() {
7
7
  const { isReplay } = (0, use_execution_mode_1.useExecutionMode)();
8
8
  if (isReplay) {
9
- return (0, replay_1.useReplayManager)().timestamp();
9
+ return (0, backtest_1.useBacktestScheduler)().timestamp();
10
10
  }
11
11
  return Date.now();
12
12
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantform/core",
3
- "version": "0.7.12",
3
+ "version": "0.7.17",
4
4
  "license": "MIT",
5
5
  "author": "Mateusz Majchrzak",
6
6
  "description": "Node.js library for building systematic trading strategies in reactive way.",
@@ -0,0 +1,6 @@
1
+ export * from './use-backtest';
2
+ export * from './use-backtest-options';
3
+ export * from './use-backtest-scheduler';
4
+ export * from './use-backtest-storage';
5
+ export * from './use-backtest-sync';
6
+ export * from './when-backtest-finished';
@@ -0,0 +1,23 @@
1
+ import { Dependency, useContext } from '@lib/module';
2
+
3
+ const injectionToken = Symbol('backtest-options');
4
+
5
+ type BacktestOptions = {
6
+ from: number;
7
+ to: number;
8
+ };
9
+
10
+ /**
11
+ *
12
+ */
13
+ export function backtestOptions(options: BacktestOptions): Dependency {
14
+ return {
15
+ provide: injectionToken,
16
+ useValue: options
17
+ };
18
+ }
19
+
20
+ /**
21
+ * Will return current backtest execution options.
22
+ */
23
+ export const useBacktestOptions = () => useContext<BacktestOptions>(injectionToken);
@@ -0,0 +1,50 @@
1
+ import { useReplayOptions } from '@lib/replay';
2
+ import { between } from '@lib/storage';
3
+ import { withMemo } from '@lib/with-memo';
4
+
5
+ import { BacktestStorage } from './use-backtest';
6
+
7
+ export const useBacktestQueryBuffer = withMemo(<T>(storage: BacktestStorage<T>) => {
8
+ const { from, to } = useReplayOptions();
9
+
10
+ let page: Array<{ timestamp: number; payload: T }> = [];
11
+ let index = 0;
12
+ let completed = false;
13
+ let count = 0;
14
+
15
+ return {
16
+ size() {
17
+ return page.length - index;
18
+ },
19
+
20
+ peek() {
21
+ return page[index];
22
+ },
23
+
24
+ dequeue() {
25
+ return page[index++];
26
+ },
27
+
28
+ completed() {
29
+ return completed;
30
+ },
31
+
32
+ async fetchNextPage() {
33
+ if (completed) {
34
+ return;
35
+ }
36
+
37
+ index = 0;
38
+
39
+ page = await storage.query({
40
+ where: { timestamp: between(from, to) },
41
+ limit: 10000,
42
+ offset: count,
43
+ orderBy: 'ASC'
44
+ });
45
+
46
+ count += page.length;
47
+ completed = page.length === 0;
48
+ }
49
+ };
50
+ });
@@ -0,0 +1,40 @@
1
+ import { withMemo } from '@lib/with-memo';
2
+
3
+ import { BacktestStorage } from './use-backtest';
4
+ import { useBacktestQueryBuffer } from './use-backtest-query-buffer';
5
+
6
+ export const useBacktestQueryCursor = withMemo(() => {
7
+ const cursors = Array.of<ReturnType<typeof useBacktestQueryBuffer<any>>>();
8
+
9
+ return {
10
+ get<T>(query: BacktestStorage<T>) {
11
+ const buffer = useBacktestQueryBuffer<T>(query);
12
+
13
+ cursors.push(buffer);
14
+
15
+ return buffer;
16
+ },
17
+
18
+ async cursor() {
19
+ let current: ReturnType<typeof useBacktestQueryBuffer<any>> | undefined;
20
+
21
+ for (const cursor of cursors) {
22
+ if (cursor.completed()) {
23
+ continue;
24
+ }
25
+
26
+ if (cursor.size() == 0) {
27
+ await cursor.fetchNextPage();
28
+ }
29
+
30
+ if (cursor.peek()) {
31
+ if (!current || current.peek().timestamp > cursor.peek().timestamp) {
32
+ current = cursor;
33
+ }
34
+ }
35
+ }
36
+
37
+ return current;
38
+ }
39
+ };
40
+ });
@@ -0,0 +1,98 @@
1
+ import { defer, filter, map, Observable, Subject } from 'rxjs';
2
+
3
+ import { withMemo } from '@lib/with-memo';
4
+
5
+ import { BacktestStorage } from './use-backtest';
6
+ import { useBacktestOptions } from './use-backtest-options';
7
+ import { useBacktestQueryBuffer } from './use-backtest-query-buffer';
8
+ import { useBacktestQueryCursor } from './use-backtest-query-cursor';
9
+
10
+ export const useBacktestScheduler = withMemo(() => {
11
+ const { from } = useBacktestOptions();
12
+ const { get, cursor } = useBacktestQueryCursor();
13
+
14
+ let timestamp = from;
15
+ let stopAcquire = 1;
16
+ let processing = false;
17
+
18
+ const stream$ = new Subject<
19
+ [ReturnType<typeof useBacktestQueryBuffer<any>>, { timestamp: number; payload: any }]
20
+ >();
21
+
22
+ const processNext = async () => {
23
+ const storage = await cursor();
24
+
25
+ if (!storage || !storage.peek()) {
26
+ stream$.complete();
27
+ return false;
28
+ }
29
+
30
+ const sample = storage.dequeue();
31
+
32
+ timestamp = sample.timestamp;
33
+
34
+ stream$.next([storage, sample]);
35
+
36
+ return true;
37
+ };
38
+
39
+ const next = async () => {
40
+ if (processing) {
41
+ return;
42
+ }
43
+
44
+ processing = true;
45
+
46
+ while (stopAcquire === 0) {
47
+ if (!(await processNext())) {
48
+ break;
49
+ }
50
+
51
+ await new Promise(it => setImmediate(it));
52
+ }
53
+
54
+ processing = false;
55
+ };
56
+
57
+ const tryContinue = () => {
58
+ if (stopAcquire === 0) {
59
+ return;
60
+ }
61
+
62
+ stopAcquire--;
63
+
64
+ if (stopAcquire === 0) {
65
+ next();
66
+ }
67
+ };
68
+
69
+ return {
70
+ stream: stream$.asObservable(),
71
+
72
+ timestamp() {
73
+ return timestamp;
74
+ },
75
+
76
+ stop() {
77
+ stopAcquire++;
78
+ },
79
+
80
+ tryContinue,
81
+
82
+ watch<T>(query: BacktestStorage<T>): Observable<{ timestamp: number; payload: T }> {
83
+ const storage = get<T>(query);
84
+
85
+ return defer(() => {
86
+ tryContinue();
87
+
88
+ return stream$.pipe(
89
+ filter(([cur]) => cur === storage),
90
+ map(([, it]) => ({
91
+ timestamp: it.timestamp,
92
+ payload: it.payload as T
93
+ }))
94
+ );
95
+ });
96
+ }
97
+ };
98
+ });
@@ -0,0 +1,79 @@
1
+ import {
2
+ eq,
3
+ InferQueryObject,
4
+ Query,
5
+ QueryObject,
6
+ QueryObjectType,
7
+ Storage,
8
+ useStorage
9
+ } from '@lib/storage';
10
+ import { Uri } from '@lib/uri';
11
+
12
+ import { BacktestStorage } from './use-backtest';
13
+
14
+ export type BacktestStorageQuery<V> = {
15
+ sync: <T extends QueryObjectType<K>, K extends QueryObject>(
16
+ query: Query<InferQueryObject<T>> & {
17
+ where: { timestamp: { min: number; max: number } };
18
+ },
19
+ storage: { save: (objects: { timestamp: number; payload: V }[]) => Promise<void> }
20
+ ) => Promise<void>;
21
+ };
22
+
23
+ const storageIndexObject = Storage.createObject('index://range', {
24
+ timestamp: 'number',
25
+ uri: 'string',
26
+ min: 'number',
27
+ max: 'number'
28
+ });
29
+
30
+ export function useBacktestStorage<V, P extends Record<string, string | number>>(
31
+ uri: Uri<P>,
32
+ { sync }: BacktestStorageQuery<V>
33
+ ): BacktestStorage<V> {
34
+ const storage = useStorage(['backtest']);
35
+ const storageObjectKey = uri.query;
36
+ const storageObject = Storage.createObject(storageObjectKey, {
37
+ timestamp: 'number',
38
+ payload: 'string'
39
+ });
40
+
41
+ return {
42
+ async query(query) {
43
+ const [index] = await storage.query(storageIndexObject, {
44
+ limit: 1,
45
+ where: { uri: eq(storageObjectKey) }
46
+ });
47
+
48
+ const { min, max } = query.where.timestamp;
49
+
50
+ if (!index || min < index.min || max > index.max) {
51
+ await sync(query, {
52
+ async save(objects) {
53
+ await storage.save(
54
+ storageObject,
55
+ objects.map(it => ({
56
+ timestamp: it.timestamp,
57
+ payload: JSON.stringify(it.payload)
58
+ }))
59
+ );
60
+ }
61
+ });
62
+
63
+ await storage.save(storageIndexObject, [
64
+ {
65
+ timestamp: 0,
66
+ max,
67
+ min,
68
+ uri: storageObjectKey
69
+ }
70
+ ]);
71
+ }
72
+
73
+ return (await storage.query(storageObject, query)).map(it => ({
74
+ timestamp: it.timestamp,
75
+ payload: JSON.parse(it.payload) as V
76
+ }));
77
+ }
78
+ };
79
+ }
@@ -0,0 +1,19 @@
1
+ import { finalize, Observable } from 'rxjs';
2
+
3
+ import { useExecutionMode } from '@lib/use-execution-mode';
4
+
5
+ import { useBacktestScheduler } from './use-backtest-scheduler';
6
+
7
+ export function useBacktestSync<T>(input: Observable<T>): Observable<T> {
8
+ const { isReplay } = useExecutionMode();
9
+
10
+ if (!isReplay) {
11
+ return input;
12
+ }
13
+
14
+ const { stop, tryContinue } = useBacktestScheduler();
15
+
16
+ stop();
17
+
18
+ return input.pipe(finalize(() => tryContinue()));
19
+ }
@@ -0,0 +1,26 @@
1
+ import { Observable } from 'rxjs';
2
+
3
+ import { Query, QueryObject } from '@lib/storage';
4
+ import { useExecutionMode } from '@lib/use-execution-mode';
5
+
6
+ import { useBacktestScheduler } from './use-backtest-scheduler';
7
+
8
+ export interface BacktestStorage<V> {
9
+ query(
10
+ query: Query<QueryObject> & { where: { timestamp: { min: number; max: number } } }
11
+ ): Promise<{ timestamp: number; payload: V }[]>;
12
+ }
13
+
14
+ export function useBacktest<T>(
15
+ input: Observable<{ timestamp: number; payload: T }>,
16
+ query: BacktestStorage<T>
17
+ ) {
18
+ const { isReplay } = useExecutionMode();
19
+
20
+ if (isReplay) {
21
+ const { watch } = useBacktestScheduler();
22
+ return watch<T>(query);
23
+ }
24
+
25
+ return input;
26
+ }
@@ -0,0 +1,20 @@
1
+ import { from, last, map, Subject } from 'rxjs';
2
+
3
+ import { useExecutionMode } from '@lib/use-execution-mode';
4
+
5
+ import { useBacktestScheduler } from './use-backtest-scheduler';
6
+
7
+ export function whenBacktestFinished() {
8
+ const { isReplay } = useExecutionMode();
9
+
10
+ if (!isReplay) {
11
+ return new Subject<boolean>().asObservable();
12
+ }
13
+
14
+ const { stream } = useBacktestScheduler();
15
+
16
+ return from(stream).pipe(
17
+ last(),
18
+ map(() => true)
19
+ );
20
+ }
package/src/cli/index.ts CHANGED
@@ -7,7 +7,6 @@ import watch from 'node-watch';
7
7
  import build from '@lib/cli/build';
8
8
  import live from '@lib/cli/live';
9
9
  import paper from '@lib/cli/paper';
10
- import pull from '@lib/cli/pull';
11
10
  import replay from '@lib/cli/replay';
12
11
 
13
12
  program
@@ -45,15 +44,6 @@ program
45
44
  .option('-w', 'watch mode')
46
45
  .action(replay);
47
46
 
48
- program
49
- .command('pull')
50
- .description('pulls instrument historical data to storage')
51
- .argument('<name>', 'strategy to execute')
52
- .argument('<instrument>', 'instrument to import')
53
- .option('-f, --from <from>', 'date from')
54
- .option('-t, --to <to>', 'date to')
55
- .action(pull);
56
-
57
47
  program.name('quantform').description('quantform tools');
58
48
 
59
49
  if (process.argv.length < 3) {
package/src/cli/replay.ts CHANGED
@@ -14,7 +14,7 @@ export default async function (
14
14
  }
15
15
 
16
16
  const from = options.from ? new Date(options.from).getTime() : 0;
17
- const to = options.to ? new Date(options.to).getTime() : Number.MAX_VALUE;
17
+ const to = options.to ? new Date(options.to).getTime() : 1893452400; // 01-01-2030;
18
18
 
19
19
  const script = new Script(name, [
20
20
  useSession.options({ id: options.id ?? Date.now().toString() }),
package/src/index.ts CHANGED
@@ -18,3 +18,5 @@ export * from '@lib/with-memo';
18
18
  export * from '@lib/session';
19
19
  export * from '@lib/strategy';
20
20
  export * from '@lib/operators';
21
+ export * from '@lib/backtest';
22
+ export * from '@lib/uri';
package/src/uri.ts ADDED
@@ -0,0 +1,22 @@
1
+ export type Uri<T> = {
2
+ base: `${string}://${string}`;
3
+ params: T;
4
+ query: string;
5
+ };
6
+
7
+ export function uri<T extends Record<string, string | number>>(
8
+ base: `${string}://${string}`,
9
+ params: T
10
+ ) {
11
+ const search = new URLSearchParams();
12
+
13
+ for (const [k, v] of Object.entries(params)) {
14
+ search.set(k, String(v));
15
+ }
16
+
17
+ return {
18
+ base,
19
+ params,
20
+ query: `${base}?${search.toString()}`
21
+ };
22
+ }
@@ -1,11 +1,12 @@
1
- import { useReplayManager } from '@lib/replay';
2
1
  import { useExecutionMode } from '@lib/use-execution-mode';
3
2
 
3
+ import { useBacktestScheduler } from './backtest';
4
+
4
5
  export function useTimestamp() {
5
6
  const { isReplay } = useExecutionMode();
6
7
 
7
8
  if (isReplay) {
8
- return useReplayManager().timestamp();
9
+ return useBacktestScheduler().timestamp();
9
10
  }
10
11
 
11
12
  return Date.now();
package/lib/cli/pull.d.ts DELETED
@@ -1,2 +0,0 @@
1
- export default function (name: string, instrument: string, options: any): Promise<void>;
2
- //# sourceMappingURL=pull.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pull.d.ts","sourceRoot":"","sources":["../../src/cli/pull.ts"],"names":[],"mappings":"AAKA,yBAA+B,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,iBAS5E"}
package/lib/cli/pull.js DELETED
@@ -1,28 +0,0 @@
1
- "use strict";
2
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
- return new (P || (P = Promise))(function (resolve, reject) {
5
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
- step((generator = generator.apply(thisArg, _arguments || [])).next());
9
- });
10
- };
11
- var __importDefault = (this && this.__importDefault) || function (mod) {
12
- return (mod && mod.__esModule) ? mod : { "default": mod };
13
- };
14
- Object.defineProperty(exports, "__esModule", { value: true });
15
- const build_1 = __importDefault(require("../cli/build"));
16
- const use_execution_mode_1 = require("../use-execution-mode");
17
- const script_1 = require("./internal/script");
18
- function default_1(name, instrument, options) {
19
- return __awaiter(this, void 0, void 0, function* () {
20
- if (yield (0, build_1.default)()) {
21
- return;
22
- }
23
- const script = new script_1.Script(name, [use_execution_mode_1.useExecutionMode.idleOptions()]);
24
- //const output = await script.run();
25
- //console.log(output);
26
- });
27
- }
28
- exports.default = default_1;
package/src/cli/pull.ts DELETED
@@ -1,15 +0,0 @@
1
- import build from '@lib/cli/build';
2
- import { useExecutionMode } from '@lib/use-execution-mode';
3
-
4
- import { Script } from './internal/script';
5
-
6
- export default async function (name: string, instrument: string, options: any) {
7
- if (await build()) {
8
- return;
9
- }
10
-
11
- const script = new Script(name, [useExecutionMode.idleOptions()]);
12
- //const output = await script.run();
13
-
14
- //console.log(output);
15
- }