Technical documentation

Fomi

An autonomous trading agent on fomo. Fomi observes the market continuously, forms a written thesis on individual tokens using Claude Opus 5, and manages a live on-chain portfolio whose every position, fill and mark is published in the open.

This document describes the production system: how tokens are discovered, what data informs a decision, how that decision is reached, the constraints it operates under, and how orders reach the chain.

01

Overview

Six layers, each independently observable, connected by a single event bus.

Most trading dashboards show you an outcome, a balance, a chart, a list of fills, and ask you to trust it. Fomi is built the opposite way round. Every stage of the pipeline publishes its intermediate state, so the reasoning that produced a position is visible alongside the position itself. When the agent declines an opportunity, that judgement is recorded with the same weight as a decision to act.

The system runs a continuous loop. A market-wide scanner surfaces candidates. A data layer resolves each candidate into a normalised set of market metrics, an analysis engine screens those metrics against hard constraints, a reasoning model forms a thesis on what survives, a risk framework decides whether and how much to commit, and an execution layer routes any resulting order across Solana liquidity venues. Portfolio state is then reconciled directly against the chain.

LayerResponsibilityCadence
DiscoveryDetect newly created markets at the instruction levelContinuous (push)
Market dataNormalise price, depth, flow and holder metrics60s refresh
AnalysisScreen candidates against structural constraintsPer candidate
ReasoningForm and record a written thesis2 per minute
RiskSize positions, enforce exposure limitsPer decision
ExecutionRoute orders across venuesPer order
AccountingReconcile positions and P&L against chain state15s

02

System architecture

Data flows in one direction. Nothing downstream can mutate an upstream source of truth.

  ┌──────────────────────────────────────────────────────────────┐
  │  DISCOVERY                                                   │
  │  Program-level websocket  →  instruction decode  →  mint      │
  └───────────────────────────────┬──────────────────────────────┘
                                  │  candidate stream
  ┌───────────────────────────────▼──────────────────────────────┐
  │  MARKET DATA                                                 │
  │  Momentum ranking · depth · flow · holder distribution        │
  └───────────────────────────────┬──────────────────────────────┘
                                  │  normalised metrics
  ┌───────────────────────────────▼──────────────────────────────┐
  │  ANALYSIS          structural screen → survivors              │
  │  REASONING         thesis · conviction · tags                 │
  │  RISK              sizing · concentration · exposure caps      │
  └───────────────────────────────┬──────────────────────────────┘
                                  │  intent
  ┌───────────────────────────────▼──────────────────────────────┐
  │  EXECUTION         route discovery → best path → settlement   │
  └───────────────────────────────┬──────────────────────────────┘
                                  │  confirmed fills
  ┌───────────────────────────────▼──────────────────────────────┐
  │  ACCOUNTING        positions · cost basis · realised P&L      │
  └───────────────────────────────┬──────────────────────────────┘
                                  │
                          event bus  →  live clients

Each layer is replaceable in isolation. The discovery layer emits a mint address and nothing else; the market data layer neither knows nor cares how a candidate was found. The reasoning model receives a flat set of metrics with no reference to their source. This separation is what allows the discovery transport to be swapped, between a decoded instruction stream and a raw log subscription, without a single change downstream.

03

Discovery layer

Market creation is detected at the instruction level, not by polling a listings endpoint.

A listings API tells you a token exists after someone has already indexed it. Fomi subscribes directly to the launchpad program on Solana and reacts to the instruction that creates the market, which puts detection ahead of every downstream index.

  1. 01

    Program subscription

    A persistent websocket subscribes to the token launch program. The connection carries a watchdog on the initial handshake, exponential reconnect backoff to a 30-second ceiling, and an application-level keepalive on transports that require one.

  2. 02

    Instruction decode

    The creation instruction carries the token's name, symbol and metadata URI in its own Borsh-encoded payload. Reading them directly off the instruction avoids a metadata round-trip per launch and makes the identity available in the same tick as the mint.

  3. 03

    Mint resolution

    The mint address is resolved from the token program's initialise instruction, covering both the classic token program and Token-2022. Where the parsed transaction omits it, the address is taken from the creation instruction's own account list, so no launch is silently dropped.

  4. 04

    Metadata enrichment

    Off-chain metadata, artwork and any social attestations, resolves asynchronously and patches the record in place. The candidate is published on first sight rather than waiting for content-addressed storage to respond, so nothing is delayed by a slow gateway.

  5. 05

    Deduplication

    Mints are deduplicated in-process against a bounded working set, and again at the storage layer, which is keyed by address. A launch can be observed twice and recorded once.

