@quantform/create 0.7.21 → 0.7.24

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/lib/index.js CHANGED
@@ -1,14 +1,5 @@
1
1
  #!/usr/bin/env node
2
2
  "use strict";
3
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
4
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
5
- return new (P || (P = Promise))(function (resolve, reject) {
6
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
7
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
8
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
9
- step((generator = generator.apply(thisArg, _arguments || [])).next());
10
- });
11
- };
12
3
  var __importDefault = (this && this.__importDefault) || function (mod) {
13
4
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
5
  };
@@ -17,93 +8,73 @@ const child_process_1 = require("child_process");
17
8
  const commander_1 = require("commander");
18
9
  const edit_json_file_1 = __importDefault(require("edit-json-file"));
19
10
  const fs_1 = require("fs");
11
+ const promises_1 = require("fs/promises");
12
+ const path_1 = require("path");
20
13
  const process_1 = require("process");
21
14
  const util_1 = require("util");
22
- const path_1 = require("path");
23
15
  const shell = (0, util_1.promisify)(child_process_1.exec);
24
16
  commander_1.program
25
17
  .name('quantform')
26
18
  .description('Setup the quantform project by running a single command.')
27
- .argument('<dir>', 'directory to initialize', './')
28
- .action((dir) => __awaiter(void 0, void 0, void 0, function* () {
29
- yield createDirectory(dir);
30
- yield addPackageJson();
31
- yield addDependencies();
32
- yield addTypescript();
33
- yield addSWCConfig();
34
- yield copyTemplateFiles();
35
- }))
19
+ .argument('<dir>', 'directory to initialize')
20
+ .action(async (dir) => {
21
+ await createDirectory(dir);
22
+ await addPackageJson();
23
+ await addDependencies();
24
+ await addTypescript();
25
+ await copyTemplateFiles();
26
+ })
36
27
  .parse(process.argv);
