145 lines
4.6 KiB
TypeScript
145 lines
4.6 KiB
TypeScript
// GammaDesk shared data model
|
|
// ORATS-style options analytics types + Drizzle tables for auth.
|
|
|
|
import { z } from "zod";
|
|
import { sqliteTable, text, integer, real } from "drizzle-orm/sqlite-core";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Auth - Users table (Drizzle)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const users = sqliteTable("users", {
|
|
id: integer("id").primaryKey({ autoIncrement: true }),
|
|
name: text("name").notNull(),
|
|
email: text("email").notNull().unique(),
|
|
passwordHash: text("password_hash").notNull(),
|
|
createdAt: integer("created_at", { mode: "timestamp" }).$defaultFn(() => new Date()),
|
|
});
|
|
|
|
export type User = typeof users.$inferSelect;
|
|
export type NewUser = typeof users.$inferInsert;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Symbols
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const symbolSchema = z.object({
|
|
ticker: z.string(),
|
|
name: z.string(),
|
|
type: z.enum(["index", "etf", "equity"]),
|
|
sector: z.string().optional(),
|
|
});
|
|
|
|
export type SymbolInfo = z.infer<typeof symbolSchema>;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Summary card payload — what the Dashboard top row shows
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const summarySchema = z.object({
|
|
ticker: z.string(),
|
|
spot: z.number(),
|
|
spotChange: z.number(), // absolute $ change vs prior close
|
|
spotChangePct: z.number(),
|
|
netGex: z.number(), // signed, in $ per 1% move
|
|
gammaRegime: z.enum(["positive", "negative"]),
|
|
hvl: z.number(), // High Volume Level / gamma flip
|
|
callWall: z.number(),
|
|
putWall: z.number(),
|
|
ivRank: z.number(), // 0-100
|
|
ivx: z.number(), // implied vol index, percent
|
|
expectedMove: z.number(), // absolute $ expected move next session
|
|
expectedMovePct: z.number(),
|
|
skew: z.number(), // put skew minus call skew, percent points
|
|
asOf: z.string(), // ISO timestamp
|
|
});
|
|
|
|
export type Summary = z.infer<typeof summarySchema>;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// GEX profile — bars by strike for the gamma-by-strike visualization
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const gexBarSchema = z.object({
|
|
strike: z.number(),
|
|
callGex: z.number(), // $ gamma exposure attributable to call OI
|
|
putGex: z.number(), // negative or positive depending on sign convention
|
|
netGex: z.number(),
|
|
});
|
|
|
|
export const gexProfileSchema = z.object({
|
|
ticker: z.string(),
|
|
spot: z.number(),
|
|
hvl: z.number(),
|
|
callWall: z.number(),
|
|
putWall: z.number(),
|
|
bars: z.array(gexBarSchema),
|
|
asOf: z.string(),
|
|
});
|
|
|
|
export type GexBar = z.infer<typeof gexBarSchema>;
|
|
export type GexProfile = z.infer<typeof gexProfileSchema>;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Expirations matrix
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const expirationRowSchema = z.object({
|
|
expiry: z.string(), // ISO date
|
|
dte: z.number(),
|
|
netGex: z.number(),
|
|
ivx: z.number(),
|
|
skew: z.number(),
|
|
expectedMove: z.number(),
|
|
expectedMovePct: z.number(),
|
|
callWall: z.number(),
|
|
putWall: z.number(),
|
|
callSkew: z.number(), // for IV/skew curve chart
|
|
putSkew: z.number(),
|
|
});
|
|
|
|
export const expirationsResponseSchema = z.object({
|
|
ticker: z.string(),
|
|
spot: z.number(),
|
|
rows: z.array(expirationRowSchema),
|
|
asOf: z.string(),
|
|
});
|
|
|
|
export type ExpirationRow = z.infer<typeof expirationRowSchema>;
|
|
export type ExpirationsResponse = z.infer<typeof expirationsResponseSchema>;
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Screener
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export const screenerPresetSchema = z.enum([
|
|
"all",
|
|
"negative-gamma",
|
|
"high-iv-rank",
|
|
"near-call-wall",
|
|
"near-put-wall",
|
|
"zero-dte",
|
|
]);
|
|
|
|
export type ScreenerPreset = z.infer<typeof screenerPresetSchema>;
|
|
|
|
export const screenerRowSchema = z.object({
|
|
ticker: z.string(),
|
|
name: z.string(),
|
|
spot: z.number(),
|
|
spotChangePct: z.number(),
|
|
netGex: z.number(),
|
|
gammaRegime: z.enum(["positive", "negative"]),
|
|
ivRank: z.number(),
|
|
ivx: z.number(),
|
|
callWall: z.number(),
|
|
putWall: z.number(),
|
|
distanceToCallWall: z.number(), // % away from spot (signed: + if wall above)
|
|
distanceToPutWall: z.number(),
|
|
expectedMovePct: z.number(),
|
|
skew: z.number(),
|
|
zeroDteNetGex: z.number(),
|
|
});
|
|
|
|
export type ScreenerRow = z.infer<typeof screenerRowSchema>;
|