First Block

Building a Filter Set That Fails Cheaply

A filter set is a rejection machine, and its quality is decided by which rare candidate it admits and which failure it accepts in exchange. This page covers rule ordering, threshold arithmetic, the false positive every rule owes you, and how to review a set once it is running.

FLT-01 The First Block Desk 2310 words 11 min read Updated 12 September 2026

Decision card

Question
How should screening rules be written, ordered and reviewed
Inputs
Available account fields, your own position size, and a rejection log
Rule
Cheap checks first, one claim per rule, every threshold derived from a size you chose
Failure mode
Copying thresholds from elsewhere and bundling rules into a single opaque score

A filter set is not a predictor. It is a stated policy about which mistakes you are willing to make, expressed as rules a program can evaluate quickly against incomplete state. Building one well is mostly ordering, arithmetic and bookkeeping: cheap checks before expensive ones, one claim per rule, every threshold derived from a position size you chose, and a log that lets you argue with yourself a week later.

A filter set is a rejection machine

On any active day, a screening pipeline will reject the overwhelming majority of what it sees. That is the normal operating condition, not a sign that the rules are too tight. It follows that the cost of a rejection is the cost that dominates the whole system, and that the design should be optimised for cheap rejections rather than for elegant admissions.

It also follows that the two possible errors are not equivalent. Rejecting something that would have worked costs an opportunity you never observe. Admitting something that traps the position costs the position, and you observe it every time. Because the second error is visible and the first is not, unexamined tuning drifts toward looseness: rules get relaxed after each visible miss and never get tightened after an invisible one.

Writing that asymmetry down at the top of the rule file is a surprisingly effective corrective. It converts every later tuning argument into a comparison between two named costs instead of a reaction to whatever happened most recently.

Ordering rules by what they cost

Rules have wildly different evaluation costs, and the ordering decides how much a typical candidate costs to reject. Three tiers cover nearly everything.

TierExample checksCost per candidatePosition in the pipeline
LocalIdentity deduplication, program identity, blocklisted addresses, obviously malformed namesNone beyond CPUFirst, always
Single readMint account: supply, decimals, mint and freeze authority, token program in useOne round tripSecond
Single readPool or curve account: reserves, quote asset, pool token ownershipOne round tripThird
Multi readLargest token accounts, effective float, metadata account contentsSeveral round trips, sometimes paginatedLast

The saving is larger than it looks. If nine candidates in ten are rejected by a mint-account check, then running the expensive distribution read first multiplies your request volume by roughly ten for no additional information. Under load, that difference is the difference between a pipeline that keeps up and one that queues.

None of these reads requires privileged access. They are ordinary account and program queries served by any standard endpoint, and the request shapes are catalogued in the Solana developer documentation. That matters for a practical reason: it means the cost of a rule is measured in round trips rather than in access, and a rule that is expensive is expensive for everyone reading the same state.

There is one deliberate exception worth making. Sizing should be computed early, immediately after reserves are known, and used as an admission criterion. If the pool cannot support a position large enough to be worth taking at your impact tolerance, nothing about the holder distribution matters and the candidate can be dropped before the most expensive check runs.

One rule, one claim

The most damaging structural choice in screening is the composite score. It feels sophisticated, it produces a tidy number, and it makes the system impossible to debug. When a candidate scores well and then behaves badly, a score tells you nothing about which component was wrong.

Keep rules atomic. Each one reads a named field, applies one comparison, and produces a verdict with a reason. Aggregate afterwards if you want a summary, but keep the individual verdicts as the primary record. This has three practical benefits: you can change one rule without disturbing the others, you can attribute a rejection to a specific number, and you can answer the question that eventually matters, which is why a particular candidate was admitted.

Atomic rules also make it obvious when a rule is not running. A rule that references a field which does not exist at your trigger point will silently evaluate to a pass in most implementations. Recording the value each rule read, not just its verdict, exposes this immediately: a rule whose logged value is always the same default is not a rule.

Writing a threshold you can defend

Every threshold should be the output of a calculation whose inputs you can state. The liquidity floor is the clearest example, because it is the most frequently copied number in the entire subject and it is meaningless without a position size attached.

The calculation runs backwards from tolerance. Decide the price impact you are willing to accept on entry. In a constant-product pool, ignoring fees, an input equal to a fraction of the quote reserve moves the price by roughly twice that fraction. So if you will accept about four percent impact, your input should be around two percent of the quote reserve, which means the reserve must be about fifty times your intended position.

Intended positionImpact toleranceImplied input share of reserveRequired quote reserve
0.5 SOLAbout 2 percent1 percentAbout 50 SOL
0.5 SOLAbout 4 percent2 percentAbout 25 SOL
2 SOLAbout 2 percent1 percentAbout 200 SOL
2 SOLAbout 4 percent2 percentAbout 100 SOL

This table is arithmetic on a constant-product curve with fees ignored, using position sizes chosen to make the multiplication readable. It is not a recommendation and contains no observation of any pool. The point is the structure: the floor is a function of your size and your tolerance, so two operators with different sizes should have different floors and neither of them should be using yours.

The same discipline applies to every other number in the set. A holder concentration limit implies a view about how much sell pressure can arrive at once relative to the depth you are trading into. A minimum age implies a view about what the extra time reveals. A rule whose number cannot be derived from anything is a rule you inherited, and inherited numbers are the most common defect in screening pipelines.

Every rule owes you a false positive

A rule without a named false positive has not been examined. The exercise takes a minute per rule and changes how the set is read afterwards, because it forces you to describe the legitimate candidate you are choosing to give up.

RuleWhat it is trying to preventThe legitimate candidate it rejects
Mint authority must be revokedArbitrary supply inflation after entryA project that revokes shortly after launch as part of a planned sequence
Quote reserve above a floorUnacceptable slippage on entry and exitA thin launch that deepens quickly, entered later at a worse price
Top holder below a share of supplyA single account able to overwhelm the poolA treasury or vesting account that is contractually unable to sell
Metadata must be immutableIdentity being changed after entryA team that keeps metadata mutable for legitimate later edits
Standard token program onlyTransfer behaviour altered by extensionsA token using extensions for a benign reason such as a documented transfer fee
Pool tokens verifiably lockedLiquidity withdrawn from under the positionA launch where the lock exists but is held in a form your check cannot read

Reading the third column as a list is instructive. It is the description of what your rule set is not for. If that list contains the kind of launch you were actually hoping to catch, the set is misconfigured and no amount of speed will help. If it contains only things you are content to miss, the set is doing its job.

Fail closed, log everything

Two defaults do more for a screening pipeline than any individual rule. The first is that any failure to evaluate is a rejection. A timeout, a malformed response, an account that does not deserialise, a paginated read that did not finish: all of them reject. The alternative, skipping the rule, loosens the entire set precisely when conditions are worst.

The second is that every decision is logged with its reason and its value. Not a boolean, not a score, but the rule identifier and the number it compared. This log is the only asset the system produces that improves over time. Positions come and go; the rejection log is what lets you ask, a month later, whether a rule has ever admitted anything or ever rejected anything.

Both defaults have a visible cost: more rejections, more storage, more code paths. Both are worth it, and an operator who has run without them for a while can usually tell you exactly which unexplained entry made them change their mind.

Assembling a set, step by step

The sequence below produces a set you can defend, in an order that prevents the most common structural mistakes. It works on paper first, deliberately.

  1. Write every rule as one plain sentence. One claim, one field named. If a sentence needs an "and", it is two rules.
  2. Mark the state each rule depends on. Mint fields, pool reserves, holder accounts, metadata, ownership. Group them.
  3. Pick the trigger that supports the whole set. The latest event at which all your required fields exist. Anything earlier means some rules cannot run.
  4. Order the rules by evaluation cost. Local, then single-account reads, then multi-account reads. Insert the sizing calculation immediately after reserves.
  5. Derive every threshold from your position size. Impact tolerance in, required reserve out. Write the derivation next to the number.
  6. Write the false positive for each rule. The legitimate candidate you are choosing to give up. Keep it in the same file as the rule.
  7. Make every evaluation failure a rejection. One code path, no exceptions, including for rules you consider minor.
  8. Log rule identifier and observed value for every candidate. Admissions and rejections alike, with the timestamp of the triggering event.
  9. Schedule the review before you start. A weekly read of the rejection log, one rule changed at a time, previous version kept for comparison.

Step nine is the one most likely to be skipped and the one that decides whether the set improves. A rule set that is never reviewed does not stay the same; it drifts, because the environment changes around it while the numbers stay fixed.

Testing without fooling yourself

