diff --git a/src/autonomy/coordinator.js b/src/autonomy/coordinator.js index a173e84..c6afde0 100644 --- a/src/autonomy/coordinator.js +++ b/src/autonomy/coordinator.js @@ -8,6 +8,19 @@ const ALLOWED_HORIZONS = new Set([1, 5, 10, 20, 30, 60, 90]); // one thing in the prompt and another in calibration. const ALLOWED_EVENT_TYPES = new Set(EVENT_FAMILY_NAMES); +// 96% of rejected proposals name an instrument we cannot trade: indices (SPX, +// DXY, ^TNX), fx (EURUSD, XAU/USD), futures (CL=F, BZ=F) and foreign listings +// (VOW3.DE, RHM.DE, 1211.HK). The analysis behind those is usually fine, it is +// the ticker that is unusable, so tell the model what the allowlist actually +// holds instead of paying for the call and discarding it at validation. +// Every symbol named below was checked against the live allowlist. +const INSTRUMENT_RULES = `Instrument rules. instrument must be a symbol tradable on a US exchange, and only these resolve: +- US listed common stock or ETF, by its US ticker. +- A foreign company only via its US listing or ADR, never its home listing. Volkswagen is VWAGY not VOW3.DE, Alibaba is BABA, Toyota is TM, Sony is SONY. If you do not know the company has a US listing, omit the prediction. +- An index, currency, rate or commodity only via a US listed ETF that tracks it: S&P 500 -> SPY, Nasdaq 100 -> QQQ, gold -> GLD, crude oil -> USO, US dollar -> UUP, treasuries -> TLT. Never emit SPX, DXY, ^TNX, EURUSD, XAU/USD, CL=F or any futures or fx symbol. +- Never SPY itself as the prediction, it is the benchmark and its excess return is always zero. +If the only instrument the evidence supports is untradable under these rules, leave it out rather than substituting something loosely related.`; + // An exact family is what we want. If the model ignores the list we try to salvage // the label through the same mapper calibration uses, and only give up when it is // unplaceable -- an explicit 'other' is a legitimate answer, unplaceable free text @@ -128,4 +141,4 @@ function recordRejectedProposal(intelligenceDb, raw, metadata = {}, reason = 'va metadata.model || 'unknown', metadata.promptVersion || 'unknown', String(reason).slice(0, 1000)).lastInsertRowid; } -module.exports = { normalizeProposal, verifyEvidence, acceptProposal, recordRejectedProposal }; +module.exports = { normalizeProposal, verifyEvidence, acceptProposal, recordRejectedProposal, INSTRUMENT_RULES }; diff --git a/workers/coordinatorWorker.js b/workers/coordinatorWorker.js index ef0b254..1e4837b 100644 --- a/workers/coordinatorWorker.js +++ b/workers/coordinatorWorker.js @@ -5,7 +5,7 @@ const { openRuntimeDb } = require('../src/db/runtime'); const { initAutonomySchema } = require('../src/autonomy/schema'); const { leaseNextJob, completeJob, failJob } = require('../src/autonomy/jobs'); const { callCoordinator } = require('../src/autonomy/llm'); -const { acceptProposal, recordRejectedProposal } = require('../src/autonomy/coordinator'); +const { acceptProposal, recordRejectedProposal, INSTRUMENT_RULES } = require('../src/autonomy/coordinator'); const { EVENT_FAMILY_NAMES } = require('../src/autonomy/calibration'); function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } @@ -30,7 +30,7 @@ function buildPrompt(event, articles) { causal_channel: '', horizon_days: '', evidence_article_ids: [''], invalidation_condition: '', - }] }, null, 2)}\n\nEvery value in that shape is a placeholder describing the field. Do not copy them. Choose instrument, direction and horizon_days from the evidence in front of you.\n\nevent_type must be exactly one of: ${EVENT_FAMILY_NAMES.join(', ')}. Pick the closest one. Use "other" only when none of them genuinely apply, and never invent a value outside this list.\n\nUse only instruments and evidence directly supported by the articles. Return an empty predictions array when there is no clear, tradable hypothesis. Never include probabilities, returns, confidence, position sizes, or actions.`; + }] }, null, 2)}\n\nEvery value in that shape is a placeholder describing the field. Do not copy them. Choose instrument, direction and horizon_days from the evidence in front of you.\n\nevent_type must be exactly one of: ${EVENT_FAMILY_NAMES.join(', ')}. Pick the closest one. Use "other" only when none of them genuinely apply, and never invent a value outside this list.\n\n${INSTRUMENT_RULES}\n\nUse only instruments and evidence directly supported by the articles. Return an empty predictions array when there is no clear, tradable hypothesis. Never include probabilities, returns, confidence, position sizes, or actions.`; } async function runCoordinatorWorker({ archivePath, intelligencePath, workerId = `coordinator-${os.hostname()}-${process.pid}`, pollMs = 1000 } = {}) { diff --git a/workers/replayWorker.js b/workers/replayWorker.js index 716eada..c86adfd 100644 --- a/workers/replayWorker.js +++ b/workers/replayWorker.js @@ -5,7 +5,7 @@ const { openRuntimeDb } = require('../src/db/runtime'); const { initAutonomySchema } = require('../src/autonomy/schema'); const { enqueueJob, leaseNextJob, completeJob, failJob } = require('../src/autonomy/jobs'); const { callCoordinator } = require('../src/autonomy/llm'); -const { acceptProposal, recordRejectedProposal } = require('../src/autonomy/coordinator'); +const { acceptProposal, recordRejectedProposal, INSTRUMENT_RULES } = require('../src/autonomy/coordinator'); const { EVENT_FAMILY_NAMES } = require('../src/autonomy/calibration'); function sleep(ms) { return new Promise((resolve) => setTimeout(resolve, ms)); } @@ -39,6 +39,7 @@ function replayPrompt(article) { }] }, null, 2)}\n\n` + 'Every value in that shape is a placeholder describing the field. Do not copy them. Choose instrument, direction and horizon_days from the evidence in front of you.\n\n' + `event_type must be exactly one of: ${EVENT_FAMILY_NAMES.join(', ')}. Pick the closest one. Use "other" only when none of them genuinely apply, and never invent a value outside this list.\n\n` + + `${INSTRUMENT_RULES}\n\n` + 'Use only this dated evidence. Return an empty predictions array when there is no clear, tradable hypothesis. Never include probabilities, returns, confidence, position sizes, or actions.'; }