news screen enhancement - 1
This commit is contained in:
@@ -85,12 +85,12 @@ test('BondScorer', async (t) => {
|
||||
});
|
||||
|
||||
await t.test('handles null/undefined metrics gracefully', () => {
|
||||
const metrics: BondMetrics = {
|
||||
const metrics = {
|
||||
ytm: null,
|
||||
duration: 5,
|
||||
creditRating: null,
|
||||
creditRatingNumeric: null,
|
||||
};
|
||||
} as unknown as BondMetrics;
|
||||
|
||||
const result = BondScorer.score(metrics, DEFAULT_RULES);
|
||||
// Should not crash
|
||||
|
||||
@@ -12,13 +12,13 @@ class MockMarketCallRepository {
|
||||
quarter: 'Q2 2024',
|
||||
thesis: 'Strong iPhone sales cycle',
|
||||
tickers: ['AAPL'],
|
||||
date: new Date('2024-05-01'),
|
||||
snapshots: [{ ticker: 'AAPL', price: 180, date: new Date('2024-05-01') }],
|
||||
date: '2024-05-01',
|
||||
snapshot: {},
|
||||
},
|
||||
];
|
||||
|
||||
async list(): Promise<(MarketCall & { id: string })[]> {
|
||||
return this.calls.sort((a, b) => b.date.getTime() - a.date.getTime());
|
||||
return this.calls.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||
}
|
||||
|
||||
async get(id: string): Promise<(MarketCall & { id: string }) | null> {
|
||||
@@ -27,7 +27,7 @@ class MockMarketCallRepository {
|
||||
|
||||
async create(call: MarketCall): Promise<MarketCall & { id: string }> {
|
||||
const id = String(this.calls.length + 1);
|
||||
const newCall = { id, ...call };
|
||||
const newCall = { ...call, id };
|
||||
this.calls.push(newCall);
|
||||
return newCall;
|
||||
}
|
||||
@@ -152,7 +152,7 @@ test('CallsController', async (t) => {
|
||||
const calls = await repository.list();
|
||||
assert.ok(Array.isArray(calls));
|
||||
assert.equal(calls.length, 1);
|
||||
assert.equal(calls[0].ticker || calls[0].title, 'AAPL Post-Earnings' || 'AAPL');
|
||||
assert.equal(calls[0].title, 'AAPL Post-Earnings');
|
||||
});
|
||||
|
||||
await t.test('returns calls sorted by date (newest first)', async () => {
|
||||
@@ -164,8 +164,8 @@ test('CallsController', async (t) => {
|
||||
quarter: 'Q1 2024',
|
||||
thesis: 'Old thesis',
|
||||
tickers: ['AAPL'],
|
||||
date: new Date('2024-01-01'),
|
||||
snapshots: [],
|
||||
date: '2024-01-01',
|
||||
snapshot: {},
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
@@ -173,13 +173,13 @@ test('CallsController', async (t) => {
|
||||
quarter: 'Q2 2024',
|
||||
thesis: 'New thesis',
|
||||
tickers: ['MSFT'],
|
||||
date: new Date('2024-05-01'),
|
||||
snapshots: [],
|
||||
date: '2024-05-01',
|
||||
snapshot: {},
|
||||
},
|
||||
];
|
||||
|
||||
async list() {
|
||||
return this.calls.sort((a, b) => b.date.getTime() - a.date.getTime());
|
||||
return this.calls.sort((a, b) => new Date(b.date).getTime() - new Date(a.date).getTime());
|
||||
}
|
||||
|
||||
async get(id: string) {
|
||||
@@ -205,14 +205,14 @@ test('CallsController', async (t) => {
|
||||
await t.test('creates new market call', async () => {
|
||||
const repository = new MockMarketCallRepository() as any;
|
||||
|
||||
const newCall: MarketCall = {
|
||||
const newCall = {
|
||||
title: 'MSFT Q3 2024',
|
||||
quarter: 'Q3 2024',
|
||||
thesis: 'Cloud growth acceleration',
|
||||
tickers: ['MSFT'],
|
||||
date: new Date('2024-07-01'),
|
||||
snapshots: [],
|
||||
};
|
||||
date: '2024-07-01',
|
||||
snapshot: {},
|
||||
} as MarketCall;
|
||||
|
||||
const created = await repository.create(newCall);
|
||||
assert.ok(created.id);
|
||||
@@ -261,14 +261,14 @@ test('CallsController', async (t) => {
|
||||
const repository = new MockMarketCallRepository() as any;
|
||||
const engine = new MockScreenerEngine() as any;
|
||||
|
||||
const newCall: MarketCall = {
|
||||
const newCall = {
|
||||
title: 'Tech Quartet',
|
||||
quarter: 'Q3 2024',
|
||||
thesis: 'All tech leaders',
|
||||
tickers: ['AAPL', 'MSFT', 'NVDA', 'GOOG'],
|
||||
date: new Date('2024-07-01'),
|
||||
snapshots: [],
|
||||
};
|
||||
date: '2024-07-01',
|
||||
snapshot: {},
|
||||
} as MarketCall;
|
||||
|
||||
const created = await repository.create(newCall);
|
||||
const results = await engine.screenTickers(created.tickers);
|
||||
@@ -290,11 +290,13 @@ test('CallsController', async (t) => {
|
||||
}
|
||||
});
|
||||
|
||||
await t.test('call includes snapshots of entry prices', async () => {
|
||||
await t.test('call includes a snapshot of entry prices', async () => {
|
||||
const repository = new MockMarketCallRepository() as any;
|
||||
|
||||
const call = await repository.get('1');
|
||||
assert.ok(call);
|
||||
assert.ok(Array.isArray(call.snapshots));
|
||||
// MarketCall.snapshot is Record<ticker, TickerSnapshot>, not an array
|
||||
assert.equal(typeof call.snapshot, 'object');
|
||||
assert.ok(!Array.isArray(call.snapshot));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -142,7 +142,7 @@ test('PortfolioAdvisor', async (t) => {
|
||||
displayMetrics: {},
|
||||
} as any,
|
||||
{
|
||||
signal: SIGNAL.BUY,
|
||||
signal: SIGNAL.STRONG_BUY,
|
||||
fundamental: { label: 'pass', scoreSummary: '', audit: { passedGates: true } },
|
||||
inflated: { label: 'pass', scoreSummary: '', audit: { passedGates: true } },
|
||||
asset: {
|
||||
@@ -239,7 +239,7 @@ test('PortfolioAdvisor', async (t) => {
|
||||
displayMetrics: {},
|
||||
} as any,
|
||||
{
|
||||
signal: SIGNAL.BUY,
|
||||
signal: SIGNAL.STRONG_BUY,
|
||||
fundamental: { label: 'pass', scoreSummary: '', audit: { passedGates: true } },
|
||||
inflated: { label: 'pass', scoreSummary: '', audit: { passedGates: true } },
|
||||
asset: {
|
||||
|
||||
@@ -3,11 +3,7 @@ import assert from 'node:assert/strict';
|
||||
import { ScreenerController } from '../server/domains/screener/screener.controller.js';
|
||||
import { ScreenerEngine } from '../server/domains/screener/ScreenerEngine.js';
|
||||
|
||||
import type {
|
||||
LiveAssetResult,
|
||||
MarketContext,
|
||||
Stock,
|
||||
} from '../server/domains/shared/types/index.js';
|
||||
import type { LiveAssetResult, MarketContext } from '../server/domains/shared/types/index.js';
|
||||
import { ASSET_TYPE, SIGNAL } from '../server/domains/shared/config/constants.js';
|
||||
|
||||
// Mock implementations
|
||||
@@ -43,12 +39,24 @@ class MockScreenerEngine extends ScreenerEngine {
|
||||
returnOnEquity: 95.2,
|
||||
freeCashFlow: 100000000,
|
||||
}),
|
||||
} as unknown as Stock;
|
||||
} as unknown as LiveAssetResult['asset'];
|
||||
|
||||
const mockResult: LiveAssetResult = {
|
||||
asset: mockStock,
|
||||
fundamentalScore: { label: '✓ BUY', scoreSummary: 'Quality gate PASS' },
|
||||
inflatedScore: { label: '✓ BUY', scoreSummary: 'Market adjusted gate PASS' },
|
||||
fundamental: {
|
||||
label: '🟢 BUY (High Conviction)',
|
||||
tier: 'PASS',
|
||||
score: 9,
|
||||
scoreSummary: 'Quality gate PASS',
|
||||
audit: { passedGates: true },
|
||||
},
|
||||
inflated: {
|
||||
label: '🟢 BUY (High Conviction)',
|
||||
tier: 'PASS',
|
||||
score: 9,
|
||||
scoreSummary: 'Market adjusted gate PASS',
|
||||
audit: { passedGates: true },
|
||||
},
|
||||
signal: SIGNAL.STRONG_BUY,
|
||||
};
|
||||
|
||||
@@ -190,7 +198,7 @@ test('ScreenerController', async (t) => {
|
||||
assert.equal(results.STOCK.length, 1);
|
||||
const result = results.STOCK[0];
|
||||
assert.ok(result.signal);
|
||||
assert.ok(result.fundamentalScore);
|
||||
assert.ok(result.inflatedScore);
|
||||
assert.ok(result.fundamental);
|
||||
assert.ok(result.inflated);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user