Support single-token Alpaca auth

This commit is contained in:
dirtydishes 2026-05-05 03:14:01 -04:00
parent dd32be7717
commit 5025de78b9
8 changed files with 63 additions and 20 deletions

View file

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

View file

@ -49,6 +49,7 @@ const envSchema = z.object({
CLICKHOUSE_URL: z.string().default("http://127.0.0.1:8123"),
CLICKHOUSE_DATABASE: z.string().default("default"),
OPTIONS_INGEST_ADAPTER: z.string().min(1).default("synthetic"),
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"),
@ -229,14 +230,17 @@ const selectAdapter = (name: string): OptionIngestAdapter => {
}
if (name === "alpaca") {
if (!env.ALPACA_KEY_ID || !env.ALPACA_SECRET_KEY) {
logger.warn("alpaca credentials missing; set ALPACA_KEY_ID and ALPACA_SECRET_KEY");
throw new Error("ALPACA_KEY_ID and ALPACA_SECRET_KEY are required for the alpaca adapter.");
const hasApiKey = Boolean(env.ALPACA_API_KEY);
const hasKeyPair = Boolean(env.ALPACA_KEY_ID && env.ALPACA_SECRET_KEY);
if (!hasApiKey && !hasKeyPair) {
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());
return createAlpacaOptionsAdapter({
apiKey: env.ALPACA_API_KEY,
keyId: env.ALPACA_KEY_ID,
secretKey: env.ALPACA_SECRET_KEY,
restUrl: env.ALPACA_REST_URL,