phase-10.5: screener enhancements

This commit is contained in:
Kazuma
2026-06-11 19:18:19 -04:00
parent f0c794f0c0
commit bf2a85b5c4
51 changed files with 3745 additions and 36 deletions
+191
View File
@@ -0,0 +1,191 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { DigestService } from '../server/domains/digest/DigestService.js';
import { DiscordNotifier } from '../server/domains/digest/DiscordNotifier.js';
import type { SignalSnapshotRepository } from '../server/domains/shared/persistence/SignalSnapshotRepository.js';
import type { NewsRepository } from '../server/domains/news/NewsRepository.js';
import type { NewsArticleRow, SignalSnapshotRow } from '../server/domains/shared/types/index.js';
function snap(over: Partial<SignalSnapshotRow>): SignalSnapshotRow {
return {
ticker: 'AAPL',
snapshot_date: '2026-06-09',
asset_type: 'STOCK',
price: 189.5,
signal: '✅ Strong Buy',
fundamental_tier: 'PASS',
fundamental_score: 9,
fundamental_label: '🟢 BUY (High Conviction)',
inflated_tier: 'PASS',
inflated_score: 9,
inflated_label: '🟢 BUY (High Conviction)',
coverage_active: 8,
coverage_total: 11,
risk_flags: null,
rate_regime: 'NORMAL',
created_at: '2026-06-09T21:00:00.000Z',
...over,
};
}
function article(over: Partial<NewsArticleRow>): NewsArticleRow {
return {
url_hash: 'h1',
title_hash: 't1',
ticker_list: '["AAPL"]',
headline: '8-K filing: APPLE INC',
body: null,
source: 'edgar',
catalyst: 'regulatory',
url: 'https://sec.gov/x',
published_at: '2026-06-08T20:00:00.000Z',
created_at: '2026-06-08T20:01:00.000Z',
...over,
};
}
function makeService(
today: SignalSnapshotRow[],
prev: SignalSnapshotRow[],
newsByTicker: Record<string, NewsArticleRow[]> = {},
): DigestService {
const snapshots = {
byDate: () => today,
latestBefore: () => prev,
} as unknown as SignalSnapshotRepository;
const news = {
newsForTicker: (t: string) => newsByTicker[t] ?? [],
} as unknown as NewsRepository;
return new DigestService(snapshots, news);
}
test('DigestService', async (t) => {
await t.test('detects signal change and attaches catalysts', () => {
const service = makeService(
[snap({ signal: '🔄 Neutral', fundamental_score: 2 })],
[snap({ snapshot_date: '2026-06-08', signal: '✅ Strong Buy', fundamental_score: 9 })],
{ AAPL: [article({})] },
);
const report = service.build('2026-06-09');
assert.equal(report.changes.length, 1);
const c = report.changes[0];
assert.equal(c.previousSignal, '✅ Strong Buy');
assert.equal(c.newSignal, '🔄 Neutral');
assert.equal(c.scoreDelta, -7);
assert.equal(c.catalysts.length, 1);
assert.equal(c.catalysts[0].catalyst, 'regulatory');
});
await t.test('no change → empty digest', () => {
const service = makeService([snap({})], [snap({ snapshot_date: '2026-06-08' })]);
const report = service.build('2026-06-09');
assert.equal(report.changes.length, 0);
assert.equal(report.snapshotCount, 1);
});
await t.test('first-ever snapshot lands in newTickers, not changes', () => {
const service = makeService([snap({ ticker: 'NVDA' })], []);
const report = service.build('2026-06-09');
assert.equal(report.changes.length, 0);
assert.deepEqual(report.newTickers, ['NVDA']);
});
await t.test('M&A stories surface even without a signal change', () => {
const service = makeService(
[snap({})],
[snap({ snapshot_date: '2026-06-08' })], // same signal — no change
{
AAPL: [
article({
catalyst: 'ma',
headline: 'SC 13D filing: APPLE INC',
url_hash: 'h2',
url: 'https://sec.gov/13d',
}),
],
},
);
const report = service.build('2026-06-09');
assert.equal(report.changes.length, 0);
assert.equal(report.maStories.length, 1);
assert.ok(report.maStories[0].headline.includes('SC 13D'));
});
await t.test('sorts changes by signal-distance impact', () => {
const service = makeService(
[
snap({ ticker: 'SMALL', signal: '⚡ Momentum' }), // Strong Buy(0) → Momentum(1): impact 1
snap({ ticker: 'BIG', signal: '❌ Avoid' }), // Strong Buy(0) → Avoid(4): impact 4
],
[
snap({ ticker: 'SMALL', snapshot_date: '2026-06-08', signal: '✅ Strong Buy' }),
snap({ ticker: 'BIG', snapshot_date: '2026-06-08', signal: '✅ Strong Buy' }),
],
);
const report = service.build('2026-06-09');
assert.equal(report.changes[0].ticker, 'BIG');
assert.equal(report.changes[1].ticker, 'SMALL');
});
});
test('DiscordNotifier.buildPayload', async (t) => {
await t.test('returns null when nothing to report', () => {
assert.equal(
DiscordNotifier.buildPayload({
date: '2026-06-09',
changes: [],
newTickers: [],
maStories: [],
snapshotCount: 5,
}),
null,
);
});
await t.test('builds embed with change fields and M&A section', () => {
const payload = DiscordNotifier.buildPayload({
date: '2026-06-09',
changes: [
{
ticker: 'AAPL',
previousSignal: '✅ Strong Buy',
newSignal: '🔄 Neutral',
previousDate: '2026-06-08',
scoreDelta: -7,
price: 189.5,
catalysts: [
{
headline: '8-K filing: APPLE INC',
catalyst: 'regulatory',
source: 'edgar',
url: 'https://sec.gov/x',
publishedAt: '2026-06-08T20:00:00.000Z',
},
],
},
],
newTickers: [],
maStories: [
{
headline: 'SC 13D filing: APPLE INC',
catalyst: 'ma',
source: 'edgar',
url: 'https://sec.gov/13d',
publishedAt: '2026-06-08T21:00:00.000Z',
},
],
snapshotCount: 12,
});
assert.ok(payload);
const embed = payload.embeds[0] as {
title: string;
fields: Array<{ name: string; value: string }>;
};
assert.ok(embed.title.includes('2026-06-09'));
assert.equal(embed.fields.length, 2); // 1 change + 1 M&A section
assert.ok(embed.fields[0].name.includes('AAPL'));
assert.ok(embed.fields[0].name.includes('score -7'));
assert.ok(embed.fields[0].value.includes('regulatory'));
assert.ok(embed.fields[1].name.includes('M&A'));
});
});