SPB Git

Repository Public

live.ts 2.8 KB 44 lines typescript

1// Controlled live trading — wiring for the rest of the API service: the process-wide execution engine on the real signer2// client and the real venue, and its reconciliation worker. Disabled by construction: an account trades only after the owner's3// passkey approved a venue authorization on the signer, and mainnet additionally needs SPW_DERIV_LIVE_MAINNET=on.4import { listCreds } from '../auth.ts';5import { config } from '../config.ts';6import { signer } from '../signer-client.ts';7import { loop } from '../worker.ts';8import { LiveService, LIVE_STEP_MS, type LiveDeps } from './exec.ts';9import { LiveVenues } from './live-venue.ts';1011/** server setting, read at every use: anything but the exact value `on` keeps the mainnet exchange endpoint closed */12export const mainnetLiveEnabled = (): boolean => process.env.SPW_DERIV_LIVE_MAINNET === 'on';1314/** second, separate server setting for AUTONOMOUS (mandate) trading on mainnet: it needs this AND SPW_DERIV_LIVE_MAINNET, both exactly `on` */15export const mandateMainnetEnabled = (): boolean => process.env.SPW_DERIV_MANDATE_MAINNET === 'on';1617/** tests pass a fake venue (`fetch`), an in-process signer and a clock */18export function createLive(o: Partial<LiveDeps> & { fetch?: typeof fetch } = {}): LiveService {19  const now = o.now ?? Date.now, mainnetEnabled = o.mainnetEnabled ?? mainnetLiveEnabled;20  return new LiveService({21    venues: o.venues ?? new LiveVenues({ now, mainnetEnabled, ...(o.fetch ? { fetch: o.fetch } : {}) }),22    signer: o.signer ?? signer, now, rpId: o.rpId ?? config.rpId, mainnetEnabled,23    // derivative mandates: the real signer client offers them; a test that injects its own signer port says so explicitly24    ...(o.mandateSigner ? { mandateSigner: o.mandateSigner } : o.signer ? {} : { mandateSigner: signer }), mandateMainnetEnabled: o.mandateMainnetEnabled ?? mandateMainnetEnabled,25    credentials: o.credentials ?? (async () => (await listCreds()).map((c) => ({ id: c.id, transports: c.transports }))),26    ...(o.confirmDelayMs !== undefined ? { confirmDelayMs: o.confirmDelayMs } : {}),27  });28}2930let shared: LiveService | null = null;31export function live(): LiveService { return (shared ??= createLive()); }3233/** ONE reconciliation worker: orders in flight, fills, funding, positions, divergence. It never signs and never places anything. */34export function startLiveWorker(l: LiveService = live()): void {35  let lastError = '';36  loop('deriv-live', async () => {37    const s = await l.step();38    if (s.fills || s.funding || s.divergences) console.log(`[derivatives] live: ${s.accounts} account(s), ${s.reconciled} order(s) reconciled, ${s.fills} fill(s), ${s.funding} funding payment(s), ${s.divergences} divergence(s)`);39    const err = s.errors[0] ?? '';40    if (err && err !== lastError) console.log(`[derivatives] live: ${s.errors.length} error(s) (${err})`);41    lastError = err;42  }, LIVE_STEP_MS);43}44
Files main
Menu

Theme