@quantform/create 0.7.26 → 0.7.28
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 +1 -1
- package/package.json +1 -1
- package/src/index.ts +1 -1
- package/template/app.ts +14 -8
- package/template/binance/use-binance.ts +2 -2
- package/template/binance/{watch-agg-trade.live.ts → watch-trade.live.ts} +4 -3
- package/template/binance/watch-trade.replay.ts +48 -0
- package/template/binance/watch-trade.ts +13 -0
- package/template/binance/watch-agg-trade.replay.ts +0 -50
- package/template/binance/watch-agg-trade.ts +0 -8
package/lib/index.js
CHANGED
package/package.json
CHANGED
package/src/index.ts
CHANGED
package/template/app.ts
CHANGED
|
@@ -5,15 +5,21 @@ import { sqlite } from '@quantform/sqlite';
|
|
|
5
5
|
|
|
6
6
|
import { useBinance } from './binance/use-binance';
|
|
7
7
|
|
|
8
|
-
export function
|
|
9
|
-
const {
|
|
10
|
-
const { info } = useLogger('
|
|
8
|
+
export function triangularArbitrageInefficiency() {
|
|
9
|
+
const { watchTrade } = useBinance();
|
|
10
|
+
const { info } = useLogger('arbitrage');
|
|
11
11
|
|
|
12
|
-
return combineLatest([
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
)
|
|
12
|
+
return combineLatest([
|
|
13
|
+
watchTrade('btcusdc'),
|
|
14
|
+
watchTrade('ethusdc'),
|
|
15
|
+
watchTrade('ethbtc')
|
|
16
|
+
]).pipe(
|
|
17
|
+
tap(([btcusdc, ethusdc, ethbtc]) => {
|
|
18
|
+
const spread = ethusdc.div(btcusdc).sub(ethbtc).abs();
|
|
19
|
+
|
|
20
|
+
info(`spread: ${spread.toFixed(6)}`);
|
|
21
|
+
})
|
|
16
22
|
);
|
|
17
23
|
}
|
|
18
24
|
|
|
19
|
-
export default app().use(sqlite()).start(
|
|
25
|
+
export default app().use(sqlite()).start(triangularArbitrageInefficiency);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { map } from 'rxjs';
|
|
1
|
+
import { map, retry } from 'rxjs';
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
|
|
4
4
|
import { d, useSocket } from '@quantform/core';
|
|
@@ -11,12 +11,13 @@ const schema = z.object({
|
|
|
11
11
|
})
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
export function
|
|
14
|
+
export function watchTradeLive(symbol: string) {
|
|
15
15
|
const { watch } = useSocket(
|
|
16
|
-
`wss://fstream.binance.com/stream?streams=${symbol.toLowerCase()}@
|
|
16
|
+
`wss://fstream.binance.com/stream?streams=${symbol.toLowerCase()}@trade`
|
|
17
17
|
);
|
|
18
18
|
|
|
19
19
|
return watch().pipe(
|
|
20
|
+
retry(),
|
|
20
21
|
map(({ timestamp, payload }) => {
|
|
21
22
|
const { data } = schema.parse(payload);
|
|
22
23
|
|
|
@@ -0,0 +1,48 @@
|
|
|
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({ 1: z.string(), 2: z.string() });
|
|
10
|
+
const DAY_NS = convert(us(86_400_000_000n), 'us', 'ns');
|
|
11
|
+
|
|
12
|
+
export function watchTradeReplay(symbol: string) {
|
|
13
|
+
const { watch } = useReplayStorage(uri(`binance://trade`, { symbol }), {
|
|
14
|
+
sync: async (query, storage) => {
|
|
15
|
+
const { min, max } = query.where.timestamp;
|
|
16
|
+
|
|
17
|
+
for (let time = min; time <= max; time = add(time, DAY_NS)) {
|
|
18
|
+
const date = new Date(Number(convert(time, 'ns', 'ms')));
|
|
19
|
+
|
|
20
|
+
for await (const payload of queryTradeHistory(date, symbol)) {
|
|
21
|
+
const timestamp = convert(us(BigInt(payload[4])), 'us', 'ns');
|
|
22
|
+
|
|
23
|
+
await storage.save([{ timestamp, payload }]);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
return watch().pipe(
|
|
30
|
+
map(({ timestamp, payload }) => {
|
|
31
|
+
const { 1: price, 2: quantity } = schema.parse(payload);
|
|
32
|
+
|
|
33
|
+
return { timestamp, payload: { price: d(price), size: d(quantity) } };
|
|
34
|
+
})
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
async function* queryTradeHistory(date: Date, symbol: string) {
|
|
39
|
+
const yyyyMMdd = date.toISOString().slice(0, 10);
|
|
40
|
+
|
|
41
|
+
const response = await fetch(
|
|
42
|
+
`https://data.binance.vision/data/spot/daily/trades/${symbol.toUpperCase()}/${symbol.toUpperCase()}-trades-${yyyyMMdd}.zip`
|
|
43
|
+
);
|
|
44
|
+
|
|
45
|
+
yield* Readable.fromWeb(response.body as any)
|
|
46
|
+
.pipe(unzipper.ParseOne(/\.csv$/))
|
|
47
|
+
.pipe(csv({ headers: false }));
|
|
48
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { map } from 'rxjs';
|
|
2
|
+
|
|
3
|
+
import { useReplay } from '@quantform/core';
|
|
4
|
+
|
|
5
|
+
import { watchTradeLive } from './watch-trade.live';
|
|
6
|
+
import { watchTradeReplay } from './watch-trade.replay';
|
|
7
|
+
|
|
8
|
+
export function watchTrade(symbol: string) {
|
|
9
|
+
return useReplay(
|
|
10
|
+
watchTradeReplay,
|
|
11
|
+
watchTradeLive
|
|
12
|
+
)(symbol).pipe(map(it => it.payload.price));
|
|
13
|
+
}
|
|
@@ -1,50 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,8 +0,0 @@
|
|
|
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
|
-
}
|