Add Alpaca dev adapter and option selection

This commit is contained in:
dirtydishes 2025-12-28 12:34:12 -05:00
parent a35ab0b778
commit 6dc279099f
8 changed files with 917 additions and 6 deletions

View file

@ -13,6 +13,7 @@ import {
insertOptionPrint
} from "@islandflow/storage";
import { OptionPrintSchema, type OptionPrint } from "@islandflow/types";
import { createAlpacaOptionsAdapter } from "./adapters/alpaca";
import { createIbkrOptionsAdapter } from "./adapters/ibkr";
import { createSyntheticOptionsAdapter } from "./adapters/synthetic";
import type { OptionIngestAdapter, StopHandler } from "./adapters/types";
@ -25,10 +26,30 @@ const envSchema = z.object({
NATS_URL: z.string().default("nats://localhost:4222"),
CLICKHOUSE_URL: z.string().default("http://localhost:8123"),
CLICKHOUSE_DATABASE: z.string().default("default"),
INGEST_ADAPTER: z.string().min(1).default("synthetic"),
INGEST_ADAPTER: z.string().min(1).default("alpaca"),
ALPACA_KEY_ID: z.string().default("PKQDUYKNHDYCPONSMWIXZHT6QV"),
ALPACA_SECRET_KEY: z.string().default("5ktmszfCiWg125GtPguuFpSeTB2zHNewScncAaY4hnKo"),
ALPACA_REST_URL: z.string().default("https://data.alpaca.markets"),
ALPACA_WS_BASE_URL: z.string().default("wss://stream.data.alpaca.markets/v1beta1"),
ALPACA_FEED: z.enum(["indicative", "opra"]).default("indicative"),
ALPACA_UNDERLYINGS: z.string().default("SPY"),
ALPACA_STRIKES_PER_SIDE: z.coerce.number().int().positive().default(8),
ALPACA_MAX_DTE_DAYS: z.coerce.number().int().positive().default(30),
ALPACA_MONEYNESS_PCT: z.coerce.number().positive().default(0.06),
ALPACA_MONEYNESS_FALLBACK_PCT: z.coerce.number().positive().default(0.1),
ALPACA_MAX_QUOTES: z.coerce.number().int().positive().default(200),
IBKR_HOST: z.string().default("127.0.0.1"),
IBKR_PORT: z.coerce.number().int().positive().default(7497),
IBKR_CLIENT_ID: z.coerce.number().int().nonnegative().default(0),
IBKR_SYMBOL: z.string().min(1).default("SPY"),
IBKR_EXPIRY: z.string().min(1).default("20250117"),
IBKR_STRIKE: z.coerce.number().positive().default(450),
IBKR_RIGHT: z
.preprocess((value) => (typeof value === "string" ? value.toUpperCase() : value), z.enum(["C", "P"]))
.default("C"),
IBKR_EXCHANGE: z.string().min(1).default("SMART"),
IBKR_CURRENCY: z.string().min(1).default("USD"),
IBKR_PYTHON_BIN: z.string().min(1).default("python3"),
EMIT_INTERVAL_MS: z.coerce.number().int().positive().default(1000)
});
@ -70,11 +91,40 @@ const selectAdapter = (name: string): OptionIngestAdapter => {
return createSyntheticOptionsAdapter({ emitIntervalMs: env.EMIT_INTERVAL_MS });
}
if (name === "alpaca") {
if (!env.ALPACA_KEY_ID || !env.ALPACA_SECRET_KEY) {
throw new Error("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({
keyId: env.ALPACA_KEY_ID,
secretKey: env.ALPACA_SECRET_KEY,
restUrl: env.ALPACA_REST_URL,
wsBaseUrl: env.ALPACA_WS_BASE_URL,
feed: env.ALPACA_FEED,
underlyings,
strikesPerSide: env.ALPACA_STRIKES_PER_SIDE,
maxDteDays: env.ALPACA_MAX_DTE_DAYS,
moneynessPct: env.ALPACA_MONEYNESS_PCT,
moneynessFallbackPct: env.ALPACA_MONEYNESS_FALLBACK_PCT,
maxQuotes: env.ALPACA_MAX_QUOTES
});
}
if (name === "ibkr") {
return createIbkrOptionsAdapter({
host: env.IBKR_HOST,
port: env.IBKR_PORT,
clientId: env.IBKR_CLIENT_ID
clientId: env.IBKR_CLIENT_ID,
symbol: env.IBKR_SYMBOL,
expiry: env.IBKR_EXPIRY,
strike: env.IBKR_STRIKE,
right: env.IBKR_RIGHT,
exchange: env.IBKR_EXCHANGE,
currency: env.IBKR_CURRENCY,
pythonBin: env.IBKR_PYTHON_BIN
});
}