unrip/test/maker-timing-competitiveness.test.mjs
philipp 1d66ae208f
All checks were successful
deploy / deploy (push) Successful in 48s
Expose maker edge competitiveness
Proof: Maker competitiveness now persists edge_bps into quote outcome payloads, groups summaries by edge, and shows the edge in the operator dashboard so filled versus not-filled responses can be compared against configured strategy edge.

Assumptions: Edge bps remains DB-owned pair strategy config; this change is observational and does not change live pair enablement, notional limits, inventory checks, response policy, or relay submission behavior.

Still fake: Venue-native terminal fill ids and fee-complete realized PnL remain unavailable; relay acceptance is still only submission evidence.
2026-05-19 15:45:30 +02:00

193 lines
6.9 KiB
JavaScript

import test from 'node:test';
import assert from 'node:assert/strict';
import {
buildInitialMakerTiming,
extendMakerTiming,
quoteAgeMsAt,
} from '../src/core/maker-timing.mjs';
import { buildMakerCompetitivenessSummary } from '../src/core/maker-competitiveness.mjs';
import { classifyRelaySubmissionFailure } from '../src/core/relay-failure-classification.mjs';
test('maker timing computes waterfall fields and marks clock skew unavailable', () => {
const initial = buildInitialMakerTiming({
quoteReceivedAt: '2026-05-18T10:00:00.000Z',
quoteNormalizedAt: '2026-05-18T10:00:00.010Z',
quotePublishedAt: '2026-05-18T10:00:00.020Z',
});
const timing = extendMakerTiming(initial, {
strategy_received_at: '2026-05-18T10:00:00.030Z',
strategy_decided_at: '2026-05-18T10:00:00.040Z',
command_published_at: '2026-05-18T10:00:00.050Z',
executor_received_at: '2026-05-18T10:00:00.075Z',
relay_result_at: '2026-05-18T10:00:00.140Z',
outcome_observed_at: '2026-05-18T10:00:02.000Z',
});
assert.equal(timing.quote_to_decision_ms, 40);
assert.equal(timing.decision_to_command_ms, 10);
assert.equal(timing.command_to_executor_ms, 25);
assert.equal(timing.executor_to_relay_result_ms, 65);
assert.equal(timing.quote_to_relay_result_ms, 140);
assert.equal(timing.quote_to_outcome_ms, 2000);
assert.equal(quoteAgeMsAt(timing, '2026-05-18T10:00:00.050Z'), 50);
const skewed = extendMakerTiming(initial, {
strategy_decided_at: '2026-05-18T09:59:59.999Z',
});
assert.equal(skewed.quote_to_decision_ms, null);
assert.equal(skewed.unavailable_reasons.quote_to_decision_ms, 'clock_skew_or_negative_duration');
});
test('relay submission failure classifier preserves specific already-finished category', () => {
assert.equal(
classifyRelaySubmissionFailure(new Error('quote not found or already finished')),
'quote_not_found_or_finished',
);
assert.equal(
classifyRelaySubmissionFailure({ error_message: 'quote not found or already finished' }),
'quote_not_found_or_finished',
);
assert.equal(
classifyRelaySubmissionFailure(new Error('quote_response timed out')),
'relay_timeout',
);
assert.equal(
classifyRelaySubmissionFailure(new Error('Socket not connected')),
'relay_disconnected',
);
assert.equal(
classifyRelaySubmissionFailure(new Error('current_salt unavailable')),
'salt_unavailable',
);
});
test('maker competitiveness aggregates pair, direction, request kind, result, failure, age, notional, and outcome', () => {
const nbtc = 'nep141:nbtc.bridge.near';
const eure = 'nep141:eure.omft.near';
const usdc = 'nep141:eth-0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.omft.near';
const baseTiming = buildInitialMakerTiming({
quoteReceivedAt: '2026-05-18T10:00:00.000Z',
});
const summary = buildMakerCompetitivenessSummary({
generatedAt: '2026-05-18T10:01:00.000Z',
lifecycleRows: [
{
quote_id: 'quote-eure-ok',
pair: `${nbtc}->${eure}`,
direction: 'base_to_quote',
request_kind: 'exact_in',
edge_bps: '49',
notional: '5',
notional_symbol: 'EURe',
outcome_status: 'submitted',
execution_result_at: '2026-05-18T10:00:00.080Z',
maker_timing: extendMakerTiming(baseTiming, {
strategy_decided_at: '2026-05-18T10:00:00.010Z',
command_published_at: '2026-05-18T10:00:00.020Z',
executor_received_at: '2026-05-18T10:00:00.030Z',
relay_result_at: '2026-05-18T10:00:00.080Z',
}),
execution: {
status: 'submitted',
result_code: 'quote_response_ok',
timing: { current_salt_source: 'cache' },
},
},
{
quote_id: 'quote-usdc-failed',
pair: `${nbtc}->${usdc}`,
direction: 'base_to_quote',
request_kind: 'exact_in',
edge_bps: '20',
notional: '8',
notional_symbol: 'USDC',
execution_result_at: '2026-05-18T10:00:00.400Z',
maker_timing: extendMakerTiming(baseTiming, {
strategy_decided_at: '2026-05-18T10:00:00.100Z',
command_published_at: '2026-05-18T10:00:00.150Z',
executor_received_at: '2026-05-18T10:00:00.250Z',
relay_result_at: '2026-05-18T10:00:00.400Z',
}),
execution: {
status: 'failed',
result_code: 'submission_failed',
failure_category: 'quote_not_found_or_finished',
error_message: 'quote not found or already finished',
timing: { current_salt_source: 'cache' },
},
},
{
quote_id: 'quote-usdc-skip',
pair: `${nbtc}->${usdc}`,
direction: 'base_to_quote',
request_kind: 'exact_in',
edge_bps: '20',
notional: '12',
notional_symbol: 'USDC',
maker_timing: extendMakerTiming(baseTiming, {
strategy_decided_at: '2026-05-18T10:00:00.600Z',
}),
decision: {
decision: 'blocked',
decision_reason: 'maker_quote_too_old',
response_policy: {
measured_quote_age_ms: 600,
max_quote_age_ms: 250,
},
},
},
],
});
assert.equal(summary.total.count, 3);
assert.equal(summary.total.accepted_count, 1);
assert.equal(summary.total.relay_failed_count, 1);
assert.equal(summary.total.policy_skip_count, 1);
assert.equal(summary.total.quote_not_found_or_finished_count, 1);
assert.ok(summary.groups.some((group) => (
group.pair === `${nbtc}->${usdc}`
&& group.request_kind === 'exact_in'
&& group.edge_bps === '20'
&& group.result_code === 'submission_failed'
&& group.failure_category === 'quote_not_found_or_finished'
&& group.quote_age_bucket === '250-500ms'
&& group.notional_bucket === '5-25 USDC'
&& group.outcome_status === 'relay_failed'
)));
assert.ok(summary.age_buckets.some((bucket) => (
bucket.pair === `${nbtc}->${usdc}`
&& bucket.quote_age_bucket === '500-1000ms'
&& bucket.outcome_status === 'policy_skip'
)));
assert.equal(summary.latest_errors[0].error_message, 'quote not found or already finished');
assert.equal(summary.policy_skips[0].reason_code, 'maker_quote_too_old');
});
test('maker competitiveness keeps edge bps as a grouping dimension', () => {
const nbtc = 'nep141:nbtc.bridge.near';
const usdc = 'nep141:usdc.omft.near';
const base = {
pair: `${nbtc}->${usdc}`,
direction: 'base_to_quote',
request_kind: 'exact_in',
notional: '8',
notional_symbol: 'USDC',
outcome_status: 'not_filled',
execution: {
status: 'submitted',
result_code: 'quote_response_ok',
},
};
const summary = buildMakerCompetitivenessSummary({
lifecycleRows: [
{ ...base, quote_id: 'quote-edge-1', edge_bps: '1' },
{ ...base, quote_id: 'quote-edge-20', edge_bps: '20' },
],
});
const edgeGroups = summary.groups.filter((group) => group.pair === `${nbtc}->${usdc}`);
assert.equal(edgeGroups.length, 2);
assert.deepEqual(edgeGroups.map((group) => group.edge_bps).sort(), ['1', '20']);
});