multimodelv1

package
v0.4.23 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

loopInstance runs in its own goroutine after Init completes. It subscribes to the bot's stdout stream and dispatches incoming messages:

  • KeyFlagCommonAccountUsage: parses and persists the bot's reported account-usage tally into prefetch.db's account_usage table (see optimizer/prefetch/alt), same as every other bot mode.
  • KeyFlagResidualSnapshotReport: persists one mint's real residual/ z-score warm-up state (prefetch/multimodel.SaveResidualSnapshot -- the residual data itself is opaque, not parsed here; only the mint is extracted, to key the row) so cmd/multimodel.go's pushResidualSnapshots can replay it back at the next boot. See message.go's doc comment and catscope-rust-bot's src/trader/residual_snapshot.rs.

Deliberately does NOT send a DoEchoRequest liveness ping the way every other mode's loopInstance does (leveragedloopv1/testperpv1's own instance.go): that ping's key flag (1) collides here with a real, load-bearing one -- catscope-rust-bot's src/brain/multimodelv1/message.rs maps key flag 1 to CUSTOM_KEY_FLAG_REPLAY_OPEN_INTENT (Phase 4's still-unwired intent replay), whose deserializer hard-errors on a payload that isn't a real OpenIntent record, instead of silently falling through to Blank the way an unmatched key flag does on every other mode. Sending the template's echo ping here would corrupt that parse path. Not fixed by renumbering the Rust side (out of scope for this pass, and Phase 4's wire protocol is otherwise already load-bearing); the echo ping itself is a diagnostic nicety, not required for the "connects, subscribes, idles cleanly" milestone this package targets, so it's dropped instead. No LST-yield pushing either (leveragedloopv1/testperpv1's own pushLstApyEstimates) -- this mode's Rust side has no use for it yet. The loop exits and cancels the context if the bot process closes or any framing error occurs.

Package multimodelv1 is the Go-side orchestrator for multimodelv1 -- catscope-rust-bot's src/brain/multimodelv1/PLAN-1.md Phase 5. A direct copy of package leveragedloopv1's orchestration shape (same allocation/upload/handshake/wallet-init flow, only MODE=multimodelv1 selected at upload time).

It implements the brain.Brain interface and is responsible for:

  • Allocating a slot on a Catscope/Solpipe validator pipeline (bid = 0, free tier).
  • Uploading a WASM bot image to the validator with MODE=multimodelv1.
  • Completing the handshake with the running bot instance.
  • Sending a child keypair to the bot via stdin -- this mode's Rust side (catscope-rust-bot/src/brain/multimodelv1) uses it to subscribe to its own wallet balances; it does not sign or send any real transaction yet.
  • Sending TriggerEnableFactorLogging on request (sub-phase 5b) -- opts the bot into a real-but-read-only periodic factor-graph resync/logging cycle; opens/closes nothing.

Sub-phase 5a shipped with zero SendTrigger* methods on Hook (nothing to trigger yet); sub-phase 5b adds the first one, SendTriggerEnableFactorLogging. Add more alongside the corresponding Rust-side CustomMessageInbound variant as PLAN-1.md's later sub-phases land real decision/execution logic.

Real, live-confirmed gap this file closes (2026-09-08): Evaluate fires on every solpipe state update but never looked at the trading child's own token balances or Solend/Kamino obligation state -- token balances were technically reachable via SolpipeState.TokenByOwner but nothing called it, and obligation account state (deposits/borrows) had no path into this package at all. This wires both in: obligations get a persistent subscription on the same graph.Graph the wallet itself already subscribes through (see initWallet), so reading them here is a pure in-memory lookup -- no new RPC round trip, no new subscription per call, same EdgeManager-read pattern cmd/watchobligations.go uses.

Index

Constants

