/** * Strategy cookbook: a grid. A drop-in strategy.ts for the Orblivion starter bot. * * It lays price levels STEP apart around a centre: LEVELS above it and LEVELS below. Each time ETH falls through a level * it buys ORDER_USD of ETH with USDG; each time it rises through one it sells ORDER_USD of ETH. It remembers the level it * last traded at (in state), so a sale needs ETH to climb a whole step above the last buy, and a price that wobbles * around one level doesn't trade on every tick. A jump through several levels in one tick trades them all in one order. * Under the lowest level it buys no more, and over the highest it sells no more. * * To use it, copy this file over the starter bot's strategy.ts and keep "dry_run": true in config.json until the log * shows what you expect. decide(market, book, state) gets what the starter bot's strategy.ts gets (prices, the wallet's * balances, and state kept in bot-state.json) and returns a list of orders. bot.ts cuts every order to your limits. * * The grid follows the price, not the wallet: when the wallet can't pay for a level, bot.ts skips the order (and logs * it), and the grid still moves to that level. A wallet with both ETH and USDG can trade both ways. * * An example to learn from, not advice: a price that leaves the grid leaves it holding all ETH (below) or none (above). */ export type Market = Record export type Holding = { units: number | null; usd: number | null } export type Book = { balances: Record; totalUsd: number } export type State = Record export type Order = { sell: string // the token to sell, e.g. 'ETH' buy: string // the token to buy, e.g. 'USDG' amountUsd: number // how many dollars of `sell` to trade reason?: string // why: shown in the console and bot-log.jsonl } const CENTRE = 0 // the grid's middle, in dollars per ETH; 0: the ETH price on the grid's first tick const STEP = 0.02 // levels 2% of the centre apart: wider steps trade less often const LEVELS = 3 // levels on each side of the centre const ORDER_USD = 5 // dollars traded at each level export function decide(market: Market, book: Book, state: State): Order[] { const s = (state.grid ??= {}) // this strategy's own corner of state const price = market.ETH if (price === undefined) return [] // no ETH price this tick: wait for the next one if (s.centre === undefined || (CENTRE && s.centre !== CENTRE)) { s.centre = CENTRE || price // a new grid (or CENTRE changed): start at its middle s.level = 0 } const centre: number = s.centre const start: number = s.level let level = start while (level > -LEVELS && price <= centre * (1 + (level - 1) * STEP)) level -= 1 // down through a level: buy while (level < LEVELS && price >= centre * (1 + (level + 1) * STEP)) level += 1 // up through a level: sell s.level = level const moved = level - start if (moved === 0) return [] const why = `ETH $${price.toFixed(2)}: from level ${start} to ${level} of a grid ${(STEP * 100).toFixed(0)}% apart around $${centre.toFixed(2)}` if (moved < 0) return [{ sell: 'USDG', buy: 'ETH', amountUsd: ORDER_USD * -moved, reason: why }] return [{ sell: 'ETH', buy: 'USDG', amountUsd: ORDER_USD * moved, reason: why }] }