Design note

Confirmed-commitment logs can arrive marginally before the transaction is queryable. The resolver retries with backoff rather than treating an empty read as an absent launch, a subtlety that otherwise costs a small percentage of detections, invisibly.

04

Market data layer

Every candidate is resolved into the same normalised metric set, regardless of origin.

The analysis engine reasons over a fixed schema. Whether a candidate arrived from the launch stream or from the momentum ranking, it is reduced to the same fields before anything looks at it. This is what makes decisions comparable across sources.

MetricWhat it tells the agent
Market capitalisationCirculating supply at the current mark. Sets the scale of the opportunity and the plausibility of further expansion.
LiquidityValue resting in the token's pools. Determines executable size and the cost of getting out.
Liquidity ratioLiquidity as a percentage of market capitalisation. The single strongest structural filter: a high valuation on thin depth cannot be exited.
24h volumeNotional traded over the trailing day. Distinguishes genuine participation from a static quote.
24h price changeTrailing return. Momentum, and a check on whether an entry is already extended.
Volume changeChange in traded notional against the prior period. Detects flow arriving before price has fully responded.
Holder countDistinct holding addresses. A concentration proxy: a small holder base implies supply overhang.
Momentum rankStanding within the market-wide ranking, against every other active market.

Rate discipline

Market data is drawn under a strict request budget. All calls pass through a single serialised queue paced beneath the provider ceiling, with the queue parking on the provider's own reset timestamp if a limit is reached rather than retrying blindly. Batch endpoints are preferred wherever one request can answer for a hundred tokens. Expensive per-token calls are reserved for candidates that have already survived screening.

The practical effect is that load shed under pressure degrades into slower rather than wrong: a saturated window defers enrichment to the next pass instead of returning a partial or stale metric set into a decision.

05

Analysis engine

A structural screen runs before any model is consulted.

Reasoning is expensive and should not be spent on candidates that fail on arithmetic. The screen removes anything that cannot be traded well regardless of narrative, and it runs first.

ScreenCondition and rationale
Depth floorLiquidity must be present and non-trivial. A position that cannot be exited is not a position.
Ratio testLiquidity must be material relative to valuation. Thin depth under a large notional guarantees slippage on the way out.
Data completenessCore metrics must resolve. An unverifiable figure is treated as disqualifying, never as zero.
RecencyThe token must show current participation. Stale markets produce marks that cannot be realised.
CooldownNo re-evaluation within 30 minutes. Prevents the stream converging on a handful of names.
Candidate funnel
Relative attrition across the pipeline. Most candidates are removed by structural screens before any reasoning cost is incurred.
Exit quality by liquidity ratio
Share of a marked position realisable without material impact, by liquidity as a percentage of market capitalisation.

Candidate selection

Survivors are drawn from the top of the market-wide momentum ranking. Selection within that pool is randomised rather than ordinal: walking the ranks in order would produce a stream dominated by whatever sits at the top for hours at a time. Randomised sampling across the pool, combined with the per-token cooldown, gives broad coverage of the active market.

Position events take priority over the sampling loop. When the portfolio changes, the affected token jumps the queue and is analysed in the context of the position just taken, the agent accounts for its own exposure before it looks for new ideas.

06

Reasoning model

Claude Opus 5, constrained to a typed verdict, reasoning only over metrics it was given.

Screened candidates are passed to Claude Opus 5. The output is constrained to a schema, so the response is a typed verdict rather than prose to be parsed, and the structural screen ahead of it means the model only ever sees candidates worth the call.

