UNPKG

@sudowealth/schwab-api

Version:

TypeScript client for Charles Schwab API with OAuth support, market data, trading functionality, and complete type safety

274 lines (273 loc) 11.7 kB
import { z } from 'zod'; import { mergeShapes } from '../../utils/schema-utils'; import { assetType, AccountAPIOptionDeliverable } from '../shared'; export const AccountsBaseInstrument = z.object({ assetType: assetType, cusip: z.string().optional(), // Made optional based on some TransactionInstrument variations symbol: z.string(), description: z.string().optional(), instrumentId: z.number().int().optional(), netChange: z.number().optional(), }); const AccountCashEquivalent = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.CASH_EQUIVALENT), type: z .enum(['SWEEP_VEHICLE', 'SAVINGS', 'MONEY_MARKET_FUND', 'UNKNOWN']) .optional(), underlyingSymbol: z.string().optional(), }); const AccountEquity = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.EQUITY), }); const AccountFixedIncome = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.FIXED_INCOME), maturityDate: z.string().datetime().optional(), factor: z.number().optional(), variableRate: z.number().optional(), }); const AccountMutualFund = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.MUTUAL_FUND), }); const AccountOption = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.OPTION), optionDeliverables: z .array(z.lazy(() => AccountAPIOptionDeliverable)) .optional(), putCall: z.enum(['PUT', 'CALL', 'UNKNOWN']).optional(), optionMultiplier: z.number().int().optional(), type: z.enum(['VANILLA', 'BINARY', 'BARRIER', 'UNKNOWN']).optional(), underlyingSymbol: z.string().optional(), // Added as it's common for options }); const AccountFuture = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.FUTURE), expirationDate: z.string().datetime().optional(), activeContract: z.boolean().default(false).optional(), }); const AccountForex = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.FOREX), }); const AccountIndex = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.INDEX), }); const AccountProduct = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.PRODUCT), // Assuming PRODUCT is in AssetType enum }); const AccountCurrency = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.CURRENCY), }); const AccountCollectiveInvestment = AccountsBaseInstrument.extend({ assetType: z.literal(assetType.Enum.COLLECTIVE_INVESTMENT), }); const AccountsInstrument = z.discriminatedUnion('assetType', [ AccountCashEquivalent, AccountEquity, AccountFixedIncome, AccountMutualFund, AccountOption, AccountFuture, AccountForex, AccountIndex, AccountProduct, AccountCurrency, AccountCollectiveInvestment, ]); // --- Position Schema --- const Position = z.object({ shortQuantity: z.number().optional(), averagePrice: z.number().optional(), currentDayProfitLoss: z.number().optional(), currentDayProfitLossPercentage: z.number().optional(), longQuantity: z.number().optional(), settledLongQuantity: z.number().optional(), settledShortQuantity: z.number().optional(), agedQuantity: z.number().optional(), instrument: z.lazy(() => AccountsInstrument), // Defined above marketValue: z.number().optional(), maintenanceRequirement: z.number().optional(), averageLongPrice: z.number().optional(), averageShortPrice: z.number().optional(), taxLotAverageLongPrice: z.number().optional(), taxLotAverageShortPrice: z.number().optional(), longOpenProfitLoss: z.number().optional(), shortOpenProfitLoss: z.number().optional(), previousSessionLongQuantity: z.number().optional(), previousSessionShortQuantity: z.number().optional(), currentDayCost: z.number().optional(), }); // --- Account Balance Schemas --- const MarginInitialBalance = z.object({ accruedInterest: z.number().optional(), availableFundsNonMarginableTrade: z.number().optional(), bondValue: z.number().optional(), buyingPower: z.number().optional(), cashBalance: z.number().optional(), cashAvailableForTrading: z.number().optional(), cashReceipts: z.number().optional(), dayTradingBuyingPower: z.number().optional(), dayTradingBuyingPowerCall: z.number().optional(), dayTradingEquityCall: z.number().optional(), equity: z.number().optional(), equityPercentage: z.number().optional(), liquidationValue: z.number().optional(), longMarginValue: z.number().optional(), longOptionMarketValue: z.number().optional(), longStockValue: z.number().optional(), maintenanceCall: z.number().optional(), maintenanceRequirement: z.number().optional(), margin: z.number().optional(), marginEquity: z.number().optional(), moneyMarketFund: z.number().optional(), mutualFundValue: z.number().optional(), regTCall: z.number().optional(), shortMarginValue: z.number().optional(), shortOptionMarketValue: z.number().optional(), shortStockValue: z.number().optional(), totalCash: z.number().optional(), isInCall: z.boolean().optional(), unsettledCash: z.number().optional(), pendingDeposits: z.number().optional(), marginBalance: z.number().optional(), shortBalance: z.number().optional(), accountValue: z.number().optional(), }); const MarginBalance = z.object({ availableFunds: z.number().optional(), availableFundsNonMarginableTrade: z.number().optional(), buyingPower: z.number().optional(), buyingPowerNonMarginableTrade: z.number().optional(), dayTradingBuyingPower: z.number().optional(), dayTradingBuyingPowerCall: z.number().optional(), equity: z.number().optional(), equityPercentage: z.number().optional(), longMarginValue: z.number().optional(), maintenanceCall: z.number().optional(), maintenanceRequirement: z.number().optional(), marginBalance: z.number().optional(), regTCall: z.number().optional(), shortBalance: z.number().optional(), shortMarginValue: z.number().optional(), sma: z.number().optional(), isInCall: z.boolean().optional(), stockBuyingPower: z.number().optional(), optionBuyingPower: z.number().optional(), }); const CashInitialBalance = z.object({ accruedInterest: z.number().optional(), cashAvailableForTrading: z.number().optional(), cashAvailableForWithdrawal: z.number().optional(), cashBalance: z.number().optional(), bondValue: z.number().optional(), cashReceipts: z.number().optional(), liquidationValue: z.number().optional(), longOptionMarketValue: z.number().optional(), longStockValue: z.number().optional(), moneyMarketFund: z.number().optional(), mutualFundValue: z.number().optional(), shortOptionMarketValue: z.number().optional(), shortStockValue: z.number().optional(), isInCall: z.boolean().optional(), unsettledCash: z.number().optional(), cashDebitCallValue: z.number().optional(), pendingDeposits: z.number().optional(), accountValue: z.number().optional(), }); const CashBalance = z.object({ cashAvailableForTrading: z.number().optional(), cashAvailableForWithdrawal: z.number().optional(), cashCall: z.number().optional(), longNonMarginableMarketValue: z.number().optional(), totalCash: z.number().optional(), cashDebitCallValue: z.number().optional(), unsettledCash: z.number().optional(), accruedInterest: z.number().optional(), cashBalance: z.number().optional(), cashReceipts: z.number().optional(), longOptionMarketValue: z.number().optional(), liquidationValue: z.number().optional(), longMarketValue: z.number().optional(), moneyMarketFund: z.number().optional(), savings: z.number().optional(), shortMarketValue: z.number().optional(), pendingDeposits: z.number().optional(), mutualFundValue: z.number().optional(), bondValue: z.number().optional(), shortOptionMarketValue: z.number().optional(), }); // --- Account Core Schemas --- const SecuritiesAccountBase = z.object({ type: z.string(), // Will be 'MARGIN' or 'CASH' in extending types accountNumber: z.string(), roundTrips: z.number().int().optional(), isDayTrader: z.boolean().default(false).optional(), isClosingOnlyRestricted: z.boolean().default(false).optional(), pfcbFlag: z.boolean().default(false).optional(), positions: z.array(Position).default([]).optional(), }); const MarginAccount = SecuritiesAccountBase.extend({ type: z.literal('MARGIN'), initialBalances: MarginInitialBalance.optional(), currentBalances: MarginBalance.optional(), projectedBalances: MarginBalance.optional(), // MCP used MarginBalance here }); const CashAccount = SecuritiesAccountBase.extend({ type: z.literal('CASH'), initialBalances: CashInitialBalance.optional(), currentBalances: CashBalance.optional(), projectedBalances: z .object({ // MCP had a specific structure here cashAvailableForTrading: z.number().optional(), cashAvailableForWithdrawal: z.number().optional(), }) .optional(), }); const SecuritiesAccount = z.discriminatedUnion('type', [ MarginAccount, CashAccount, ]); // --- GET /accounts endpoint schemas --- // Path Parameters Schema for GET /accounts (no path params) export const GetAccountsPathParams = z.object({}); // Query Parameters Schema for GET /accounts export const GetAccountsQueryParams = z.object({ fields: z .string() .optional() .describe('A comma-separated string of fields to return for each account. Valid values include: positions'), }); // Request Params Schema for GET /accounts (merged path + query params) export const GetAccountsParams = z.object(mergeShapes(GetAccountsQueryParams.shape, GetAccountsPathParams.shape)); // Response Body Schema for GET /accounts export const GetAccountsResponse = z.array(z.object({ securitiesAccount: SecuritiesAccount, })); // --- GET /accounts/{accountNumber} endpoint schemas --- // Path Parameters Schema for GET /accounts/{accountNumber} export const GetAccountByNumberPathParams = z.object({ accountNumber: z.string().describe('Encrypted account number'), }); // Query Parameters Schema for GET /accounts/{accountNumber} export const GetAccountByNumberQueryParams = z.object({ fields: z .string() .optional() .describe('A comma-separated string of fields to return. Valid values include: positions'), }); // Request Params Schema for GET /accounts/{accountNumber} (merged path + query params) export const GetAccountByNumberParams = z.object(mergeShapes(GetAccountByNumberQueryParams.shape, GetAccountByNumberPathParams.shape)); // Response Body Schema for GET /accounts/{accountNumber} export const GetAccountByNumberResponse = z.object({ securitiesAccount: SecuritiesAccount, }); // --- GET /accountNumbers endpoint schemas --- // Path Parameters Schema for GET /accountNumbers (no path params) export const GetAccountNumbersPathParams = z.object({}); // Query Parameters Schema for GET /accountNumbers (no query params) export const GetAccountNumbersQueryParams = z.object({}); // Request Params Schema for GET /accountNumbers (merged path + query params) export const GetAccountNumbersParams = z.object(mergeShapes(GetAccountNumbersQueryParams.shape, GetAccountNumbersPathParams.shape)); // Response Body Schema for GET /accountNumbers export const GetAccountNumbersResponse = z.array(z.object({ accountNumber: z.string().describe('Encrypted account number'), hashValue: z.string().describe('Hash value for the account'), }));