View Source
const (
	KeyFlagTriggerEnableFactorLogging uint8 = 2
	KeyFlagWallet                     uint8 = 3
	KeyFlagTriggerEnablePairTrading   uint8 = 4
	// KeyFlagReplayResidualSnapshot (Go→bot) must match catscope-rust-bot's
	// src/brain/multimodelv1/message.rs CUSTOM_KEY_FLAG_REPLAY_RESIDUAL_SNAPSHOT.
	KeyFlagReplayResidualSnapshot uint8 = 5
	// KeyFlagResidualSnapshotReport (bot→Go) must match catscope-rust-bot's
	// CUSTOM_KEY_FLAG_RESIDUAL_SNAPSHOT_REPORT -- a different namespace
	// than the Go→bot flags above, see the doc comment above.
	KeyFlagResidualSnapshotReport uint8 = 2
	// KeyFlagTriggerEnableDirectionalTrading (Go→bot) must match
	// catscope-rust-bot's CUSTOM_KEY_FLAG_TRIGGER_ENABLE_DIRECTIONAL_TRADING
	// -- trade type 1 (directional factor-neutral). Unlike every other
	// trigger in this file, the value is a real 32-byte mint pubkey, not
	// an empty payload: entry itself is human-specified (see
	// catscope-rust-bot's PLAN-1.md directional-neutral design notes for
	// why), so the human's chosen long target has to travel over the
	// wire.
	KeyFlagTriggerEnableDirectionalTrading uint8 = 6
	// KeyFlagTriggerCloseDirectionalPosition (Go→bot) must match
	// catscope-rust-bot's CUSTOM_KEY_FLAG_TRIGGER_CLOSE_DIRECTIONAL_POSITION
	// -- empty payload; explicit human request to close whatever
	// directional position is currently open (or pending open). Trade
	// type 1's stop-loss/borrow-gate closes are automatic; "I'm
	// satisfied, take profit" isn't, since there's no computed
	// take-profit target for a directional bet.
	KeyFlagTriggerCloseDirectionalPosition uint8 = 7
	// KeyFlagTriggerEnableDispersionTrading (Go→bot) must match
	// catscope-rust-bot's CUSTOM_KEY_FLAG_TRIGGER_ENABLE_DISPERSION_TRADING
	// -- trade type 3 (dispersion). Empty payload, unlike trade type 1's
	// enable trigger: entry itself is automated (a real, computed
	// idiosyncratic-volatility signal), this only arms the automated
	// decision loop.
	KeyFlagTriggerEnableDispersionTrading uint8 = 8
	// KeyFlagTriggerCloseDispersionPosition (Go→bot) must match
	// catscope-rust-bot's CUSTOM_KEY_FLAG_TRIGGER_CLOSE_DISPERSION_POSITION
	// -- empty payload; explicit human request to close whatever
	// dispersion position is currently open. The automated exit signal
	// handles ordinary reversion; this is the human override.
	KeyFlagTriggerCloseDispersionPosition uint8 = 9
	// KeyFlagTriggerSweepMint (Go→bot) must match catscope-rust-bot's
	// CUSTOM_KEY_FLAG_TRIGGER_SWEEP_MINT -- temporary, standalone manual
	// cleanup tool (2026-09-03), not tied to any of the four trade
	// types' own gates. Carries two real 32-byte mint pubkeys (source
	// then destination): the bot sweeps its entire real balance of the
	// source mint to the destination mint via the same real
	// execute_spot_leg path every other real send in this mode uses.
	// Destination isn't hardcoded to mint_usdc -- a real, live-confirmed
	// incident found the router had no route to USDC within the normal
	// hop budget for one stranded mint, while a different destination
	// did. See catscope-rust-bot's src/brain/multimodelv1/message.rs's
	// identically-named variant for the real incident (a hop-chain send
	// stranding a real balance in an unsubscribed pass-through
	// intermediate mint) this exists to clean up.
	KeyFlagTriggerSweepMint uint8 = 10
	// KeyFlagTriggerEnableHawkesTrading (Go→bot) must match
	// catscope-rust-bot's CUSTOM_KEY_FLAG_TRIGGER_ENABLE_HAWKES_TRADING
	// -- trade type 5 (Hawkes-on-eigenfactor momentum, see
	// docs/HAWKES_FACTOR_TRADE_PLAN.md). Empty payload, same shape as
	// KeyFlagTriggerEnableDispersionTrading: entry itself is automated
	// (a real, discrete-time self-exciting intensity crossing its own
	// threshold), this only arms the automated decision loop.
	KeyFlagTriggerEnableHawkesTrading uint8 = 11
	// KeyFlagTriggerCloseHawkesPosition (Go→bot) must match
	// catscope-rust-bot's CUSTOM_KEY_FLAG_TRIGGER_CLOSE_HAWKES_POSITION
	// -- empty payload; explicit human request to close whatever Hawkes
	// momentum basket is currently open. The automated exit signal
	// (intensity decay, max-holding-cycles cap, borrow-gate re-check)
	// handles ordinary reversion; this is the human override.
	KeyFlagTriggerCloseHawkesPosition uint8 = 12
	// KeyFlagCommonAccountUsage is shared across every brain/* package
	// (not just this one) -- see the doc comment above.
	KeyFlagCommonAccountUsage uint8 = 200
)

