Build · Optimizer

Coordinate the fleet.

The optimizer deploys bots to validators, pushes configuration down, and receives signals back. The design work is deciding what those messages carry.

Note

Reference repo: noncepad/optimizer

Designing your messages

Before writing any code, answer three questions:

  • What does the bot need to know? Thresholds, weights, target accounts, rate limits — anything the optimizer controls at runtime goes into AdjustConfiguration.
  • What does the bot report back? PnL, latency, price signals, fill rates — anything the optimizer needs to make decisions.
  • When does the optimizer act? What state change triggers a new configuration push?

These answers define your message interface. Bot side: message.rs and configuration.rs. Optimizer side: manager/bot/message.go.

Where to look in the code

manager/bot/message.go

Implement your inbound message handlers here. OnCustom is called every time the bot sends a custom outbound message:

go
func (in *internalBot) OnCustom(pair *catmsg.Pair) error {
    // pair.Key identifies the message type
    // pair.Value carries the payload
    // update your state here, push new config if needed
    return nil
}

manager/bot/bot.go

Use Bot.Send(msg) to push messages down to a bot, including AdjustConfiguration.

Example — PnL-based spend control

The bot reports trade results. The optimizer tracks rolling PnL per bot and adjusts max_spend_lamports in response.

Bot side (message.rs)

rust
pub enum CustomMessageOutbound {
    TradeResult { pnl: f64, slot: u64 },
}

Optimizer side (manager/bot/message.go)

go
func (in *internalBot) OnCustom(pair *catmsg.Pair) error {
    // decode pair.Value into TradeResult
    // update rolling PnL tracker
    // if PnL below threshold, call Bot.Send with new AdjustConfiguration
    return nil
}

When the bot's PnL drops below the threshold, the optimizer pushes a new Configuration with max_spend_lamports: 0 to pause it. When performance recovers, it opens the bot back up.