tradelab 0.1.0 → 0.1.2
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 +3 -25
- package/package.json +10 -7
- package/scripts/import-csv.js +0 -69
- package/scripts/prefetch.js +0 -52
package/README.md
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
+
<img src="https://i.imgur.com/HGvvQbq.png" width="500" alt="tradelab logo"/>
|
|
2
|
+
|
|
1
3
|
# tradelab
|
|
2
4
|
|
|
3
5
|
`tradelab` is a candle-based backtesting toolkit for Node.js. It is built for two use cases:
|
|
4
|
-
|
|
5
6
|
- you already have candles and want a solid execution/backtest engine
|
|
6
7
|
- you want to fetch Yahoo Finance data or import CSVs and backtest with minimal setup
|
|
7
8
|
|
|
8
|
-
The package stays focused on historical research
|
|
9
|
+
The package stays focused on historical research and testing, and is not trying to be a broker adapter or a live trading framework.
|
|
9
10
|
|
|
10
11
|
## Features
|
|
11
12
|
|
|
@@ -200,29 +201,6 @@ node examples/emaCross.js
|
|
|
200
201
|
node examples/yahooEmaCross.js SPY 1d 1y
|
|
201
202
|
```
|
|
202
203
|
|
|
203
|
-
## Local Scripts
|
|
204
|
-
|
|
205
|
-
```bash
|
|
206
|
-
npm run prefetch -- --symbol SPY --interval 1d --period 1y
|
|
207
|
-
npm run import-csv -- ./data/sample.csv --symbol BTC-USD --interval 5m
|
|
208
|
-
npm test
|
|
209
|
-
```
|
|
210
|
-
|
|
211
|
-
## Publishing
|
|
212
|
-
|
|
213
|
-
Validate the package contents before publishing:
|
|
214
|
-
|
|
215
|
-
```bash
|
|
216
|
-
npm test
|
|
217
|
-
npm pack --dry-run
|
|
218
|
-
```
|
|
219
|
-
|
|
220
|
-
Publish when the dry run looks correct:
|
|
221
|
-
|
|
222
|
-
```bash
|
|
223
|
-
npm publish
|
|
224
|
-
```
|
|
225
|
-
|
|
226
204
|
## Notes
|
|
227
205
|
|
|
228
206
|
- Yahoo downloads can be cached under `output/data` by default.
|
package/package.json
CHANGED
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tradelab",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Reusable trading and backtesting engine for candle-based strategies",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
7
7
|
"license": "MIT",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/ishsharm0/tradelab.git"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/ishsharm0/tradelab#readme",
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/ishsharm0/tradelab/issues"
|
|
15
|
+
},
|
|
8
16
|
"engines": {
|
|
9
17
|
"node": ">=18"
|
|
10
18
|
},
|
|
@@ -18,16 +26,11 @@
|
|
|
18
26
|
"src",
|
|
19
27
|
"templates",
|
|
20
28
|
"examples/*.js",
|
|
21
|
-
"scripts",
|
|
22
29
|
"README.md",
|
|
23
30
|
"LICENSE"
|
|
24
31
|
],
|
|
25
32
|
"scripts": {
|
|
26
|
-
"test": "node --test"
|
|
27
|
-
"prefetch": "node scripts/prefetch.js",
|
|
28
|
-
"import-csv": "node scripts/import-csv.js",
|
|
29
|
-
"example:ema": "node examples/emaCross.js",
|
|
30
|
-
"example:yahoo": "node examples/yahooEmaCross.js"
|
|
33
|
+
"test": "node --test"
|
|
31
34
|
},
|
|
32
35
|
"keywords": [
|
|
33
36
|
"trading",
|
package/scripts/import-csv.js
DELETED
|
@@ -1,69 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import {
|
|
4
|
-
candleStats,
|
|
5
|
-
loadCandlesFromCSV,
|
|
6
|
-
saveCandlesToCache,
|
|
7
|
-
} from "../src/index.js";
|
|
8
|
-
|
|
9
|
-
function parseArgs(argv) {
|
|
10
|
-
const args = {};
|
|
11
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
12
|
-
const token = argv[index];
|
|
13
|
-
if (!token.startsWith("--")) {
|
|
14
|
-
if (!args.file) args.file = token;
|
|
15
|
-
continue;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
const key = token.slice(2);
|
|
19
|
-
const next = argv[index + 1];
|
|
20
|
-
if (next && !next.startsWith("--")) {
|
|
21
|
-
args[key] = next;
|
|
22
|
-
index += 1;
|
|
23
|
-
} else {
|
|
24
|
-
args[key] = "true";
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
return args;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const args = parseArgs(process.argv.slice(2));
|
|
31
|
-
|
|
32
|
-
if (!args.file || !args.symbol) {
|
|
33
|
-
console.log(
|
|
34
|
-
"Usage: node scripts/import-csv.js <file.csv> --symbol BTC-USD [--interval 5m] [--period 90d]"
|
|
35
|
-
);
|
|
36
|
-
process.exit(1);
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
try {
|
|
40
|
-
const candles = loadCandlesFromCSV(args.file, {
|
|
41
|
-
delimiter: args.delimiter || ",",
|
|
42
|
-
timeCol: args.timeCol || "time",
|
|
43
|
-
openCol: args.openCol || "open",
|
|
44
|
-
highCol: args.highCol || "high",
|
|
45
|
-
lowCol: args.lowCol || "low",
|
|
46
|
-
closeCol: args.closeCol || "close",
|
|
47
|
-
volumeCol: args.volumeCol || "volume",
|
|
48
|
-
startDate: args.startDate,
|
|
49
|
-
endDate: args.endDate,
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
const stats = candleStats(candles);
|
|
53
|
-
const interval = args.interval || "1d";
|
|
54
|
-
const period = args.period || `${Math.max(1, Math.ceil(stats?.durationDays || 1))}d`;
|
|
55
|
-
const outputPath = saveCandlesToCache(candles, {
|
|
56
|
-
symbol: args.symbol,
|
|
57
|
-
interval,
|
|
58
|
-
period,
|
|
59
|
-
outDir: args.outDir || "output/data",
|
|
60
|
-
source: "csv",
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
console.log(`Loaded ${stats?.count ?? 0} candles`);
|
|
64
|
-
console.log(`Range: ${stats?.firstTime ?? "—"} -> ${stats?.lastTime ?? "—"}`);
|
|
65
|
-
console.log(`Saved cache: ${outputPath}`);
|
|
66
|
-
} catch (error) {
|
|
67
|
-
console.error(error.message);
|
|
68
|
-
process.exit(1);
|
|
69
|
-
}
|
package/scripts/prefetch.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import { getHistoricalCandles, saveCandlesToCache } from "../src/index.js";
|
|
4
|
-
|
|
5
|
-
function parseArgs(argv) {
|
|
6
|
-
const args = {};
|
|
7
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
8
|
-
const token = argv[index];
|
|
9
|
-
if (!token.startsWith("--")) continue;
|
|
10
|
-
|
|
11
|
-
const key = token.slice(2);
|
|
12
|
-
const next = argv[index + 1];
|
|
13
|
-
if (next && !next.startsWith("--")) {
|
|
14
|
-
args[key] = next;
|
|
15
|
-
index += 1;
|
|
16
|
-
} else {
|
|
17
|
-
args[key] = "true";
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
return args;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const args = parseArgs(process.argv.slice(2));
|
|
24
|
-
const symbol = args.symbol || "SPY";
|
|
25
|
-
const interval = args.interval || "1d";
|
|
26
|
-
const period = args.period || "1y";
|
|
27
|
-
const outDir = args.outDir || "output/data";
|
|
28
|
-
|
|
29
|
-
async function main() {
|
|
30
|
-
const candles = await getHistoricalCandles({
|
|
31
|
-
source: "yahoo",
|
|
32
|
-
symbol,
|
|
33
|
-
interval,
|
|
34
|
-
period,
|
|
35
|
-
cache: false,
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
const outputPath = saveCandlesToCache(candles, {
|
|
39
|
-
symbol,
|
|
40
|
-
interval,
|
|
41
|
-
period,
|
|
42
|
-
outDir,
|
|
43
|
-
source: "yahoo",
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
console.log(`Saved ${candles.length} candles to ${outputPath}`);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
main().catch((error) => {
|
|
50
|
-
console.error(error.message);
|
|
51
|
-
process.exit(1);
|
|
52
|
-
});
|