37
- function createDirectory(dir) {
38
- return __awaiter(this, void 0, void 0, function* () {
39
- if ((0, path_1.basename)(dir) != '' && (0, path_1.basename)(dir) != '.') {
40
- (0, fs_1.mkdirSync)(dir);
41
- (0, process_1.chdir)(dir);
42
- }
43
- });
28
+ async function createDirectory(dir) {
29
+ if ((0, path_1.basename)(dir) != '' && (0, path_1.basename)(dir) != '.') {
30
+ (0, fs_1.mkdirSync)(dir);
31
+ (0, process_1.chdir)(dir);
32
+ }
44
33
  }
45
- function addPackageJson() {
46
- return __awaiter(this, void 0, void 0, function* () {
47
- yield shell(`npm init --yes`);
48
- const config = (0, edit_json_file_1.default)(`./package.json`);
49
- config.set('main', 'pipeline.js');
50
- config.set('scripts', {
51
- live: 'qf live pipeline',
52
- start: 'qf paper pipeline',
53
- replay: 'qf replay pipeline',
54
- pull: 'qf pull pipeline'
55
- });
56
- config.save();
34
+ async function addPackageJson() {
35
+ await shell(`npm init --yes`);
36
+ const config = (0, edit_json_file_1.default)(`./package.json`);
37
+ config.set('scripts', {
38
+ live: 'qf live app',
39
+ start: 'qf paper app',
40
+ replay: 'qf replay app',
41
+ pull: 'qf pull app'
57
42
  });
43
+ config.save();
58
44
  }
59
- function addTypescript() {
60
- return __awaiter(this, void 0, void 0, function* () {
61
- const config = (0, edit_json_file_1.default)(`./tsconfig.json`);
62
- config.set('compilerOptions.module', 'commonjs');
63
- config.set('compilerOptions.declaration', true);
64
- config.set('compilerOptions.removeComments', true);
65
- config.set('compilerOptions.emitDecoratorMetadata', true);
66
- config.set('compilerOptions.experimentalDecorators', true);
67
- config.set('compilerOptions.allowSyntheticDefaultImports', true);
68
- config.set('compilerOptions.target', 'es2017');
69
- config.set('compilerOptions.outDir', './lib');
70
- config.set('compilerOptions.baseUrl', './');
71
- config.set('compilerOptions.incremental', true);
72
- config.set('include', ['*.ts', 'src/*']);
73
- config.set('exclude', ['node_modules', 'test', 'lib', '**/*spec.ts']);
74
- config.save();
75
- });
45
+ async function addTypescript() {
46
+ const config = (0, edit_json_file_1.default)(`./tsconfig.json`);
47
+ config.set('compilerOptions.module', 'commonjs');
48
+ config.set('compilerOptions.declaration', true);
49
+ config.set('compilerOptions.removeComments', true);
50
+ config.set('compilerOptions.emitDecoratorMetadata', true);
51
+ config.set('compilerOptions.experimentalDecorators', true);
52
+ config.set('compilerOptions.allowSyntheticDefaultImports', true);
53
+ config.set('compilerOptions.target', 'es2017');
54
+ config.set('compilerOptions.rootDir', 'src');
55
+ config.set('compilerOptions.outDir', './lib');
56
+ config.set('compilerOptions.baseUrl', './');
57
+ config.set('compilerOptions.incremental', true);
58
+ config.set('include', ['src/**/*']);
59
+ config.set('exclude', ['node_modules', 'test', 'lib', '**/*spec.ts']);
60
+ config.save();
76
61
  }
77
- function addSWCConfig() {
78
- return __awaiter(this, void 0, void 0, function* () {
79
- (0, fs_1.writeFileSync)('./.swcrc', '{}');
80
- const config = (0, edit_json_file_1.default)(`./.swcrc`);
81
- config.set('$schema', 'http://json.schemastore.org/swcrc');
82
- config.set('jsc.parser.syntax', 'typescript');
83
- config.set('jsc.parser.tsx', false);
84
- config.set('module.type', 'commonjs');
85
- config.set('module.strict', false);
86
- config.set('module.strictMode', true);
87
- config.set('module.lazy', false);
88
- config.set('module.noInterop', false);
89
- config.save();
90
- });
62
+ async function addDependencies() {
63
+ for (const dependency of ['typescript', '@types/node']) {
64
+ await shell(`npm add -D ${dependency}`);
65
+ }
66
+ for (const dependency of [
67
+ '@quantform/core',
68
+ '@quantform/sqlite',
69
+ 'rxjs',
70
+ 'zod',
71
+ 'csv-parser',
72
+ 'unzipper'
73
+ ]) {
74
+ await shell(`npm add ${dependency}`);
75
+ }
91
76
  }
92
- function addDependencies() {
93
- return __awaiter(this, void 0, void 0, function* () {
94
- const devDependencies = ['typescript', '@types/node', '@swc/core', 'zod'];
95
- const dependencies = ['@quantform/core@beta', '@quantform/binance@beta', 'rxjs'];
96
- for (const dependency of devDependencies) {
97
- yield shell(`npm add -D ${dependency}`);
98
- }
99
- for (const dependency of dependencies) {
100
- yield shell(`npm add ${dependency}`);
101
- }
102
- });
103
- }
104
- function copyTemplateFiles() {
105
- return __awaiter(this, void 0, void 0, function* () {
106
- (0, fs_1.mkdirSync)('./src');
107
- (0, fs_1.copyFileSync)(`${__dirname}/../template/pipeline.ts`, './src/pipeline.ts');
108
- });
77
+ async function copyTemplateFiles() {
78
+ (0, fs_1.mkdirSync)('./src');
79
+ await (0, promises_1.cp)(`${__dirname}/../template`, './src', { recursive: true });
109
80
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quantform/create",
3
- "version": "0.7.21",
3
+ "version": "0.7.24",
4
4
  "license": "MIT",
5
5
  "author": "Mateusz Majchrzak",
6
6
  "description": "Node.js library for building systematic trading strategies in reactive way.",
@@ -21,15 +21,20 @@
21
21
  },
22
22
  "main": "index.js",
23
23
  "directories": {
24
- "lib": "lib"
24
+ "lib": "lib",
25
+ "test": "test"
25
26
  },
26
27
  "keywords": [],
27
28
  "bugs": {
28
29
  "url": "https://github.com/quantform/quantform/issues"
29
30
  },
30
31
  "homepage": "https://github.com/quantform/quantform#readme",
32
+ "devDependencies": {
33
+ "typescript": "^5.0.2"
34
+ },
31
35
  "scripts": {
32
36
  "build": "tsc && tsc-alias",
33
- "test": "jest"
37
+ "test": "jest",
38
+ "create": "ts-node src/index.ts"
34
39
  }
35
40
  }
package/src/index.ts CHANGED
@@ -3,23 +3,23 @@
3
3
  import { exec } from 'child_process';
4
4
  import { program } from 'commander';
5
5
  import editJsonFile from 'edit-json-file';
6
- import { copyFileSync, mkdirSync, writeFileSync } from 'fs';
6
+ import { copyFileSync, mkdirSync } from 'fs';
7
+ import { cp } from 'fs/promises';
8
+ import { basename } from 'path';
7
9
  import { chdir } from 'process';
8
10
  import { promisify } from 'util';
9
- import { basename } from 'path';
10
11
 
11
12
  const shell = promisify(exec);
12
13
 
13
14
  program
14
15
  .name('quantform')
15
16
  .description('Setup the quantform project by running a single command.')
16
- .argument('<dir>', 'directory to initialize', './')
17
+ .argument('<dir>', 'directory to initialize')
17
18
  .action(async dir => {
18
19
  await createDirectory(dir);
19
20
  await addPackageJson();
20
21
  await addDependencies();
21
22
  await addTypescript();
22
- await addSWCConfig();
23
23
  await copyTemplateFiles();
24
24
  })
25
25
  .parse(process.argv);
@@ -36,12 +36,11 @@ async function addPackageJson() {
36
36
 
37
37
  const config = editJsonFile(`./package.json`);
38
38
 
39
- config.set('main', 'pipeline.js');
40
39
  config.set('scripts', {
41
- live: 'qf live pipeline',
42
- start: 'qf paper pipeline',
43
- replay: 'qf replay pipeline',
44
- pull: 'qf pull pipeline'
40
+ live: 'qf live app',
41
+ start: 'qf paper app',
42
+ replay: 'qf replay app',
43
+ pull: 'qf pull app'
45
44
  });
46
45
 
47
46
  config.save();
@@ -57,47 +56,35 @@ async function addTypescript() {
57
56
  config.set('compilerOptions.experimentalDecorators', true);
58
57
  config.set('compilerOptions.allowSyntheticDefaultImports', true);
59
58
  config.set('compilerOptions.target', 'es2017');
59
+ config.set('compilerOptions.rootDir', 'src');
60
60
  config.set('compilerOptions.outDir', './lib');
61
61
  config.set('compilerOptions.baseUrl', './');
62
62
  config.set('compilerOptions.incremental', true);
63
- config.set('include', ['*.ts', 'src/*']);
63
+ config.set('include', ['src/**/*']);
64
64
  config.set('exclude', ['node_modules', 'test', 'lib', '**/*spec.ts']);
65
65
 
66
66
  config.save();
67
67
  }
68
68
 
69
- async function addSWCConfig() {
70
- writeFileSync('./.swcrc', '{}');
71
-
72
- const config = editJsonFile(`./.swcrc`);
73
-
74
- config.set('$schema', 'http://json.schemastore.org/swcrc');
75
- config.set('jsc.parser.syntax', 'typescript');
76
- config.set('jsc.parser.tsx', false);
77
- config.set('module.type', 'commonjs');
78
- config.set('module.strict', false);
79
- config.set('module.strictMode', true);
80
- config.set('module.lazy', false);
81
- config.set('module.noInterop', false);
82
-
83
- config.save();
84
- }
85
-
86
69
  async function addDependencies() {
87
- const devDependencies = ['typescript', '@types/node', '@swc/core', 'zod'];
88
-
89
- const dependencies = ['@quantform/core@beta', '@quantform/binance@beta', 'rxjs'];
90
-
91
- for (const dependency of devDependencies) {
70
+ for (const dependency of ['typescript', '@types/node']) {
92
71
  await shell(`npm add -D ${dependency}`);
93
72
  }
94
73
 
95
- for (const dependency of dependencies) {
74
+ for (const dependency of [
75
+ '@quantform/core',
76
+ '@quantform/sqlite',
77
+ 'rxjs',
78
+ 'zod',
79
+ 'csv-parser',
80
+ 'unzipper'
81
+ ]) {
96
82
  await shell(`npm add ${dependency}`);
97
83
  }
98
84
  }
99
85
 
100
86
  async function copyTemplateFiles() {
101
87
  mkdirSync('./src');
102
- copyFileSync(`${__dirname}/../template/pipeline.ts`, './src/pipeline.ts');
88
+
89
+ await cp(`${__dirname}/../template`, './src', { recursive: true });
103
90
  }
@@ -0,0 +1,19 @@
1
+ import { combineLatest, tap } from 'rxjs';
2
+
3
+ import { app, useLogger } from '@quantform/core';
4
+ import { sqlite } from '@quantform/sqlite';
5
+
6
+ import { useBinance } from './binance/use-binance';
7
+
8
+ export function strategy() {
9
+ const { watchAggTrade } = useBinance();
10
+ const { info } = useLogger('usdc');
11
+
12
+ return combineLatest([watchAggTrade('dogeeur'), watchAggTrade('dogeusdt')]).pipe(
13
+ tap(([dogeeur, dogeusdt]) =>
14
+ info(`eur/usdt: ${dogeusdt.payload.price.div(dogeeur.payload.price).toFixed(4)}`)
15
+ )
16
+ );
17
+ }
18
+
19
+ export default app().use(sqlite()).start(strategy);
@@ -0,0 +1,7 @@
1
+ import { watchAggTrade } from './watch-agg-trade';
2
+
3
+ export function useBinance() {
4
+ return {
5
+ watchAggTrade
6
+ };
7
+ }
@@ -0,0 +1,26 @@
1
+ import { map } from 'rxjs';
2
+ import { z } from 'zod';
3
+
4
+ import { d, useSocket } from '@quantform/core';
5
+
6
+ const schema = z.object({
7
+ stream: z.string(),
8
+ data: z.object({
9
+ p: z.string(),
10
+ q: z.string()
11
+ })
12
+ });
13
+
14
+ export function watchAggTradeLive(symbol: string) {
15
+ const { watch } = useSocket(
16
+ `wss://fstream.binance.com/stream?streams=${symbol.toLowerCase()}@aggTrade`
17
+ );
18
+
19
+ return watch().pipe(
20
+ map(({ timestamp, payload }) => {
21
+ const { data } = schema.parse(payload);
22
+
23
+ return { timestamp, payload: { price: d(data.p), size: d(data.q) } };
24
+ })
25
+ );
26
+ }
@@ -0,0 +1,50 @@
1
+ import csv from 'csv-parser';
2
+ import { map } from 'rxjs';
3
+ import { Readable } from 'stream';
4
+ import unzipper from 'unzipper';
5
+ import { z } from 'zod';
6
+
7
+ import { add, convert, d, uri, us, useReplayStorage } from '@quantform/core';
8
+
9
+ const schema = z.object({
10
+ 1: z.string(),
11
+ 2: z.string()
12
+ });
13
+
14
+ const DAY_NS = convert(us(24 * 60 * 60 * 1000 * 1000 * 1000), 'us', 'ns');
15
+
16
+ export function watchAggTradeReplay(symbol: string) {
17
+ const { watch } = useReplayStorage(uri(`binance://aggTrade`, { symbol }), {
18
+ sync: async (query, storage) => {
19
+ let timestamp = query.where.timestamp.min;
20
+
21
+ while (timestamp <= query.where.timestamp.max) {
22
+ const date = new Date(Number(convert(timestamp, 'ns', 'ms')))
23
+ .toISOString()
24
+ .slice(0, 10);
25
+
26
+ const response = await fetch(
27
+ `https://data.binance.vision/data/spot/daily/aggTrades/${symbol.toUpperCase()}/${symbol.toUpperCase()}-aggTrades-${date}.zip`
28
+ );
29
+
30
+ for await (const row of Readable.fromWeb(response.body as any)
31
+ .pipe(unzipper.ParseOne(/\.csv$/))
32
+ .pipe(csv({ headers: false }))) {
33
+ await storage.save([
34
+ { timestamp: convert(us(BigInt(row[5])), 'us', 'ns'), payload: row }
35
+ ]);
36
+ }
37
+
38
+ timestamp = add(timestamp, DAY_NS);
39
+ }
40
+ }
41
+ });
42
+
43
+ return watch().pipe(
44
+ map(({ timestamp, payload }) => {
45
+ const { 1: price, 2: quantity } = schema.parse(payload);
46
+
47
+ return { timestamp, payload: { price: d(price), size: d(quantity) } };
48
+ })
49
+ );
50
+ }
@@ -0,0 +1,8 @@
1
+ import { useReplay } from '@quantform/core';
2
+
3
+ import { watchAggTradeLive } from './watch-agg-trade.live';
4
+ import { watchAggTradeReplay } from './watch-agg-trade.replay';
5
+
6
+ export function watchAggTrade(symbol: string) {
7
+ return useReplay(watchAggTradeReplay, watchAggTradeLive)(symbol);
8
+ }
@@ -1,26 +0,0 @@
1
- import { tap } from 'rxjs';
2
-
3
- import { binance, useBinance } from '@quantform/binance';
4
- import { behavior, Commission, instrumentOf, strategy, useLogger } from '@quantform/core';
5
-
6
- export default strategy(() => {
7
- behavior(() => {
8
- // watch binance spot orderbook
9
- const { whenOrderbookTicker } = useBinance();
10
- const { info } = useLogger('binance');
11
-
12
- // subscribe for btc-usdt binance spot market and print ticker values
13
- return whenOrderbookTicker(instrumentOf(`binance:btc-usdt`)).pipe(
14
- tap(({ bids, asks }) => info(`current top bid: ${bids.rate}, ask: ${asks.rate}`))
15
- );
16
- });
17
-
18
- return [
19
- ...binance({
20
- simulator: {
21
- balance: {},
22
- commission: Commission.Zero
23
- }
24
- })
25
- ];
26
- });