Replaying historical launches against a rule set is worth doing and worth being careful about. It is genuinely good at finding structural faults: rules that never fire, rules that always fire, rules that error on real data, and rules that reference fields absent at your trigger point. Those findings are reliable because they are statements about the rules themselves.

It is not good at telling you whether the set makes money, and treating it that way is how operators acquire confident, expensive habits. A replay does not reproduce your execution, your size, your slippage, your exit behaviour or the presence of every other participant who would have reacted to your order. It also suffers the ordinary fitting problem: a set adjusted until it looks good on a past window has been shaped by that window.

  • Use replay to find dead rules. Any rule with a constant verdict across hundreds of candidates is either redundant or not running.
  • Use replay to find crash cases. Real launch data contains malformed metadata, unusual decimals and accounts that do not deserialise. Better found offline.
  • Use replay to check admission volume. If your set admits an implausible share of everything, a rule is not running. If it admits nothing across a long window, a threshold is arithmetically unreachable.
  • Do not use replay to estimate returns. The parts it omits are the parts that determine the answer.
  • Do not tune to a single window. A change that only helps in the window you tuned on is a description of that window.

Anything a replay does report can be checked against the chain itself, which is the useful property of working on a public ledger. If a replay says a candidate was admitted, the transactions around that launch are visible on any public Solana explorer, and the accounts your rules read can be inspected by hand. A replay result you cannot reconcile with the chain is a bug in the replay.

Reviewing the rejection log

The rejection log is where a rule set is improved, and it is read differently from a position log. A position log tells you about outcomes, which are dominated by things outside the rule set. The rejection log tells you about the rules, which is the only thing you control.

Three questions are worth asking every time. Which rule is doing most of the rejecting, and is that consistent with what you thought the set was for? Which rules have never rejected anything, and are they running at all? And among the rejections, are there clusters that share a characteristic you did not intend to exclude?

Change one rule at a time and keep the previous version. The temptation after a bad outcome is to change several things at once, which guarantees that the effect of each is unknowable. It is also worth noting the date and the reason for every change in the same file as the rule, because six weeks later the reason is the part you will have forgotten.

None of this is glamorous, and it is the entire difference between a rule set and a collection of numbers. The maintenance is also why some operators conclude that the decision layer is not what they want to spend their time on. If the token is already chosen and the work is routing flow through an existing pool, the problem is narrower: an automated Solana volume bot is judged on cost per swap, wallet handling and whether it separates what it attempted from what it landed, which is a much shorter list of questions than the one this page has been answering.

The next two pages take the rule families apart individually: what the authority and liquidity fields actually prove, and what a holder distribution read can and cannot tell you when the distribution itself is trivially arrangeable.

Questions this page keeps getting

What order should token filter rules run in?

Cheapest first. Checks decidable from the event payload alone cost nothing and should run before any network call. Single-account reads come next. Multi-account reads, such as enumerating the largest token accounts, are the most expensive and belong last. Since most candidates are rejected, the ordering decides how much a typical rejection costs.

How do I choose a liquidity threshold?

Work backwards from position size. Decide the price impact you are willing to accept, then compute the reserve required for your intended size to stay within it. The threshold is the output of that calculation. A number taken from somewhere else encodes a different size, a different tolerance and a different venue.

Should a filter set produce a score?

Not as its primary output. A single score is convenient and destroys the information you need to debug it, because a candidate that scores well can have failed any component. Keep rules separate, record which one fired, and treat any aggregate as a summary of individual verdicts rather than a replacement for them.

What is a false positive in a screening context?

A legitimate candidate that a rule rejects. Every threshold has them by construction, and writing them down beside the rule is what turns a guess into a design decision. If you cannot name the kind of candidate your rule would wrongly exclude, you do not yet know what the rule is doing.

Can I backtest a filter set?

You can replay historical launches against a rule set, and it is useful for finding rules that never fire or always fire. It is not useful as evidence of profitability, because the outcome of an entry depends on execution, size and exit behaviour that a replay does not reproduce, and because a set tuned against a past window is fitted to that window.

How often should a filter set change?

Slowly, and one rule at a time. Changing several rules together makes the effect of each impossible to attribute. Keep the previous version so the two can be compared on the same candidate stream, and treat any change made immediately after a loss with suspicion, since that is when reasoning is worst.

Filed under Filters. Thresholds quoted on this page are worked examples, not recommended values, and the arithmetic around them uses numbers you supply rather than numbers observed anywhere. If something here is wrong, tell the desk and the page gets amended in the open.

Read next