feat: Unraid deploy, dealer-flow heatmap, confluence zones, 13F capture
Ship Node production images, Unraid compose, and Gitea CI/CD (test then push registry images; cron script if no runner). Rebuild dealer flow as a heatmap-first map with integrity gates and chart helpers. Add confluence zone rules, session clock, capture evidence, and tighter 13F/queue/options paths, plus the matching UI and tests.
This commit is contained in:
@@ -3,6 +3,7 @@ import { strict as assert } from 'node:assert';
|
||||
import {
|
||||
buildDealerMap,
|
||||
contractGex,
|
||||
contractVex,
|
||||
isDegradedDealerMap,
|
||||
isHealthyDealerMap,
|
||||
isSaneIv,
|
||||
@@ -50,6 +51,13 @@ test('contractGex: call positive, put negative', () => {
|
||||
assert.equal(call, -put);
|
||||
});
|
||||
|
||||
test('contractVex uses the same S² notional as GEX', () => {
|
||||
const gex = contractGex(0.02, 1000, 44, 'call');
|
||||
const vex = contractVex(0.02, 1000, 44, 'call');
|
||||
assert.equal(vex, gex);
|
||||
assert.equal(contractVex(0.02, 1000, 44, 'put'), -vex);
|
||||
});
|
||||
|
||||
test('buildDealerMap: expiries sorted nearest-left and capped', () => {
|
||||
const rows: NormalizedOptionRow[] = [];
|
||||
for (const exp of ['2026-08-08', '2026-08-15', '2026-08-22', '2026-09-19', '2026-10-17', '2026-11-21', '2026-12-19', '2027-01-16']) {
|
||||
|
||||
@@ -71,6 +71,41 @@ test('auditDealerMap: complete fat surface + map', () => {
|
||||
assert.match(integritySummary(r), /complete/i);
|
||||
});
|
||||
|
||||
test('auditDealerSurface: Friday chain on Sunday is not stale', () => {
|
||||
const s = fatSurface();
|
||||
s.asOf = '2026-08-16T17:00:00.000Z';
|
||||
s.quoteAsOf = '2026-08-16T17:00:00.000Z';
|
||||
s.chainAsOf = '2026-08-14T21:30:00.000Z';
|
||||
const r = auditDealerSurface(s);
|
||||
const age = r.checks.find((c) => c.id === 'chain_age');
|
||||
assert.ok(age);
|
||||
assert.equal(age!.ok, true);
|
||||
assert.ok(!r.hardFailed.includes('chain_age'));
|
||||
});
|
||||
|
||||
test('auditDealerSurface: 3-session-old book is incomplete', () => {
|
||||
const s = fatSurface();
|
||||
s.asOf = '2026-08-16T17:00:00.000Z';
|
||||
s.quoteAsOf = '2026-08-16T17:00:00.000Z';
|
||||
s.chainAsOf = '2026-08-12T03:46:00.000Z';
|
||||
const r = auditDealerSurface(s);
|
||||
assert.equal(r.status, 'incomplete');
|
||||
assert.ok(r.hardFailed.includes('chain_age'), JSON.stringify(r.hardFailed));
|
||||
});
|
||||
|
||||
test('auditDealerSurface: spot newer than book is soft degraded', () => {
|
||||
const s = fatSurface();
|
||||
s.asOf = '2026-08-14T20:00:00.000Z';
|
||||
s.quoteAsOf = '2026-08-14T20:00:00.000Z';
|
||||
s.chainAsOf = '2026-08-14T10:00:00.000Z';
|
||||
const r = auditDealerSurface(s);
|
||||
assert.notEqual(r.status, 'incomplete');
|
||||
const mix = r.checks.find((c) => c.id === 'spot_vs_chain');
|
||||
assert.ok(mix);
|
||||
assert.equal(mix!.ok, false);
|
||||
assert.equal(r.status, 'degraded');
|
||||
});
|
||||
|
||||
test('auditDealerSurface: delay alone does not hard-fail', () => {
|
||||
const s = fatSurface();
|
||||
s.delaySeconds = 900;
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
import { test } from 'node:test';
|
||||
import { strict as assert } from 'node:assert';
|
||||
import { rthClosesBetween } from '../sessionClock.ts';
|
||||
|
||||
test('rthClosesBetween: Friday after-close book on Sunday is 0 sessions', () => {
|
||||
assert.equal(
|
||||
rthClosesBetween('2026-08-14T21:30:00.000Z', '2026-08-16T17:00:00.000Z'),
|
||||
0,
|
||||
);
|
||||
});
|
||||
|
||||
test('rthClosesBetween: Wednesday book on Sunday is 3 closes (Wed Thu Fri)', () => {
|
||||
assert.equal(
|
||||
rthClosesBetween('2026-08-12T03:46:00.000Z', '2026-08-16T17:00:00.000Z'),
|
||||
3,
|
||||
);
|
||||
});
|
||||
|
||||
test('rthClosesBetween: same timestamp is 0', () => {
|
||||
assert.equal(
|
||||
rthClosesBetween('2026-08-14T16:00:00.000Z', '2026-08-14T16:00:00.000Z'),
|
||||
0,
|
||||
);
|
||||
});
|
||||
Reference in New Issue
Block a user