@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 +57 -86
- package/package.json +8 -3
- package/src/index.ts +21 -34
- package/template/app.ts +19 -0
- package/template/binance/use-binance.ts +7 -0
- package/template/binance/watch-agg-trade.live.ts +26 -0
- package/template/binance/watch-agg-trade.replay.ts +50 -0
- package/template/binance/watch-agg-trade.ts +8 -0
- package/template/pipeline.ts +0 -26
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) =>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
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
|
|
93
|
-
|
|
94
|
-
|
|
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.
|
|
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
|
|
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
|
|
42
|
-
start: 'qf paper
|
|
43
|
-
replay: 'qf replay
|
|
44
|
-
pull: 'qf pull
|
|
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', ['
|
|
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
|
|
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
|
|
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
|
-
|
|
88
|
+
|
|
89
|
+
await cp(`${__dirname}/../template`, './src', { recursive: true });
|
|
103
90
|
}
|
package/template/app.ts
ADDED
|
@@ -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,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
|
+
}
|
package/template/pipeline.ts
DELETED
|
@@ -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
|
-
});
|