Skip to content

Commit

Permalink
Merge pull request #2 from KendelChopp/add-testing
Browse files Browse the repository at this point in the history
Add Tests and Make Backwards Compatible
  • Loading branch information
KendelChopp committed Sep 7, 2020
2 parents 6c925aa + 8ffce5c commit fcd8c65
Show file tree
Hide file tree
Showing 17 changed files with 3,840 additions and 2,026 deletions.
1 change: 1 addition & 0 deletions .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* @KendelChopp
2 changes: 1 addition & 1 deletion .github/workflows/node.js.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:

strategy:
matrix:
node-version: [14.x]
node-version: [12.x]

steps:
- uses: actions/checkout@v2
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
dist
node_modules
coverage
5,005 changes: 3,013 additions & 1,992 deletions package-lock.json

Large diffs are not rendered by default.

10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
{
"name": "@kendelchopp/alpaca-js-backtesting",
"version": "1.0.0",
"version": "1.1.0",
"description": "",
"main": "src/index.js",
"scripts": {
"build": "webpack"
"test": "jest --collect-coverage",
"ci": "npm run test"
},
"repository": {
"type": "git",
Expand All @@ -17,10 +18,9 @@
},
"homepage": "https://github.com/KendelChopp/alpaca-js-backtesting#readme",
"devDependencies": {
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12"
"jest": "^26.4.2",
"q": "^1.5.1"
},
"type": "module",
"dependencies": {
"lodash": "^4.17.20"
}
Expand Down
22 changes: 11 additions & 11 deletions src/MarketData.js → src/MarketData/MarketData.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash';
const _ = require('lodash');

import Security from './Security.js';
const Security = require('../Security/Security.js');

/**
* Class tracking market data and time for all relevant securities
Expand All @@ -18,12 +18,12 @@ class MarketData {
/**
* Adds security with data from Alpaca data
*
* @param {string} ticker - the ticker for the security
* @param {string} symbol - the symbol for the security
* @param {Object[]} data - The array of data points from Alpaca
*/
addSecurity(ticker, data) {
addSecurity(symbol, data) {
// TODO: Convert data to a separate class to ensure it is structured
this.securities[ticker] = new Security(ticker, data);
this.securities[symbol] = new Security(symbol, data);
this.maxTime = Math.max(data.length, this.maxTime);
}

Expand All @@ -40,7 +40,7 @@ class MarketData {
return {
...security.data[this.time],
ev: 'AM',
sym: security.ticker
sym: security.symbol
}
});

Expand All @@ -58,15 +58,15 @@ class MarketData {
}

/**
* Gets the current price of a security based on the ticker
* Gets the current price of a security based on the symbol
*
* @param {string} ticker - the ticker for the security
* @param {string} symbol - the symbol for the security
* @returns {number} the value of the security
*/
getPrice(ticker) {
const security = this.securities[ticker];
getPrice(symbol) {
const security = this.securities[symbol];
return security.price;
}
}

export default MarketData;
module.exports = MarketData;
135 changes: 135 additions & 0 deletions src/MarketData/MarketData.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
const Security = require('../Security/Security.js');

const MarketData = require('./MarketData.js');

describe('MarketData', () => {
let marketData;

beforeEach(() => {
marketData = new MarketData();
});

test('sets the correct defaults', () => {
expect(marketData.maxTime).toBe(0);
expect(marketData.time).toBe(0);
expect(marketData.securities).toEqual({});
});

describe('addSecurity', () => {
let data, symbol;

beforeEach(() => {
data = [{ some: 'data' }, { some: 'other data' }];
symbol = 'AAPL';
});

test('adds a new security', () => {
marketData.addSecurity(symbol, data);
const newSecurity = marketData.securities[symbol];
expect(newSecurity).toBeInstanceOf(Security);
expect(newSecurity.data).toBe(data);
expect(newSecurity.symbol).toBe(symbol);
});

describe('when max time is less than data.length', () => {
beforeEach(() => {
marketData.maxTime = 0;
});

test('sets maxTime to data.length', () => {
marketData.addSecurity(symbol, data);
expect(marketData.maxTime).toBe(data.length);
});
});

describe('when max time is less than data.length', () => {
let maxTime;

beforeEach(() => {
maxTime = 10;
marketData.maxTime = maxTime;
});

test('does not change maxTime', () => {
marketData.addSecurity(symbol, data);
expect(marketData.maxTime).toBe(maxTime);
});
});
});

describe('simulateMinute', () => {
let closePrice, validSecurity, invalidSecurity;

beforeEach(() => {
closePrice = 123;
validSecurity = new Security('SPY', [{ closePrice }]);
invalidSecurity = new Security('AAPL', []);

marketData.securities = [validSecurity, invalidSecurity];
marketData.time = 0;
});

test('updates the valid security price', () => {
marketData.simulateMinute();
expect(validSecurity.price).toBe(closePrice);
});

test('adds one to the current time', () => {
marketData.simulateMinute();
expect(marketData.time).toBe(1);
});

test('returns a stringified map of the valid securities', () => {
const simulation = marketData.simulateMinute();
const expected = JSON.stringify([{
closePrice,
ev: 'AM',
sym: validSecurity.symbol
}]);

expect(simulation).toEqual(expected);
});
});

describe('hasData', () => {
describe('when time is less than maxTime', () => {
test('returns true', () => {
marketData.time = 0;
marketData.maxTime = 1;
expect(marketData.hasData).toBe(true);
});
});

describe('when time is equal to maxTime', () => {
test('returns false', () => {
marketData.time = 1;
marketData.maxTime = 1;
expect(marketData.hasData).toBe(false);
});
});

describe('when time is greater than maxTime', () => {
test('returns false', () => {
marketData.time = 2;
marketData.maxTime = 1;
expect(marketData.hasData).toBe(false);
});
});
});

describe('getPrice', () => {
let price, symbol;

beforeEach(() => {
symbol = 'AAPL';
price = 12;
const security = new Security(symbol, []);
security.price = price;
marketData.securities = { [symbol]: security };
});

test('returns the price of the requested security', () => {
expect(marketData.getPrice(symbol)).toBe(price);
});
});
});
10 changes: 5 additions & 5 deletions src/Portfolio.js → src/Portfolio/Portfolio.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import _ from 'lodash';
const _ = require('lodash');

import Position from './Position.js';
const Position = require('../Position/Position.js');

/**
* A class representing cash and all of the positions taken
Expand Down Expand Up @@ -48,7 +48,7 @@ class Portfolio {
/**
* Create an order
*
* @param {string} options.symbol - The ticker/symbol for the order
* @param {string} options.symbol - The symbol for the order
* @param {number} options.qty - Quantity of the order
* @param {string} options.side - 'buy' or 'sell'
* @param {string} options.type - currently only supports 'market'
Expand All @@ -61,7 +61,7 @@ class Portfolio {
throw new Error('No symbol provided for order.');
}
if (!options.qty || options.qty < 1) {
throw new Error('Quantity must be >= 1 to create an order');
throw new Error('Quantity must be >= 1 to create an order.');
}
if (!options.side || (options.side !== 'sell' && options.side !== 'buy')) {
throw new Error('Side not provided correctly. Must be buy or sell.');
Expand Down Expand Up @@ -126,4 +126,4 @@ class Portfolio {
}
}

export default Portfolio;
module.exports = Portfolio;
Loading

0 comments on commit fcd8c65

Please sign in to comment.