repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources15d ago| #1 | /** |
| #2 | * ooda/state.ts — Position book + paper PnL accounting |
| #3 | * |
| #4 | * This is the authoritative in-memory state for one loop run. |
| #5 | * Persistence lives in journal/ticks.jsonl (append-only). |
| #6 | * State is reconstructed from scratch on restart by replaying the journal. |
| #7 | */ |
| #8 | |
| #9 | export type Side = 'long' | 'short'; |
| #10 | |
| #11 | export interface Position { |
| #12 | id: string; |
| #13 | side: Side; |
| #14 | /** Entry price in lamports-per-unit */ |
| #15 | entry_price: number; |
| #16 | /** Size in lamports */ |
| #17 | size_lamports: number; |
| #18 | /** Tick number when opened */ |
| #19 | opened_at_tick: number; |
| #20 | /** ISO timestamp when opened */ |
| #21 | opened_at: string; |
| #22 | } |
| #23 | |
| #24 | export interface Book { |
| #25 | positions: Position[]; |
| #26 | cash_lamports: number; |
| #27 | } |
| #28 | |
| #29 | export interface Candle { |
| #30 | t: string; // ISO-8601 |
| #31 | o: number; // open price (lamports per unit, normalised) |
| #32 | h: number; |
| #33 | l: number; |
| #34 | c: number; // close price |
| #35 | v: number; // volume |
| #36 | } |
| #37 | |
| #38 | export interface State { |
| #39 | tick: number; |
| #40 | book: Book; |
| #41 | candles: Candle[]; |
| #42 | consecutive_losses: number; |
| #43 | total_pnl_lamports: number; |
| #44 | total_trades: number; |
| #45 | } |
| #46 | |
| #47 | export function createState(startingCash = 10_000_000): State { |
| #48 | return { |
| #49 | tick: 0, |
| #50 | book: { positions: [], cash_lamports: startingCash }, |
| #51 | candles: [], |
| #52 | consecutive_losses: 0, |
| #53 | total_pnl_lamports: 0, |
| #54 | total_trades: 0, |
| #55 | }; |
| #56 | } |
| #57 | |
| #58 | export function openPosition(state: State, side: Side, size_lamports: number, currentPrice: number): Position { |
| #59 | const id = `pos-${state.tick}-${Date.now()}`; |
| #60 | const pos: Position = { |
| #61 | id, |
| #62 | side, |
| #63 | entry_price: currentPrice, |
| #64 | size_lamports, |
| #65 | opened_at_tick: state.tick, |
| #66 | opened_at: new Date().toISOString(), |
| #67 | }; |
| #68 | state.book.positions.push(pos); |
| #69 | state.book.cash_lamports -= size_lamports; |
| #70 | return pos; |
| #71 | } |
| #72 | |
| #73 | export function closePosition(state: State, positionId: string, currentPrice: number): number { |
| #74 | const idx = state.book.positions.findIndex(p => p.id === positionId); |
| #75 | if (idx === -1) throw new Error(`position ${positionId} not found`); |
| #76 | const pos = state.book.positions[idx]!; |
| #77 | |
| #78 | // PnL: for long, profit if price rose; for short, profit if price fell |
| #79 | const priceDelta = currentPrice - pos.entry_price; |
| #80 | const units = pos.size_lamports / pos.entry_price; |
| #81 | const rawPnl = pos.side === 'long' |
| #82 | ? units * priceDelta |
| #83 | : units * -priceDelta; |
| #84 | const pnl = Math.round(rawPnl); |
| #85 | |
| #86 | state.book.positions.splice(idx, 1); |
| #87 | state.book.cash_lamports += pos.size_lamports + pnl; |
| #88 | state.total_pnl_lamports += pnl; |
| #89 | state.total_trades += 1; |
| #90 | |
| #91 | if (pnl < 0) { |
| #92 | state.consecutive_losses += 1; |
| #93 | } else { |
| #94 | state.consecutive_losses = 0; |
| #95 | } |
| #96 | |
| #97 | return pnl; |
| #98 | } |
| #99 | |
| #100 | /** Unrealised PnL for open positions at current price */ |
| #101 | export function unrealisedPnl(state: State, currentPrice: number): number { |
| #102 | return state.book.positions.reduce((sum, pos) => { |
| #103 | const units = pos.size_lamports / pos.entry_price; |
| #104 | const delta = currentPrice - pos.entry_price; |
| #105 | const pnl = pos.side === 'long' ? units * delta : units * -delta; |
| #106 | return sum + pnl; |
| #107 | }, 0); |
| #108 | } |
| #109 |