Trade
Introduction
trade is a JavaScript library that executes orders on data, thereby creates a backtest and returns
the resulting positions. Those can be used to generate trading reports.
Features
- 💾 Use any data source you like – CSV files, a database or a web service
- 🪙 Use almost any instruments you'd like to backtest – stocks, futures, crypto currencies
- ⏱ Use almost any interval on data you like – weekly, daily or even second by second
- 🔌 Simple to use interface – library consists of one main function
- 🧪 Tested library with with high test coverage (>95%)
- 📈 Returns results as a JSON structure for easy reportings and analysis
Documentation
See the API documentation.
Install
- Install through npm:
npm i -S backtest
- Requires Node 12 (for private class fields)
Example
import { trade } from 'trade';
const data = [
[
{ date: '2020-01-01', open: 20.5, close: 20.9, symbol: 'AAPL' },
{ date: '2020-01-01', open: 10.2, close: 10.1, symbol: 'AMZN' },
], [
{ date: '2020-01-02', open: 20.4, close: 20.7, symbol: 'AAPL' },
{ date: '2020-01-02', open: 10.3, close: 10.6, symbol: 'AMZN' },
], [
{ date: '2020-01-03', open: 20.3, close: 20.6, symbol: 'AAPL' },
{ date: '2020-01-03', open: 10.4, close: 10.5, symbol: 'AMZN' },
]
];
function* getData() {
for (const barData of data) {
const parsedData = barData.map(item => ({ ...item, date: new Date(item.date) }));
yield parsedData;
}
}
const createOrders = ({ data, positions, cash }) => {
const [current, previous] = data;
if (!previous) return [];
const expectedPositions = [];
for (const instrumentData of current) {
const { symbol } = instrumentData;
const previousInstrumentData = previous.find(item => item.symbol === symbol);
if (!previousInstrumentData) continue;
const direction = instrumentData.close > previousInstrumentData.close ? 1 : -1;
expectedPositions.push({ data: instrumentData, direction });
}
const available = cash + positions.reduce((prev, pos) => prev + pos.value, 0);
const moneyPerPosition = available / expectedPositions.length;
const orders = expectedPositions.map(position => ({
symbol: position.data.symbol,
size: Math.floor(moneyPerPosition / position.data.close) * position.direction,
}));
return orders;
};
const cash = 10 ** 4;
const result = await trade({
getData,
createOrders,
cash,
});
The result will be:
[
{
"date": "2020-01-01T00:00:00.000Z",
"orders": [],
"cash": 10000,
"cost": 0,
"positionsOnOpen": [],
"positionsAfterTrade": [],
"positionsOnClose": [],
"closedPositions": []
},
{
"date": "2020-01-02T00:00:00.000Z",
"orders": [
{
"symbol": "AAPL",
"size": -241
},
{
"symbol": "AMZN",
"size": 471
}
],
"cash": 10000,
"cost": 0,
"positionsOnOpen": [],
"positionsAfterTrade": [],
"positionsOnClose": [],
"closedPositions": []
},
{
"date": "2020-01-03T00:00:00.000Z",
"orders": [
{
"symbol": "AAPL",
"size": -242
},
{
"symbol": "AMZN",
"size": -474
}
],
"cash": 209.29999999999927,
"cost": 9790.7,
"positionsOnOpen": [],
"positionsAfterTrade": [
{
"date": "2020-01-03T00:00:00.000Z",
"symbol": "AAPL",
"type": "open",
"price": 20.3,
"exchangeRate": 1,
"size": -241,
"barsHeld": 0,
"id": 0,
"value": 4892.3,
"initialPosition": {
"date": "2020-01-03T00:00:00.000Z",
"symbol": "AAPL",
"type": "open",
"price": 20.3,
"exchangeRate": 1,
"size": -241,
"barsHeld": 0,
"id": 0,
"margin": 20.3,
"settleDifference": false,
"pointValue": 1,
"value": 4892.3
}
},
{
"date": "2020-01-03T00:00:00.000Z",
"symbol": "AMZN",
"type": "open",
"price": 10.4,
"exchangeRate": 1,
"size": 471,
"barsHeld": 0,
"id": 1,
"value": 4898.400000000001,
"initialPosition": {
"date": "2020-01-03T00:00:00.000Z",
"symbol": "AMZN",
"type": "open",
"price": 10.4,
"exchangeRate": 1,
"size": 471,
"barsHeld": 0,
"id": 1,
"margin": 10.4,
"settleDifference": false,
"pointValue": 1,
"value": 4898.400000000001
}
}
],
"positionsOnClose": [
{
"date": "2020-01-03T00:00:00.000Z",
"symbol": "AAPL",
"type": "close",
"price": 20.6,
"exchangeRate": 1,
"size": -241,
"barsHeld": 0,
"id": 0,
"value": 4820,
"initialPosition": {
"date": "2020-01-03T00:00:00.000Z",
"symbol": "AAPL",
"type": "open",
"price": 20.3,
"exchangeRate": 1,
"size": -241,
"barsHeld": 0,
"id": 0,
"margin": 20.3,
"settleDifference": false,
"pointValue": 1,
"value": 4892.3
}
},
{
"date": "2020-01-03T00:00:00.000Z",
"symbol": "AMZN",
"type": "close",
"price": 10.5,
"exchangeRate": 1,
"size": 471,
"barsHeld": 0,
"id": 1,
"value": 4945.5,
"initialPosition": {
"date": "2020-01-03T00:00:00.000Z",
"symbol": "AMZN",
"type": "open",
"price": 10.4,
"exchangeRate": 1,
"size": 471,
"barsHeld": 0,
"id": 1,
"margin": 10.4,
"settleDifference": false,
"pointValue": 1,
"value": 4898.400000000001
}
}
],
"closedPositions": []
}
]
Contribute
- Run tests:
npm test
- Get test coverage:
npm run coverage
- Update docs before publishing:
npm run docs