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 sources16d ago| #1 | export type Side = "long" | "short"; |
| #2 | |
| #3 | export interface Position { |
| #4 | id: string; |
| #5 | side: Side; |
| #6 | entry_price: number; |
| #7 | size_lamports: number; |
| #8 | opened_at_tick: number; |
| #9 | opened_at: string; |
| #10 | } |
| #11 | |
| #12 | export interface Book { |
| #13 | positions: Position[]; |
| #14 | cash_lamports: number; |
| #15 | } |
| #16 | |
| #17 | export interface Candle { |
| #18 | t: string; |
| #19 | o: number; |
| #20 | h: number; |
| #21 | l: number; |
| #22 | c: number; |
| #23 | v: number; |
| #24 | } |
| #25 | |
| #26 | export interface State { |
| #27 | tick: number; |
| #28 | book: Book; |
| #29 | candles: Candle[]; |
| #30 | consecutive_losses: number; |
| #31 | total_pnl_lamports: number; |
| #32 | total_trades: number; |
| #33 | } |
| #34 | |
| #35 | export function createState(startingCash = 10_000_000): State { |
| #36 | return { |
| #37 | tick: 0, |
| #38 | book: { positions: [], cash_lamports: startingCash }, |
| #39 | candles: [], |
| #40 | consecutive_losses: 0, |
| #41 | total_pnl_lamports: 0, |
| #42 | total_trades: 0, |
| #43 | }; |
| #44 | } |
| #45 | |
| #46 | export function openPosition(state: State, side: Side, size_lamports: number, currentPrice: number): Position { |
| #47 | const pos: Position = { |
| #48 | id: `pos-${state.tick}-${Date.now()}`, |
| #49 | side, |
| #50 | entry_price: currentPrice, |
| #51 | size_lamports, |
| #52 | opened_at_tick: state.tick, |
| #53 | opened_at: new Date().toISOString(), |
| #54 | }; |
| #55 | state.book.positions.push(pos); |
| #56 | state.book.cash_lamports -= size_lamports; |
| #57 | return pos; |
| #58 | } |
| #59 | |
| #60 | export function closePosition(state: State, positionId: string, currentPrice: number): number { |
| #61 | const idx = state.book.positions.findIndex((position) => position.id === positionId); |
| #62 | if (idx === -1) throw new Error(`position ${positionId} not found`); |
| #63 | const pos = state.book.positions[idx]!; |
| #64 | const priceDelta = currentPrice - pos.entry_price; |
| #65 | const units = pos.size_lamports / pos.entry_price; |
| #66 | const rawPnl = pos.side === "long" ? units * priceDelta : units * -priceDelta; |
| #67 | const pnl = Math.round(rawPnl); |
| #68 | |
| #69 | state.book.positions.splice(idx, 1); |
| #70 | state.book.cash_lamports += pos.size_lamports + pnl; |
| #71 | state.total_pnl_lamports += pnl; |
| #72 | state.total_trades += 1; |
| #73 | state.consecutive_losses = pnl < 0 ? state.consecutive_losses + 1 : 0; |
| #74 | return pnl; |
| #75 | } |
| #76 | |
| #77 | export function unrealisedPnl(state: State, currentPrice: number): number { |
| #78 | return state.book.positions.reduce((sum, pos) => { |
| #79 | const units = pos.size_lamports / pos.entry_price; |
| #80 | const delta = currentPrice - pos.entry_price; |
| #81 | const pnl = pos.side === "long" ? units * delta : units * -delta; |
| #82 | return sum + pnl; |
| #83 | }, 0); |
| #84 | } |
| #85 | |
| #86 |