feat: Unraid deploy, dealer-flow heatmap, confluence zones, 13F capture
Ship Node production images, Unraid compose, and Gitea CI/CD (test then push registry images; cron script if no runner). Rebuild dealer flow as a heatmap-first map with integrity gates and chart helpers. Add confluence zone rules, session clock, capture evidence, and tighter 13F/queue/options paths, plus the matching UI and tests.
This commit is contained in:
+96
-4
@@ -58,7 +58,7 @@ export interface FundHoldersResult {
|
||||
symbol: string;
|
||||
tracked: Array<{
|
||||
fundId: string; fundName: string; shares: number | null; valueUsd: number | null;
|
||||
asOf: string; source: string; weightPct: number | null;
|
||||
asOf: string; source: string; notes: string | null; weightPct: number | null;
|
||||
}>;
|
||||
institutionCount: number;
|
||||
}
|
||||
@@ -79,6 +79,22 @@ export interface XAccountRow {
|
||||
handle: string;
|
||||
label: string;
|
||||
created_at: string;
|
||||
pullStatus?: string | null;
|
||||
pullAt?: string | null;
|
||||
pullError?: string | null;
|
||||
lastPostAt?: string | null;
|
||||
}
|
||||
|
||||
export interface FundFreshness {
|
||||
xHandle: string | null;
|
||||
xPull: {
|
||||
status: "pending" | "in_flight" | "done" | "backoff" | "failed" | "none";
|
||||
lastAttempt: string | null;
|
||||
error: string | null;
|
||||
};
|
||||
lastPostAt: string | null;
|
||||
lastCaptureAt: string | null;
|
||||
last13fAt: string | null;
|
||||
}
|
||||
|
||||
// ─── Dealer Flow types ─────────────────────────────────────────────────────
|
||||
@@ -119,6 +135,8 @@ export interface DealerMapPayload {
|
||||
symbol: string;
|
||||
spot: number;
|
||||
asOf: string;
|
||||
quoteAsOf?: string | null;
|
||||
chainAsOf?: string | null;
|
||||
delayNote: string;
|
||||
delaySeconds: number | null;
|
||||
oiFreshness: string;
|
||||
@@ -659,6 +677,8 @@ export interface XFedFeed {
|
||||
nextCursor: string | null;
|
||||
configured: boolean;
|
||||
health?: { healthy: 'healthy' | 'degraded'; lastError: string | null } | null;
|
||||
lastPullAt?: string | null;
|
||||
queuedHandles?: number;
|
||||
}
|
||||
export interface MacroCommentary {
|
||||
shortTerm: string;
|
||||
@@ -672,6 +692,7 @@ export interface Snapshot {
|
||||
candles: PriceCandle[] | null;
|
||||
sector: SymbolMeta | null;
|
||||
stale: { quote: boolean; candles: boolean; sector: boolean };
|
||||
observed?: { quote: string | null; candles: string | null; sector: string | null };
|
||||
}
|
||||
export interface AuthUser {
|
||||
userId: string;
|
||||
@@ -783,7 +804,7 @@ export interface IndicatorPeriods {
|
||||
}
|
||||
export interface IndicatorOptions {
|
||||
periods?: IndicatorPeriods;
|
||||
timeframe?: '1d' | '1wk' | '1mo';
|
||||
timeframe?: '1m' | '5m' | '1d' | '1wk' | '1mo';
|
||||
}
|
||||
export interface IndicatorResult {
|
||||
ema?: Record<number, (number | null)[]>;
|
||||
@@ -848,7 +869,9 @@ export const api = {
|
||||
/** Batched snapshot for multiple symbols — reduces N+1 API calls */
|
||||
snapshots: (symbols: string[]) => trpcQuery<Array<{ symbol: string; quote: Snapshot['quote'] | null; candles: PriceCandle[] | null; sector: SymbolMeta | null; stale: { quote: boolean; candles: boolean; sector: boolean } }>>("market.snapshots", { symbols }),
|
||||
tickerContext: (symbol: string) => trpcQuery<TickerContext>("market.tickerContext", { symbol }),
|
||||
candles: async (symbol: string, timeframe = "1d") => trpcQuery<{ symbol: string; timeframe: string; candles: PriceCandle[]; isStale: boolean }>("market.candles", { symbol, timeframe }).then((r) => r?.candles ?? []),
|
||||
candles: async (symbol: string, timeframe = "1d") => trpcQuery<{ symbol: string; timeframe: string; candles: PriceCandle[]; isStale: boolean; fetchedAt: string | null }>("market.candles", { symbol, timeframe }).then((r) => r?.candles ?? []),
|
||||
candlesWithMeta: (symbol: string, timeframe = "1d") =>
|
||||
trpcQuery<{ symbol: string; timeframe: string; candles: PriceCandle[]; isStale: boolean; fetchedAt: string | null }>("market.candles", { symbol, timeframe }),
|
||||
indicators: (symbol: string, opts: IndicatorOptions) =>
|
||||
trpcQuery<IndicatorResult>("market.indicators", { symbol, ...opts }),
|
||||
truckSales: () => trpcQuery<TruckSalesData>("market.truckSales"),
|
||||
@@ -890,16 +913,23 @@ export const api = {
|
||||
list: () => trpcQuery<Array<{
|
||||
id: string; ci_key: string; fund_name: string; manager_name: string;
|
||||
x_handle: string | null; paywall_status: string; enabled: boolean;
|
||||
freshness: FundFreshness | null;
|
||||
}>>("funds.list", {}),
|
||||
get: (input: { id: string }) => trpcQuery<{
|
||||
id: string; ci_key: string; fund_name: string; manager_name: string;
|
||||
x_handle: string | null; paywall_status: string; enabled: boolean;
|
||||
freshness: FundFreshness | null;
|
||||
} | null>("funds.get", input),
|
||||
liveBook: (input: { fundId: string }) =>
|
||||
trpcQuery<Array<{
|
||||
symbol: string; shares: number | null; value_usd: number | null;
|
||||
cost_basis: number | null; as_of: string; source: string; evidence_url: string | null;
|
||||
notes: string | null; post_text: string | null;
|
||||
status: "held" | "exited"; last_held_shares: number | null;
|
||||
prior_shares: number | null; share_delta: number | null;
|
||||
change: "add" | "cut" | "flat" | "new" | null;
|
||||
x_only: boolean;
|
||||
evidence_status?: "ok" | "deleted" | "unknown";
|
||||
}>>("funds.liveBook", input),
|
||||
adminCreate: (input: {
|
||||
ci_key: string; fund_name: string; manager_name: string; x_handle?: string;
|
||||
@@ -928,7 +958,7 @@ export const api = {
|
||||
symbol: string;
|
||||
tracked: Array<{
|
||||
fundId: string; fundName: string; shares: number | null; valueUsd: number | null;
|
||||
asOf: string; source: string; weightPct: number | null;
|
||||
asOf: string; source: string; notes: string | null; weightPct: number | null;
|
||||
}>;
|
||||
institutionCount: number;
|
||||
}>("symbols.holders", input),
|
||||
@@ -1305,6 +1335,8 @@ export const api = {
|
||||
queueLogs: (key: string, limit: number) => trpcQuery<ErrorLogEntry[]>("admin.queueLogs", { key, limit }),
|
||||
queueRetryJob: (key: string) => trpcMutate<{ ok: boolean }>("admin.queueRetryJob", { key }),
|
||||
queueRetrySource: (sourceKind: string) => trpcMutate<{ cleared: number }>("admin.queueRetrySource", { sourceKind }),
|
||||
clearSourceCooldown: (sourceKind: string) =>
|
||||
trpcMutate<{ ok: boolean; sourceKind: string }>("admin.clearSourceCooldown", { sourceKind }),
|
||||
queueSourcePause: (source: string) => trpcMutate<{ ok: boolean; source: string }>("admin.queueSourcePause", { source }),
|
||||
queueSourceResume: (source: string) => trpcMutate<{ ok: boolean; source: string }>("admin.queueSourceResume", { source }),
|
||||
queueSourceStop: (source: string) => trpcMutate<{ ok: boolean; source: string; cleared: number }>("admin.queueSourceStop", { source }),
|
||||
@@ -1485,8 +1517,64 @@ export const api = {
|
||||
body: 'bull' | 'bear' | 'exit';
|
||||
firedAt: string;
|
||||
qualityAtFire: string;
|
||||
verdict: 'real' | 'false_alarm' | null;
|
||||
priceConfirmed: boolean;
|
||||
}>;
|
||||
}>("confluence.signalHistory", { symbol }),
|
||||
recentZones: (symbol: string, opts?: { rackId?: string; entries?: number; exits?: number }) =>
|
||||
trpcQuery<{
|
||||
symbol: string;
|
||||
rackId: string | null;
|
||||
entries: Array<{
|
||||
kind: 'entry' | 'exit';
|
||||
startAsOf: string;
|
||||
endAsOf: string;
|
||||
open: boolean;
|
||||
qualityAtStart: string;
|
||||
netEvidenceAtStart: number;
|
||||
firedSlotIds: string[];
|
||||
forwardReturn: number | null;
|
||||
verdict: 'confirmed' | 'false_alarm' | 'open';
|
||||
}>;
|
||||
exits: Array<{
|
||||
kind: 'entry' | 'exit';
|
||||
startAsOf: string;
|
||||
endAsOf: string;
|
||||
open: boolean;
|
||||
qualityAtStart: string;
|
||||
netEvidenceAtStart: number;
|
||||
firedSlotIds: string[];
|
||||
forwardReturn: number | null;
|
||||
verdict: 'confirmed' | 'false_alarm' | 'open';
|
||||
}>;
|
||||
entryRule: {
|
||||
label: string;
|
||||
source: string;
|
||||
derivedAt: string | null;
|
||||
sampleCaveat: string;
|
||||
train: { hitRate: number | null; resolved: number; baselineHitRate: number | null } | null;
|
||||
validate: { hitRate: number | null; resolved: number; baselineHitRate: number | null } | null;
|
||||
};
|
||||
exitRule: {
|
||||
label: string;
|
||||
source: string;
|
||||
derivedAt: string | null;
|
||||
sampleCaveat: string;
|
||||
train: { hitRate: number | null; resolved: number; baselineHitRate: number | null } | null;
|
||||
validate: { hitRate: number | null; resolved: number; baselineHitRate: number | null } | null;
|
||||
};
|
||||
coverage: { evaluatedDays: number; lookbackDays: number; replayComplete: boolean };
|
||||
symbolScore: {
|
||||
entry: { zones: number; resolved: number; confirmed: number; hitRate: number | null };
|
||||
exit: { zones: number; resolved: number; confirmed: number; hitRate: number | null };
|
||||
};
|
||||
learningLedger: { ready: boolean; filled: number; total: number };
|
||||
}>("confluence.recentZones", {
|
||||
symbol,
|
||||
...(opts?.rackId ? { rackId: opts.rackId } : {}),
|
||||
...(opts?.entries ? { entries: opts.entries } : {}),
|
||||
...(opts?.exits ? { exits: opts.exits } : {}),
|
||||
}),
|
||||
saveRack: (input: { id?: string; name: string; description?: string; slotIds: string[] }) =>
|
||||
trpcMutate<{ id: string; name: string; slotIds: string[]; isSystem: boolean; description: string | null }>("confluence.saveRack", input),
|
||||
corridorSnapshot: (symbol: string) =>
|
||||
@@ -1734,6 +1822,10 @@ export interface QueueStatus {
|
||||
pausedSources?: string[];
|
||||
/** Operator-stopped sources (drain frozen, jobs cleared, enqueue blocked). */
|
||||
stoppedSources?: string[];
|
||||
pendingByKind?: Record<string, number>;
|
||||
demandSize?: number;
|
||||
dataPlaneHealthy?: boolean;
|
||||
dataPlaneNotes?: string[];
|
||||
}
|
||||
|
||||
export interface ScheduleEntry {
|
||||
|
||||
Reference in New Issue
Block a user