refactor: restructure to clean architecture
fix: restore ScoringConfig improvements lost in refactor commit docs: rewrite README and CLAUDE.md to reflect current architecture code-format code fixes
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { mapToStandardFormat } from '../src/screener/DataMapper.js';
|
||||
|
||||
const base = {
|
||||
price: { quoteType: 'EQUITY', regularMarketPrice: 150 },
|
||||
assetProfile: { sector: 'Technology', industry: 'Software', category: '' },
|
||||
financialData: {
|
||||
quickRatio: 1.2,
|
||||
debtToEquity: 150,
|
||||
freeCashflow: 5e9,
|
||||
revenueGrowth: 0.15,
|
||||
profitMargins: 0.25,
|
||||
operatingMargins: 0.3,
|
||||
returnOnEquity: 0.2,
|
||||
earningsGrowth: 0.12,
|
||||
operatingCashflow: 8e9,
|
||||
},
|
||||
defaultKeyStatistics: { pegRatio: null, forwardPE: 28, sharesOutstanding: 1e9, priceToBook: 12 },
|
||||
summaryDetail: {
|
||||
trailingAnnualDividendYield: 0.005,
|
||||
trailingPE: 30,
|
||||
beta: 1.2,
|
||||
fiftyTwoWeekHigh: 200,
|
||||
fiftyTwoWeekLow: 120,
|
||||
},
|
||||
};
|
||||
|
||||
test('maps EQUITY quote type to STOCK', () => {
|
||||
const result = mapToStandardFormat('AAPL', base);
|
||||
assert.equal(result.type, 'STOCK');
|
||||
assert.equal(result.ticker, 'AAPL');
|
||||
});
|
||||
|
||||
test('computes PEG from trailingPE / earningsGrowth when Yahoo returns null', () => {
|
||||
const result = mapToStandardFormat('AAPL', base);
|
||||
const expected = +(30 / (0.12 * 100)).toFixed(2); // trailingPE=30, earningsGrowth=12%
|
||||
assert.equal(result.pegRatio, expected);
|
||||
});
|
||||
|
||||
test('uses Yahoo pegRatio when available', () => {
|
||||
const summary = {
|
||||
...base,
|
||||
defaultKeyStatistics: { ...base.defaultKeyStatistics, pegRatio: 1.5 },
|
||||
};
|
||||
const result = mapToStandardFormat('AAPL', summary);
|
||||
assert.equal(result.pegRatio, 1.5);
|
||||
});
|
||||
|
||||
test('debtToEquity is divided by 100', () => {
|
||||
const result = mapToStandardFormat('AAPL', base);
|
||||
assert.equal(result.debtToEquity, 1.5); // 150 / 100
|
||||
});
|
||||
|
||||
test('maps ETF quoteType to ETF', () => {
|
||||
const etfSummary = {
|
||||
...base,
|
||||
price: { ...base.price, quoteType: 'ETF' },
|
||||
assetProfile: { category: 'Large Blend' },
|
||||
};
|
||||
const result = mapToStandardFormat('VOO', etfSummary);
|
||||
assert.equal(result.type, 'ETF');
|
||||
});
|
||||
|
||||
test('classifies bond ETF from category keyword', () => {
|
||||
const bondSummary = {
|
||||
...base,
|
||||
price: { ...base.price, quoteType: 'ETF' },
|
||||
assetProfile: { category: 'Intermediate-Term Bond' },
|
||||
};
|
||||
const result = mapToStandardFormat('BND', bondSummary);
|
||||
assert.equal(result.type, 'BOND');
|
||||
});
|
||||
|
||||
test('FCF yield is computed when data available', () => {
|
||||
const result = mapToStandardFormat('AAPL', base);
|
||||
assert.notEqual(result.fcfYield, null);
|
||||
assert(result.fcfYield > 0);
|
||||
});
|
||||
|
||||
test('metrics are null (not 0) when data missing', () => {
|
||||
const sparse = {
|
||||
price: { quoteType: 'EQUITY', regularMarketPrice: 100 },
|
||||
financialData: {},
|
||||
defaultKeyStatistics: {},
|
||||
summaryDetail: {},
|
||||
assetProfile: {},
|
||||
};
|
||||
const result = mapToStandardFormat('X', sparse);
|
||||
assert.equal(result.pegRatio, null);
|
||||
assert.equal(result.quickRatio, null);
|
||||
});
|
||||
Reference in New Issue
Block a user