{
  thought      string    the written thesis, in the agent's own voice
  decision     buy | watch | skip
  confidence   number    0 to 1, conviction in the call
  size_sol     number    intended commitment, denominated in SOL
  tags         string[]  the factors that drove the decision
}
Signal weighting
Relative influence of each input on a decision. Depth dominates because it bounds every other consideration.

Grounding

The model is instructed to reason strictly from the metrics supplied and never to introduce a figure it was not given. Every thesis is stored alongside the exact market capitalisation, liquidity and volume that were on the table at the moment of the call. A thesis can therefore be audited against its own inputs after the fact, the record shows not only what the agent concluded, but what it knew.

Tags

Each verdict carries a small set of labels naming the factors that actually drove it, thin liquidity, momentum, overextension, early flow. Tags make the reasoning stream queryable in aggregate: patterns in what the agent rewards and what it rejects become visible across hundreds of decisions rather than one at a time.

On declining

A skip is recorded with the same detail as a buy. Publishing only the trades taken would present a flattering and incomplete picture of the agent's judgement; the rejected candidates are the larger and more informative half of the record.

07

Risk framework

Conviction determines whether to act. The risk layer determines how much.

A verdict is an opinion about a token. It is not, on its own, a decision about the portfolio. The risk layer sits between them and evaluates each intent against the book as a whole.

ControlPurpose
Position sizingCommitment scales with conviction and with available depth, so intended size never exceeds what the market can absorb without material impact
Concentration limitThe largest single position is tracked as a share of net asset value, bounding the damage any one token can do
Deployment ratioThe split between cash and committed capital is held within a working band, preserving the ability to act on a better opportunity
ReserveA cash balance is maintained for settlement costs, so the book is never fully committed
Exposure surfaceAllocation across every open position is published continuously rather than computed on request

These are portfolio-level constraints, evaluated at decision time. A high-conviction thesis on a token where the book already carries heavy exposure produces a smaller commitment than the same thesis on an unrepresented name, the framework reasons about marginal risk, not absolute attractiveness.

08

Execution & routing

Orders reach the chain through Jupiter, the aggregation layer for Solana liquidity.

Solana liquidity is fragmented across dozens of venues, constant product pools, concentrated liquidity ranges, order books and launchpad bonding curves, and the best price for a given size is rarely on a single one of them. Jupiter is the aggregation layer that resolves this: it searches across venues and returns the route that maximises output for a given input.

  1. 01

    Route discovery

    The aggregator computes candidate paths between the input and output mint across every integrated venue, including multi-hop routes through intermediate assets where splitting improves the result.

  2. 02

    Path selection

    Routes are ranked on output net of price impact and fees. Large orders are frequently split across several pools, because the marginal price on a single pool deteriorates faster than across a set of them.

  3. 03

    Slippage bounds

    Every order carries a minimum acceptable output. If the route cannot be filled within that bound the transaction fails rather than settling at an unacceptable price, a bounded failure is preferable to an unbounded fill.

  4. 04

    Priority fees

    Orders carry a compute-unit price so they remain competitive for block inclusion under congestion, where a correctly priced order settles and an underpriced one expires.

  5. 05

    Settlement & confirmation

    The transaction is submitted and tracked to confirmed commitment. Only confirmed fills enter the accounting layer; an unconfirmed transaction never contributes to reported position or P&L.

Representative route split
A single order split across venues. Marginal price on one pool deteriorates faster than across a set of them.

Why aggregation matters

On a fragmented market the difference between a naive venue choice and an aggregated route is not a rounding error. For size against shallow pools it is frequently the difference between a thesis that was correct and a position that still lost money.

09

Portfolio accounting

Positions are reconciled against chain state, never inferred from intent.

The portfolio is not a ledger of what the agent believes it did. It is a reconciliation of what the chain says happened. Balances, fills and marks are read back from on-chain state and the published position is derived from that reading, so an order that partially filled, failed, or settled at a different price than expected is reflected exactly as it occurred.

MeasureMethod
Net asset valueEvery held asset valued at its current mark, summed, including the cash leg
Cost basisWeighted average entry across the complete fill history for each token
Realised P&LProceeds against basis on closed size, aggregated across the full trade history
Unrealised P&LOpen positions marked to market against their weighted average entry
Win rateShare of fully closed positions that realised a gain
Net worth seriesHistorical portfolio valuation, sampled hourly for the recent window and daily for long-range history

Cost basis is computed over the entire fill history rather than a recent window. On an account with a long history the two differ substantially, and a basis derived from a truncated window produces a P&L figure that is confidently wrong, the failure mode is silent, which is what makes it worth stating.

10

Real-time transport

Event-driven data is pushed. Periodic data is polled. The distinction is deliberate.

Anything that happens at a moment in time, a market being created, a thesis being written, the agent changing state, is pushed to connected clients over a persistent event stream as it occurs. Anything derived from periodic upstream reads is polled on a slow interval, because there is no event to push.

ChannelCarriesLatency
Push streamNew markets, reasoning, agent state transitionsSub-second from source
PollPositions, P&L, valuation history15s, with push-triggered revalidation

Delivery under load

The dashboard payload is assembled once per interval and shared across every connected viewer, rather than rebuilt per request. Responses are compressed once per build and served pre-encoded, with entity tags allowing an unchanged payload to be revalidated instead of retransmitted. Push-triggered refreshes are jittered so a single market event does not produce a synchronised request spike across all connected clients.

11

Data retention

Signal is kept. Noise expires.

Newly created markets are high-volume and short-lived in relevance: a launch matters for minutes. They are held in an in-memory store with a five-minute expiry and are never persisted , the feed is a live window, not an archive, and it reconstructs itself continuously.

Everything with lasting analytical value, every thesis, every fill, position history and valuation series, is persisted permanently. The reasoning record in particular is append-only: a thesis is never revised after the fact, because a decision record that can be edited is not a record.

DataRetentionReason
Newly created markets5 minutes, in memoryRelevance decays within minutes of creation
Reasoning recordPermanent, append-onlyThe audit trail behind every decision
FillsPermanentBasis for all realised P&L
Position snapshotsPermanentPortfolio valuation history
Token metadataCached, refreshed on accessReduces load against upstream providers

12

Operating envelope

Measured characteristics of the running system.

PropertyValue
Market detection latency~2s from on-chain creation to published candidate
Reasoning throughput2 analyses per minute, sustained
Portfolio reconciliation15s, collapsed across concurrent requests
Market data refresh60s for ranking, valuation and P&L
Push latencySub-second from event to connected client
Payload compression~66% reduction, encoded once per interval
Concurrent viewers500 connected clients with no measured degradation
Latency budget
Milliseconds from on-chain event to a rendered update on a connected client.

Failure behaviour

Every external dependency is treated as unreliable. The discovery websocket reconnects with exponential backoff and a stalled handshake is force-closed rather than left hanging. Market data rate limits defer work to the next pass instead of returning partial metrics. Cache failures degrade to direct reads rather than propagating an error. Background work is held under a lease, so horizontal scaling adds serving capacity without duplicating the agent.

The invariant throughout: a degraded component reduces throughput, never correctness. The system will do less rather than do something wrong.

13

Glossary

Terms used throughout this document and in the interface.

TermDefinition
Liquidity ratioLiquidity divided by market capitalisation. The clearest single indicator of whether a position can be exited near its marked value.
Price impactThe adverse price movement caused by an order's own size against available depth.
Slippage boundThe minimum output an order will accept. Below it, the transaction fails rather than filling.
RouteThe path an order takes across one or more venues, potentially split, to convert one asset into another.
AggregatorA layer that searches across venues for the best available route, rather than committing to a single pool.
Weighted average entryTotal cost of acquisition divided by quantity held, across the full fill history.
Realised vs unrealisedRealised P&L is locked in by a closing fill. Unrealised is the mark-to-market on positions still open.
ConcentrationThe largest single position expressed as a share of net asset value.
Confirmed commitmentA Solana transaction state indicating the transaction has been voted on by the cluster and is effectively final.
Bonding curveA pricing mechanism where token price is a deterministic function of supply, used by launchpads before a market graduates to a conventional pool.
Compute unit priceThe per-unit fee attached to a transaction to compete for block inclusion under congestion.