Variables

View Source
var ErrBotNotConnectedYet = errors.New("multimodelv1: bot not connected yet")

ErrBotNotConnectedYet is returned by SendBundlerTipUpdate below when hs.instance hasn't been set yet -- see nilInstance's doc comment on every other mode's identically-named sentinel for the real, live-confirmed reliability gap this guards against.

Functions

func DoReplayResidualSnapshot

func DoReplayResidualSnapshot(payload []byte) catmsg.FixedPair

DoReplayResidualSnapshot wraps a persisted residual-snapshot payload (prefetch/multimodel.LoadResidualSnapshot's return value) for sending back to a freshly connected bot. payload is opaque to this package -- see catscope-rust-bot's src/trader/residual_snapshot.rs.

func DoTriggerCloseDirectionalPosition

func DoTriggerCloseDirectionalPosition() catmsg.FixedPair

func DoTriggerCloseDispersionPosition

func DoTriggerCloseDispersionPosition() catmsg.FixedPair

func DoTriggerCloseHawkesPosition

func DoTriggerCloseHawkesPosition() catmsg.FixedPair

func DoTriggerEnableDirectionalTrading

func DoTriggerEnableDirectionalTrading(mint sgo.PublicKey) catmsg.FixedPair

DoTriggerEnableDirectionalTrading wraps the human-specified long target (trade type 1's own entry signal, see the KeyFlagTriggerEnableDirectionalTrading doc comment above) as a real 32-byte mint pubkey payload.

func DoTriggerEnableDispersionTrading

func DoTriggerEnableDispersionTrading() catmsg.FixedPair

func DoTriggerEnableFactorLogging

func DoTriggerEnableFactorLogging() catmsg.FixedPair

func DoTriggerEnableHawkesTrading

func DoTriggerEnableHawkesTrading() catmsg.FixedPair

func DoTriggerEnablePairTrading

func DoTriggerEnablePairTrading() catmsg.FixedPair

func DoTriggerSweepMint

func DoTriggerSweepMint(mint, destMint sgo.PublicKey) catmsg.FixedPair

DoTriggerSweepMint wraps the mint to sweep plus its real destination mint (temporary manual-cleanup tool, see the KeyFlagTriggerSweepMint doc comment above) as a real 64-byte payload -- not hardcoded to USDC, since a real, live-confirmed incident found the router had no route to USDC within the normal hop budget for one stranded mint, while a different destination did have one.

func DoWallet

func DoWallet(key sgo.PrivateKey) catmsg.FixedPair

Types

type Configuration

type Configuration struct {
	BotImage string
}

type Hook

type Hook interface {
	brain.Brain
	SendBundlerTipUpdate(update bundler.TipUpdate) error
	SendTriggerEnableFactorLogging() error
	SendTriggerEnablePairTrading() error
	// SendTriggerEnableDirectionalTrading carries the human-specified
	// long target for trade type 1 (directional factor-neutral) -- real
	// execution once a real hedge basket clears the real borrow gates,
	// same real-money caveat as SendTriggerEnablePairTrading.
	SendTriggerEnableDirectionalTrading(mint sgo.PublicKey) error
	// SendTriggerCloseDirectionalPosition requests an explicit close of
	// whatever directional position is currently open (or pending open)
	// -- the human "I'm satisfied, take profit" half trade type 1's
	// otherwise-automatic close-pass doesn't have.
	SendTriggerCloseDirectionalPosition() error
	// SendTriggerEnableDispersionTrading arms trade type 3 (dispersion) --
	// unlike SendTriggerEnableDirectionalTrading, entry itself is
	// automated (a real, computed idiosyncratic-volatility signal), so
	// this carries no target; real execution (real spot buys, a real
	// Phoenix short) begins once the real signal clears on its own.
	SendTriggerEnableDispersionTrading() error
	// SendTriggerCloseDispersionPosition requests an explicit close of
	// whatever dispersion position is currently open -- same human
	// override role as SendTriggerCloseDirectionalPosition.
	SendTriggerCloseDispersionPosition() error
	// SendTriggerEnableHawkesTrading arms trade type 5 (Hawkes-on
	// -eigenfactor momentum, see docs/HAWKES_FACTOR_TRADE_PLAN.md) --
	// same no-target shape as SendTriggerEnableDispersionTrading: entry
	// itself is a real, computed signal (a discrete-time self-exciting
	// jump intensity crossing its own threshold), not a human-specified
	// target.
	SendTriggerEnableHawkesTrading() error
	// SendTriggerCloseHawkesPosition requests an explicit close of
	// whatever Hawkes momentum basket is currently open -- same human
	// override role as SendTriggerCloseDispersionPosition.
	SendTriggerCloseHawkesPosition() error
	// SendReplayResidualSnapshot pushes one persisted mint's residual/
	// z-score warm-up snapshot back to a freshly connected bot -- see
	// cmd/multimodel.go's boot-time pushResidualSnapshots, which calls
	// this once per mint returned by
	// prefetch/multimodel.LoadResidualSnapshots.
	SendReplayResidualSnapshot(payload []byte) error
	// SendTriggerSweepMint is a temporary, standalone manual-cleanup tool
	// (2026-09-03) -- not tied to any of the four trade types' own
	// gates. Sweeps the bot's entire real balance of mint to destMint.
	// See KeyFlagTriggerSweepMint's doc comment for the real incident
	// this exists to clean up and why destMint isn't hardcoded to
	// mint_usdc.
	SendTriggerSweepMint(mint, destMint sgo.PublicKey) error
}

Hook is brain.Brain plus the cross-strategy sender every real bot mode's Go package carries (SendBundlerTipUpdate, wired to startBundlerTipBroadcaster the same way every other mode's cmd file does it) plus Phase 5's real triggers: SendTriggerEnableFactorLogging (sub-phase 5b, read-only) and SendTriggerEnablePairTrading (sub-phase 5c, REAL execution -- real Kamino deposits/borrows, real spot swaps, once a real candidate clears the real z-score and borrow-cost gates).

func Create

func Create(ctx context.Context, cancel context.CancelCauseFunc, parentKey sgo.PrivateKey, config *Configuration, db *sql.DB) (Hook, error)

Create creates a multimodelv1 gRPC event hook. db is the already-open prefetch.db connection (see store.DB.Raw()) -- used by instance.go's loopInstance to persist real residual/z-score snapshots (prefetch/multimodel.SaveResidualSnapshot) and by cmd/multimodel.go's boot-time pushResidualSnapshots to replay them back. Phase 4's own Go-side persistence (the OpenIntentReport handler / boot-time ReplayOpenIntent replay) is a separate, still-unbuilt concern -- see that mechanism's own doc comments.

Source Files

  • eval.go
  • init.go
  • instance.go
  • message.go
  • multimodelv1.go
  • position.go

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL