/** Raw MCP transport function injected at construction. Internal to the senpi layer. */ export type McpTransport = (toolName: string, args: Record) => Promise; export interface SenpiClientConfig { apiKey?: string; mcpUrl?: string; clientName?: string; clientVersion?: string; } export type OrderDirection = 'LONG' | 'SHORT'; /** * Order execution type for opening positions. * - `MARKET`: Immediate fill at market price (taker fees). * - `LIMIT`: Fill at specified `limitPrice`; requires `limitPrice`, optional `timeInForce`. * - `FEE_OPTIMIZED_LIMIT`: Maker ALO (Add Liquidity Only); no `limitPrice`/`timeInForce`/`slippage` allowed. SL/TP allowed. */ export type OrderType = 'MARKET' | 'LIMIT' | 'FEE_OPTIMIZED_LIMIT'; export type LeverageType = 'CROSS' | 'ISOLATED'; export type TimeInForce = 'GTC' | 'IOC' | 'ALO'; export type ExitOrderType = 'MARKET' | 'LIMIT'; /** * Order execution type for closing positions. * - `MARKET`: Immediate close (default). * - `FEE_OPTIMIZED_LIMIT`: Reduce-only ALO maker. `LIMIT` is **not supported** for close (returns `CLOSE_LIMIT_NOT_SUPPORTED`). */ export type CloseOrderType = 'MARKET' | 'FEE_OPTIMIZED_LIMIT'; /** * Options for `FEE_OPTIMIZED_LIMIT` order execution. Only valid when `orderType` is `FEE_OPTIMIZED_LIMIT`; * must **not** be set for `MARKET` or `LIMIT` orders. * * Without `ensureExecutionAsTaker`, the order may stay resting indefinitely — * use `strategy_get_open_orders` or `cancel_order` to manage. */ export interface FeeOptimizedLimitOptions { /** When `true`, falls back to a market order if the maker order doesn't fill within the execution window. */ ensureExecutionAsTaker?: boolean; /** Override the server-default 45s ALO wait window (1–300 seconds). Set below 45 for faster fallback. */ executionTimeoutSeconds?: number; } export interface StopLoss { percentage?: number; price?: number; orderType?: ExitOrderType; } export interface TakeProfit { percentage?: number; price?: number; orderType?: ExitOrderType; } export interface CreatePositionOrder { coin: string; direction: OrderDirection; leverage: number; marginAmount: number; orderType: OrderType; leverageType?: LeverageType; limitPrice?: number; slippagePercent?: number; timeInForce?: TimeInForce; ensureExecutionAsTaker?: boolean; /** Only valid when `orderType` is `FEE_OPTIMIZED_LIMIT`. Ignored for `MARKET` and `LIMIT` orders. */ feeOptimizedLimitOptions?: FeeOptimizedLimitOptions; stopLoss?: StopLoss; takeProfit?: TakeProfit; } export interface CreatePositionRequest { strategyWalletAddress: string; orders: CreatePositionOrder[]; reason?: string; } export interface CreatePositionOrderResult { coin: string; direction: OrderDirection; leverage: number; marginAmount: number; entryPrice: number | null; size: number | null; filled: boolean; orderId: string | null; executionAsMaker: boolean | null; error: string | null; } export interface CreatePositionResult { success: boolean; orders: CreatePositionOrderResult[]; error: string | null; /** Structured MCP error code when the envelope error was object-shaped (e.g. STRATEGY_NOT_ACTIVE). */ errorCode?: string | null; /** The server's explicit retryable flag, when present. */ errorRetryable?: boolean | null; } /** Position data from clearing house; used when SZI/entryPx are not in env (e.g. live DSL run). */ export interface GetPositionResult { /** Size with sign: positive = LONG, negative = SHORT */ szi: number; /** Entry (or average) price */ entryPx: number; leverage?: number; } /** Minimal open position row derived from clearing house state (reconciliation / slots). */ export interface OpenPosition { wallet?: string; address?: string; coin: string; dex?: string; entryPx?: number; szi?: number; leverage?: number; } export interface EditPositionResult { actionsPerformed: string[]; ordersUpdated?: Record; executionAsMaker?: boolean; slOrderId?: number; } export interface OrderStatus { filled?: boolean; /** * The venue's order status, lowercased (Hyperliquid: `open`, `filled`, `canceled`, `triggered`, * `unknownoid`, …). `open` is the only status that means the order is resting on the book. * Absent when the response carried no status string. */ status?: string; } export interface GetStrategyResult { strategyId: string; name: string; status: string; traderAddress: string; /** * Total USDC funded into the strategy — `undefined` when the backend reported no readable figure * on this read. Quoted straight from {@link StrategySummary.totalFunded}, unread stays unread. * * NOT coerced to 0 here either: this figure reaches an agent through the `strategy: details` * context slice, and a `0` there is the same lie the deploy report's `[W_BUDGET_FUNDED_UNREADABLE]` * exists to prevent — an unread amount presented as an empty wallet. */ initialBudget: number | undefined; totalPnl: number; pnlPercentage: number; createdAt: string; } /** Filters accepted by {@link SenpiClient.listStrategies}. All fields are optional. */ export interface ListStrategiesFilters { /** Filter by strategy wallet addresses (Ethereum 0x addresses). */ strategyAddresses?: string[]; /** Filter by strategy status (e.g. ["ACTIVE", "PAUSED"]). */ status?: string[]; /** Filter by internal strategy UUIDs. */ strategyIds?: string[]; /** Filter by strategy type ("MIRROR" or "CUSTOM"). */ strategyType?: string; } /** Strategy metadata returned by {@link SenpiClient.listStrategies}. */ export interface StrategySummary { /** Internal strategy UUID. */ id: string; /** On-chain wallet address owned by the strategy (NOT the user's personal wallet). */ strategyWalletAddress: string; /** Trader address being copied (shortened by the server to 0x1234...abcd). */ traderAddress: string; /** Strategy lifecycle status (e.g. "ACTIVE", "PAUSED", "CLOSED", "FAILED"). */ status: string; /** "MIRROR" or "CUSTOM". */ strategyType: string; /** Human-readable strategy name. */ strategyName: string; /** ISO-8601 creation timestamp. */ createdAt: string; /** * Total USDC funded into the strategy — `undefined` when the backend did not report a readable * figure on this read. * * Deliberately NOT coerced to 0 by the client. An unreadable amount and a genuinely empty wallet * demand opposite next steps (verify before touching it vs top it up by the named shortfall), and * collapsing the two turned one bad read into a checkable-looking "$0.00 of $500.00" claim with a * money-moving instruction attached. Consumers that only need a number for a total or a display * may still `?? 0`; anything that instructs a money move must tell the two apart. */ totalFunded?: number; /** * The package/skill that created this strategy, from `strategyMetadata.skillName`. * * Scopes a set of live strategies to one package — enough to ask "are any of MY wallets live?", * never enough to tell one instance of a package from another (the stamp is the bare package id). * Deploy's fail-closed gate in `reconcileInstance` uses it for exactly that question. * * `undefined` means the backend reported no readable stamp — UNKNOWN, never "not ours". */ skillName?: string; } export interface GetOpenOrdersResult { orderId: number; coin: string; side: 'BUY' | 'SELL'; size: number; price: number; orderType: string; status: string; timestamp: number; } export interface GetTraderHistoryOptions { limit?: number; offset?: number; sort_by?: string; sort_direction?: string; } export interface TraderHistoryEntry { coin: string; direction: 'LONG' | 'SHORT'; entryPx: number; closePx: number; szi: number; realizedPnl: number; /** Unix epoch seconds (MCP `discovery_get_trader_history` / `closeTime`). */ closedTime: number; /** Unix epoch seconds (MCP `discovery_get_trader_history` / `openTime`). */ openTime: number; totalFees: number; } export interface TraderStateMarginSummary { accountValue?: string | number | null; totalMarginUsed?: string | number | null; totalNtlPos?: string | number | null; totalRawUsd?: string | number | null; } export interface TraderStateOpenPosition { coin: string; szi: string; entryPx: string | number; positionValue: string | number; unrealizedPnl: string | number; leverage: { type: string; value: number; }; marginUsed: string | number; returnOnEquity?: string | number | null; liquidationPx?: string | number | null; /** Unix seconds from ClickHouse. Present only when includePositionAge=true. */ startTime?: number | null; /** Seconds since position opened. Present only when includePositionAge=true. */ durationInSeconds?: number | null; } export interface TraderStateResult { address: string; openPositions: TraderStateOpenPosition[]; openOrders?: Array>; marginSummary: TraderStateMarginSummary; crossMarginSummary?: TraderStateMarginSummary | null; withdrawable?: string | number | null; marginPercentage?: number | null; } export interface GetTraderStateOptions { /** When true, open positions include startTime (seconds) and durationInSeconds. Default false. */ includePositionAge?: boolean; /** When true, bypass cache and fetch fresh data. Default true. */ latest?: boolean; } export interface StrategyFundResult { success: boolean; message?: string; /** * The refusal's own code from the MCP error envelope (`error.code` — `SERR055`, * `INVALID_ARGUMENT`, …), `undefined` when the answer carried none. * * Structured rather than folded into {@link message} because callers BRANCH on it: the create * retry guard fires on an enumerated set of codes and must never re-derive them by scanning * prose. See {@link CreateCustomStrategyResult.errorCode}. */ errorCode?: string; } export interface CreateCustomStrategyParams { initialBudget: number; strategyName?: string; /** * Attribution: strategies created without skillName/skillVersion are * unattributable downstream. `senpi deploy` stamps both from strategy.yaml. */ skillName: string; skillVersion: string; } export interface CreateCustomStrategyResult { success: boolean; strategyId?: string; message?: string; /** * The refusal's own code from the MCP error envelope, `undefined` when the answer carried none * (a success, a legacy flat failure, or a throw the caller shaped into a result). * * Load-bearing on the money path: `deploy`'s unnamed-create retry keys on this, so "the backend * refused the NAME" (retry is free) can be told from "the outcome is unknown" (a retry may fund * a second wallet) without pattern-matching a message written outside this repo. */ errorCode?: string; } /** Raw portfolio payload from account_get_portfolio — shape intentionally loose; funding.ts owns interpretation. */ export type PortfolioPayload = Record; export interface ClosePositionsRequest { coin: string; dex?: string; } export interface GetPnlHistoryOptions { period?: string; /** When false, drawdown PnL peak carries from pre-midnight (~24h). Default true resets at UTC midnight. */ drawdownResetOnDayRollover?: boolean; } export interface RatchetStopPosition { id: string; status: string; currentTierIndex: number; tierFloorPrice: number; highWaterPrice: number; highWaterRoe: number; activeSLOrderId: string; lastEvaluatedAt: string; lastPrice: number; } export interface RatchetStopEvent { eventType: string; details: Record; createdAt: string; } export interface AddRatchetStopInput { asset: string; strategyWalletAddress: string; strategyId: string; dex?: string; ratchetStopConfig: { tiered: { tiers: Array<{ triggerRoe: number; lockRoe: number; }>; }; }; direction?: 'LONG' | 'SHORT'; entryPrice?: number; size?: number; leverage?: number; } export interface EditRatchetStopInput { asset: string; strategyWalletAddress: string; strategyId: string; dex?: string; ratchetStopConfig?: { tiered: { tiers: Array<{ triggerRoe: number; lockRoe: number; }>; }; }; paused?: boolean; } export interface DeleteRatchetStopInput { asset: string; strategyWalletAddress: string; strategyId: string; dex?: string; cancelOrder?: boolean; } export interface QueryRatchetStopsInput { strategyWalletAddress: string; strategyId: string; asset?: string; dex?: string; status?: 'ACTIVE' | 'PAUSED' | 'DELETED' | 'ALL'; } export interface QueryRatchetStopEventsInput { strategyWalletAddress: string; strategyId: string; positionId?: string; since?: string; sortOrder?: 'ASC' | 'DESC'; } //# sourceMappingURL=types.d.ts.map