Remove deprecated Alpaca key-pair auth

This commit is contained in:
dirtydishes 2026-05-05 03:21:18 -04:00
parent 5025de78b9
commit 07a9b91df7
8 changed files with 16 additions and 55 deletions

View file

@ -4,6 +4,7 @@
{"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-ayo","title":"Drop stale backlog events from live fanout","description":"Follow-up to live freshness rollout: /ws/live was still fanning out stale backlog events for freshness-gated channels, which kept tape panes in Live feed behind despite active synthetic ingest. Gate fanout and cache ingest by freshness for options/nbbo/equities/flow.","status":"closed","priority":1,"issue_type":"bug","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:26:39Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:26:44Z","started_at":"2026-04-28T21:26:44Z","closed_at":"2026-04-28T21:26:44Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-0v6","title":"Fix tape freshness, NBBO coverage, pause controls, and filter popup","description":"Implement the tape fixes requested for synthetic options notional sizing, strict live freshness, live-mode pause/resume behavior, stronger NBBO snapshot coverage, and moving flow filters behind a popup. Includes server-side live cache changes, web terminal state/UI changes, and tests for synthetic pricing, live snapshot freshness/NBBO retention, and live pause/filter interactions.","status":"closed","priority":1,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T21:02:52Z","created_by":"dirtydishes","updated_at":"2026-04-28T21:13:38Z","started_at":"2026-04-28T21:02:57Z","closed_at":"2026-04-28T21:13:38Z","close_reason":"Completed","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-e4r","title":"Implement smart-money flow filtering and synthetic firehose modes","description":"Implement the approved multi-surface plan for named synthetic market profiles, options raw-vs-signal filtering, live/API filter contracts, Tape page client-side flow filters, firehose-readiness improvements, tests, and README updates.","status":"closed","priority":1,"issue_type":"feature","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-04-28T20:10:49Z","created_by":"dirtydishes","updated_at":"2026-04-28T20:29:29Z","started_at":"2026-04-28T20:10:53Z","closed_at":"2026-04-28T20:29:29Z","close_reason":"Implemented synthetic market profiles, options signal-path filtering, signal-aware API/replay contracts, Tape page filters, tests, and README updates. Follow-up tracked in islandflow-biq.","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-vbk","title":"Remove deprecated Alpaca key-pair auth","description":"Remove legacy Alpaca key-pair authentication support and keep ALPACA_API_KEY as the only supported auth method across options/equities ingest and docs.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T07:19:51Z","created_by":"dirtydishes","updated_at":"2026-05-05T07:21:10Z","started_at":"2026-05-05T07:19:54Z","closed_at":"2026-05-05T07:21:10Z","close_reason":"Removed key-pair auth and kept ALPACA_API_KEY only","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-h47","title":"Support single-token Alpaca auth","description":"Support single-token Alpaca authentication across ingest adapters using ALPACA_API_KEY with fallback to ALPACA_KEY_ID/ALPACA_SECRET_KEY, and document env usage.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T07:12:22Z","created_by":"dirtydishes","updated_at":"2026-05-05T07:13:54Z","started_at":"2026-05-05T07:12:25Z","closed_at":"2026-05-05T07:13:54Z","close_reason":"Added ALPACA_API_KEY support with key-pair fallback","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-h47","title":"Support single-token Alpaca auth","description":"Support single-token Alpaca authentication across ingest adapters using ALPACA_API_KEY with fallback to ALPACA_KEY_ID/ALPACA_SECRET_KEY, and document env usage.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T07:12:22Z","created_by":"dirtydishes","updated_at":"2026-05-05T07:13:54Z","started_at":"2026-05-05T07:12:25Z","closed_at":"2026-05-05T07:13:54Z","close_reason":"Added ALPACA_API_KEY support with key-pair fallback","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-neu","title":"Add Alpha Vantage event calendar provider","description":"Add an Alpha Vantage earnings-calendar provider to services/refdata that fetches CSV, normalizes entries, writes the JSON cache consumed by compute, and documents the required env variables.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T07:00:31Z","created_by":"dirtydishes","updated_at":"2026-05-05T07:02:30Z","started_at":"2026-05-05T07:00:37Z","closed_at":"2026-05-05T07:02:30Z","close_reason":"Added Alpha Vantage event-calendar provider","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-neu","title":"Add Alpha Vantage event calendar provider","description":"Add an Alpha Vantage earnings-calendar provider to services/refdata that fetches CSV, normalizes entries, writes the JSON cache consumed by compute, and documents the required env variables.\n","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-05T07:00:31Z","created_by":"dirtydishes","updated_at":"2026-05-05T07:02:30Z","started_at":"2026-05-05T07:00:37Z","closed_at":"2026-05-05T07:02:30Z","close_reason":"Added Alpha Vantage event-calendar provider","dependency_count":0,"dependent_count":0,"comment_count":0}
{"_type":"issue","id":"islandflow-b6d","title":"Finish smart-money event-calendar enrichment","description":"Finish the smart-money event-calendar provider layer in services/refdata and connect days-to-event / expiry-after-event enrichment into compute using timestamp-available data only.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-04T21:35:26Z","created_by":"dirtydishes","updated_at":"2026-05-04T23:21:09Z","started_at":"2026-05-04T23:18:29Z","closed_at":"2026-05-04T23:21:09Z","close_reason":"Completed event-calendar provider and compute enrichment","dependency_count":0,"dependent_count":0,"comment_count":0} {"_type":"issue","id":"islandflow-b6d","title":"Finish smart-money event-calendar enrichment","description":"Finish the smart-money event-calendar provider layer in services/refdata and connect days-to-event / expiry-after-event enrichment into compute using timestamp-available data only.","status":"closed","priority":2,"issue_type":"task","assignee":"dirtydishes","owner":"dishes@dpdrm.com","created_at":"2026-05-04T21:35:26Z","created_by":"dirtydishes","updated_at":"2026-05-04T23:21:09Z","started_at":"2026-05-04T23:18:29Z","closed_at":"2026-05-04T23:21:09Z","close_reason":"Completed event-calendar provider and compute enrichment","dependency_count":0,"dependent_count":0,"comment_count":0}

View file

@ -6,8 +6,6 @@ REDIS_URL=redis://127.0.0.1:6379
# Options ingest # Options ingest
OPTIONS_INGEST_ADAPTER=synthetic OPTIONS_INGEST_ADAPTER=synthetic
ALPACA_API_KEY= ALPACA_API_KEY=
ALPACA_KEY_ID=
ALPACA_SECRET_KEY=
ALPACA_REST_URL=https://data.alpaca.markets ALPACA_REST_URL=https://data.alpaca.markets
ALPACA_WS_BASE_URL=wss://stream.data.alpaca.markets/v1beta1 ALPACA_WS_BASE_URL=wss://stream.data.alpaca.markets/v1beta1
ALPACA_FEED=indicative ALPACA_FEED=indicative

View file

@ -149,8 +149,6 @@ Synthetic profile intent:
| Variable | Default | What it controls | | Variable | Default | What it controls |
| --- | --- | --- | | --- | --- | --- |
| `ALPACA_API_KEY` | empty | Single-token Alpaca API auth for options/equities adapters. Use this when your account provides one API key value. | | `ALPACA_API_KEY` | empty | Single-token Alpaca API auth for options/equities adapters. Use this when your account provides one API key value. |
| `ALPACA_KEY_ID` | empty | Alpaca key-pair auth key id (legacy/auth-pair mode). |
| `ALPACA_SECRET_KEY` | empty | Alpaca key-pair auth secret (legacy/auth-pair mode). |
| `ALPACA_REST_URL` | `https://data.alpaca.markets` | Alpaca REST base URL for contract discovery/reference calls. | | `ALPACA_REST_URL` | `https://data.alpaca.markets` | Alpaca REST base URL for contract discovery/reference calls. |
| `ALPACA_WS_BASE_URL` | `wss://stream.data.alpaca.markets/v1beta1` (options), `wss://stream.data.alpaca.markets` (equities) | Alpaca websocket base URL. | | `ALPACA_WS_BASE_URL` | `wss://stream.data.alpaca.markets/v1beta1` (options), `wss://stream.data.alpaca.markets` (equities) | Alpaca websocket base URL. |
| `ALPACA_FEED` | `indicative` | Options feed tier for Alpaca options (`indicative` or `opra`). | | `ALPACA_FEED` | `indicative` | Options feed tier for Alpaca options (`indicative` or `opra`). |
@ -162,7 +160,7 @@ Synthetic profile intent:
| `ALPACA_MAX_QUOTES` | `200` | Upper bound on selected Alpaca options contracts/quotes per cycle. | | `ALPACA_MAX_QUOTES` | `200` | Upper bound on selected Alpaca options contracts/quotes per cycle. |
| `ALPACA_EQUITIES_FEED` | `iex` | Alpaca equities feed (`iex` free tier, `sip` paid consolidated feed). | | `ALPACA_EQUITIES_FEED` | `iex` | Alpaca equities feed (`iex` free tier, `sip` paid consolidated feed). |
For Alpaca adapters, configure either `ALPACA_API_KEY` or the `ALPACA_KEY_ID` + `ALPACA_SECRET_KEY` pair. For Alpaca adapters, configure `ALPACA_API_KEY`.
### Databento replay adapter configuration ### Databento replay adapter configuration

View file

@ -21,8 +21,6 @@ NEXT_PUBLIC_NBBO_MAX_AGE_MS=1000
# Options ingest # Options ingest
OPTIONS_INGEST_ADAPTER=synthetic OPTIONS_INGEST_ADAPTER=synthetic
ALPACA_API_KEY= ALPACA_API_KEY=
ALPACA_KEY_ID=
ALPACA_SECRET_KEY=
ALPACA_REST_URL=https://data.alpaca.markets ALPACA_REST_URL=https://data.alpaca.markets
ALPACA_WS_BASE_URL=wss://stream.data.alpaca.markets/v1beta1 ALPACA_WS_BASE_URL=wss://stream.data.alpaca.markets/v1beta1
ALPACA_FEED=indicative ALPACA_FEED=indicative

View file

@ -7,8 +7,6 @@ export type AlpacaEquitiesFeed = "iex" | "sip";
export type AlpacaEquitiesAdapterConfig = { export type AlpacaEquitiesAdapterConfig = {
apiKey: string; apiKey: string;
keyId: string;
secretKey: string;
restUrl: string; restUrl: string;
wsBaseUrl: string; wsBaseUrl: string;
feed: AlpacaEquitiesFeed; feed: AlpacaEquitiesFeed;
@ -65,16 +63,9 @@ const normalizeSymbols = (symbols: string[]): string[] => {
}; };
const buildHeaders = (config: AlpacaEquitiesAdapterConfig): Record<string, string> => { const buildHeaders = (config: AlpacaEquitiesAdapterConfig): Record<string, string> => {
if (config.apiKey) {
return { return {
Authorization: `Bearer ${config.apiKey}` Authorization: `Bearer ${config.apiKey}`
}; };
}
return {
"APCA-API-KEY-ID": config.keyId,
"APCA-API-SECRET-KEY": config.secretKey
};
}; };
const parseTimestamp = (value: string): number => { const parseTimestamp = (value: string): number => {
@ -193,10 +184,8 @@ export const createAlpacaEquitiesAdapter = (
return { return {
name: "alpaca", name: "alpaca",
start: async (handlers: EquityIngestHandlers) => { start: async (handlers: EquityIngestHandlers) => {
if (!config.apiKey && (!config.keyId || !config.secretKey)) { if (!config.apiKey) {
throw new Error( throw new Error("Alpaca equities adapter requires ALPACA_API_KEY.");
"Alpaca equities adapter requires ALPACA_API_KEY or ALPACA_KEY_ID and ALPACA_SECRET_KEY."
);
} }
const symbols = normalizeSymbols(config.symbols); const symbols = normalizeSymbols(config.symbols);
@ -218,8 +207,8 @@ export const createAlpacaEquitiesAdapter = (
ws.send( ws.send(
JSON.stringify({ JSON.stringify({
action: "auth", action: "auth",
key: config.apiKey || config.keyId, key: config.apiKey,
secret: config.apiKey ? "" : config.secretKey secret: ""
}) })
); );
}); });

View file

@ -42,8 +42,6 @@ const envSchema = z.object({
// Alpaca (equities) // Alpaca (equities)
ALPACA_API_KEY: z.string().default(""), ALPACA_API_KEY: z.string().default(""),
ALPACA_KEY_ID: z.string().default(""),
ALPACA_SECRET_KEY: z.string().default(""),
ALPACA_REST_URL: z.string().default("https://data.alpaca.markets"), ALPACA_REST_URL: z.string().default("https://data.alpaca.markets"),
ALPACA_WS_BASE_URL: z.string().default("wss://stream.data.alpaca.markets"), ALPACA_WS_BASE_URL: z.string().default("wss://stream.data.alpaca.markets"),
ALPACA_UNDERLYINGS: z.string().default("SPY,NVDA,AAPL"), ALPACA_UNDERLYINGS: z.string().default("SPY,NVDA,AAPL"),
@ -168,19 +166,13 @@ const selectAdapter = (name: string): EquityIngestAdapter => {
} }
if (name === "alpaca") { if (name === "alpaca") {
const hasApiKey = Boolean(env.ALPACA_API_KEY); if (!env.ALPACA_API_KEY) {
const hasKeyPair = Boolean(env.ALPACA_KEY_ID && env.ALPACA_SECRET_KEY); logger.warn("alpaca credentials missing; set ALPACA_API_KEY");
if (!hasApiKey && !hasKeyPair) { throw new Error("ALPACA_API_KEY is required for the alpaca adapter.");
logger.warn("alpaca credentials missing; set ALPACA_API_KEY or ALPACA_KEY_ID and ALPACA_SECRET_KEY");
throw new Error(
"ALPACA_API_KEY or ALPACA_KEY_ID and ALPACA_SECRET_KEY are required for the alpaca adapter."
);
} }
return createAlpacaEquitiesAdapter({ return createAlpacaEquitiesAdapter({
apiKey: env.ALPACA_API_KEY, apiKey: env.ALPACA_API_KEY,
keyId: env.ALPACA_KEY_ID,
secretKey: env.ALPACA_SECRET_KEY,
restUrl: env.ALPACA_REST_URL, restUrl: env.ALPACA_REST_URL,
wsBaseUrl: env.ALPACA_WS_BASE_URL, wsBaseUrl: env.ALPACA_WS_BASE_URL,
feed: env.ALPACA_EQUITIES_FEED, feed: env.ALPACA_EQUITIES_FEED,

View file

@ -7,8 +7,6 @@ type AlpacaFeed = "indicative" | "opra";
type AlpacaOptionsAdapterConfig = { type AlpacaOptionsAdapterConfig = {
apiKey: string; apiKey: string;
keyId: string;
secretKey: string;
restUrl: string; restUrl: string;
wsBaseUrl: string; wsBaseUrl: string;
feed: AlpacaFeed; feed: AlpacaFeed;
@ -150,16 +148,9 @@ const normalizeUnderlyings = (value: string[]): string[] => {
}; };
const buildHeaders = (config: AlpacaOptionsAdapterConfig): Record<string, string> => { const buildHeaders = (config: AlpacaOptionsAdapterConfig): Record<string, string> => {
if (config.apiKey) {
return { return {
Authorization: `Bearer ${config.apiKey}` Authorization: `Bearer ${config.apiKey}`
}; };
}
return {
"APCA-API-KEY-ID": config.keyId,
"APCA-API-SECRET-KEY": config.secretKey
};
}; };
const fetchJson = async <T>( const fetchJson = async <T>(
@ -407,8 +398,8 @@ export const createAlpacaOptionsAdapter = (
return { return {
name: "alpaca", name: "alpaca",
start: async (handlers: OptionIngestHandlers) => { start: async (handlers: OptionIngestHandlers) => {
if (!config.apiKey && (!config.keyId || !config.secretKey)) { if (!config.apiKey) {
throw new Error("Alpaca adapter requires ALPACA_API_KEY or ALPACA_KEY_ID and ALPACA_SECRET_KEY."); throw new Error("Alpaca adapter requires ALPACA_API_KEY.");
} }
const underlyings = normalizeUnderlyings(config.underlyings); const underlyings = normalizeUnderlyings(config.underlyings);

View file

@ -50,8 +50,6 @@ const envSchema = z.object({
CLICKHOUSE_DATABASE: z.string().default("default"), CLICKHOUSE_DATABASE: z.string().default("default"),
OPTIONS_INGEST_ADAPTER: z.string().min(1).default("synthetic"), OPTIONS_INGEST_ADAPTER: z.string().min(1).default("synthetic"),
ALPACA_API_KEY: z.string().default(""), ALPACA_API_KEY: z.string().default(""),
ALPACA_KEY_ID: z.string().default(""),
ALPACA_SECRET_KEY: z.string().default(""),
ALPACA_REST_URL: z.string().default("https://data.alpaca.markets"), ALPACA_REST_URL: z.string().default("https://data.alpaca.markets"),
ALPACA_WS_BASE_URL: z.string().default("wss://stream.data.alpaca.markets/v1beta1"), ALPACA_WS_BASE_URL: z.string().default("wss://stream.data.alpaca.markets/v1beta1"),
ALPACA_FEED: z.enum(["indicative", "opra"]).default("indicative"), ALPACA_FEED: z.enum(["indicative", "opra"]).default("indicative"),
@ -230,19 +228,15 @@ const selectAdapter = (name: string): OptionIngestAdapter => {
} }
if (name === "alpaca") { if (name === "alpaca") {
const hasApiKey = Boolean(env.ALPACA_API_KEY); if (!env.ALPACA_API_KEY) {
const hasKeyPair = Boolean(env.ALPACA_KEY_ID && env.ALPACA_SECRET_KEY); logger.warn("alpaca credentials missing; set ALPACA_API_KEY");
if (!hasApiKey && !hasKeyPair) { throw new Error("ALPACA_API_KEY is required for the alpaca adapter.");
logger.warn("alpaca credentials missing; set ALPACA_API_KEY or ALPACA_KEY_ID and ALPACA_SECRET_KEY");
throw new Error("ALPACA_API_KEY or ALPACA_KEY_ID and ALPACA_SECRET_KEY are required for the alpaca adapter.");
} }
const underlyings = env.ALPACA_UNDERLYINGS.split(",").map((symbol) => symbol.trim()); const underlyings = env.ALPACA_UNDERLYINGS.split(",").map((symbol) => symbol.trim());
return createAlpacaOptionsAdapter({ return createAlpacaOptionsAdapter({
apiKey: env.ALPACA_API_KEY, apiKey: env.ALPACA_API_KEY,
keyId: env.ALPACA_KEY_ID,
secretKey: env.ALPACA_SECRET_KEY,
restUrl: env.ALPACA_REST_URL, restUrl: env.ALPACA_REST_URL,
wsBaseUrl: env.ALPACA_WS_BASE_URL, wsBaseUrl: env.ALPACA_WS_BASE_URL,
feed: env.ALPACA_FEED, feed: env.ALPACA_FEED,