cycle.ts
import { LiquidityLocked, type Zeus } from '@zeustrade/sdk'
export interface CycleStep {
step: string
detail: Record<string, unknown>
}
const plain = (value: unknown) => JSON.parse(JSON.stringify(value, (_k, v) => (typeof v === 'bigint' ? v.toString() : v)))
export async function runCycle(zeus: Zeus, log: (step: CycleStep) => void, market = 'ETH/USD'): Promise<CycleStep[]> {
const steps: CycleStep[] = []
const record = (step: string, detail: unknown) => {
const entry = { step, detail: plain(detail) }
steps.push(entry)
log(entry)
}
const status = await zeus.setup.status()
if (!status.ready) record('approvePlugins', { hashes: await zeus.setup.approvePlugins() })
if ((await zeus.setup.allowance('USDC')) < 1_000_000_000n) record('approveToken trading', { hash: await zeus.setup.approveToken('USDC') })
if ((await zeus.setup.allowance('USDC', 'liquidity')) < 1_000_000_000n) {
record('approveToken liquidity', { hash: await zeus.setup.approveToken('USDC', undefined, 'liquidity') })
}
const open = await zeus.trade.marketOpen({ market, side: 'long', collateral: 20, payToken: 'USDC', leverage: 5, slippage: 0.5 })
record('marketOpen', open)
const fill = Number(open.fillPrice)
const takeProfit = await zeus.orders.takeProfit({ market, side: 'long', triggerPrice: (fill * 1.05).toFixed(2) })
record('takeProfit', takeProfit)
const stopLoss = await zeus.orders.stopLoss({ market, side: 'long', triggerPrice: (fill * 0.95).toFixed(2) })
record('stopLoss', stopLoss)
const [position] = (await zeus.account.positions()).filter((p) => p.market === market && p.side === 'long')
record('position', position)
const half = (Number(position!.size) / 2).toFixed(2)
// Closes take the collateral back as ETH: a swap into USDC can hit the Vault's USDC buffer
record('partialClose', await zeus.trade.marketClose({ market, side: 'long', size: half, receiveToken: 'ETH', slippage: 0.5 }))
record('fullClose', await zeus.trade.marketClose({ market, side: 'long', receiveToken: 'ETH', slippage: 0.5 }))
for (const order of [takeProfit, stopLoss]) {
await zeus.orders.cancel({ kind: order.kind, orderIndex: order.orderIndex }).then(
(hash) => record('cancelOrder', { orderIndex: order.orderIndex, hash }),
(error: Error) => record('cancelOrder', { orderIndex: order.orderIndex, skipped: error.message })
)
}
const deposit = await zeus.lp.deposit({ token: 'USDC', amount: 10, minZlp: 0 })
record('lpDeposit', deposit)
// Fresh ZLP stays locked for the deposit cooldown, so the request may have to wait
await zeus.lp.requestWithdrawal({ zlpAmount: deposit.zlpAmount }).then(
(hash) => record('requestWithdrawal', { hash }),
(error: unknown) => {
if (!(error instanceof LiquidityLocked)) throw error
record('requestWithdrawal', { lockedUntilCooldown: true })
}
)
return steps
}
bot.ts
import { Zeus } from '@zeustrade/sdk'
import { runCycle } from './cycle'
const zeus = new Zeus({
network: 'base-sepolia',
rpcUrl: 'https://sepolia.base.org',
privateKey: process.env.PRIVATE_KEY as `0x${string}`
})
await runCycle(zeus, ({ step, detail }) => console.log(step, JSON.stringify(detail)))