When to Use
Use this skill when auditing what a running strategy actually did at the venue: what share of its executed flow added liquidity versus removed it, and what that posture cost or earned in exchange fees. Venues price the two sides asymmetrically — passive fills may be charged a lower fee or credited a rebate, aggressive fills are charged the taker rate — so the maker/taker split is the first-order driver of a high-turnover strategy's fee bill. The engine decomposes a fill log, computes the maker ratio on an explicit weighting basis, classifies the strategy as PURE_MAKER_STRATEGY / PURE_TAKER_STRATEGY / HYBRID_MAKER_TAKER_STRATEGY, and attributes fees and rebates to each side separately.
When NOT to Use
- To decide whether you are a regulated market maker or dealer. This is a fee/execution diagnostic computed from fills. Under MiFID II the test is one of quoting presence, not fills: Directive 2014/65/EU Article 17(4) defines pursuing a market making strategy as posting firm, simultaneous two-way quotes of comparable size at competitive prices, and RTS 8 (Regulation (EU) 2017/578) fixes the presence obligation in terms of daily trading hours quoted, not trades executed. In the US, the SEC's expanded dealer rules (3a5-4, 3a44-2) were vacated on 21 November 2024 and the SEC dismissed its appeal on 20 February 2025 — no fill ratio is a registration trigger. Use
mifid-ii-algo-trading-compliance-eufor the actual obligation. - To judge whether passive execution is working. A fill log contains the passive orders that filled and cannot contain the ones that did not, so the maker ratio says nothing about fill rate, queue position, or adverse selection. Pair with
adverse-selection-measurement-for-passive-ordersandqueue-position-modeling-for-passive-orders. - On per-contract fee schedules. Venues that bill per contract by membership/product/venue rather than by liquidity flag (CME Group's futures schedules work this way) have no maker/taker rate to attribute, and an effective rate in bps of notional does not describe their cost.
- Across venues in one run. A per-share venue and a percentage-of-notional venue do not share a pricing unit; blending them into one effective bps figure produces a number that is arithmetically valid and economically meaningless.
- As a forward-looking fee estimate. This audits realized fills. For "what would the next tier cost", use
exchange-fee-tier-and-rebate-structure-analysis.
Prerequisites
- Executed fill log with, per fill:
trade_id,symbol,is_maker(a real boolean),executed_price(> 0),quantity(> 0, absolute — encode side separately),fee_paid_usd, and optionallyliquidity_category. - Sign convention:
fee_paid_usdpositive means the venue charged you; negative means it credited you a rebate. Every USD figure and the effective bps rate follow the same convention, so a negative net is rebate capture. This matchesexchange-fee-tier-and-rebate-structure-analysis. - Classification basis —
ClassificationBasis.QUANTITYorClassificationBasis.NOTIONAL. Required, no default; see step 1. - Fills from a single venue. The engine does not normalise pricing units across venues.
Workflow
-
Choose the classification basis before computing anything — it decides what the ratio means.
QUANTITY: maker quantity / (maker + taker quantity). Correct where the fee is levied per unit — US equity venues quote maker rebates and taker fees in dollars per share, so share counts are what the bill is proportional to.NOTIONAL: maker notional / (maker + taker notional). Correct where the fee is a percentage of trade value — Binance and Kraken both quote maker/taker rates as a percentage of trade value, tiered on 30-day rolling volume.- Decision point: the two bases can return different labels on the same log. A desk that posts small passive orders in a cheap name and crosses the spread in an expensive one is maker-heavy by share count and taker-heavy by value. Pick the basis that matches how the venue bills you; the report carries both ratios so you can see the disagreement.
- Decision point: the engine refuses a multi-symbol log on the
QUANTITYbasis. One BTC and 100 shares of AAPL are not 101 of anything — either classify one symbol at a time or switch toNOTIONAL.
-
Classify each fill's liquidity category before bucketing it — the flag is not binary.
- FIX
LastLiquidityInd(tag 851) enumerates1 = Added Liquidity,2 = Removed Liquidity,3 = Liquidity Routed Out, and (FIX 5.0 SP2)4 = Auction. Only the first two are the maker and taker sides of the continuous book. - Decision point: if the log carries routed-out or auction fills, pass
liquidity_categoryexplicitly. They go into a separate excluded bucket, out of the ratio's numerator and denominator, while their fees stay in the net. Collapsing them intois_maker=Falseinflates the taker share — a closing-auction print of any size can flip a genuinely passive desk fromPURE_MAKER_STRATEGYtoHYBRID_MAKER_TAKER_STRATEGY. - Decision point:
is_makermust be a realbool. Broker REST payloads routinely carry the flag as the string"false", which is truthy in Python; the engine rejects a non-bool rather than booking every taker fill as a maker fill. - Decision point: deduplicate the log before submitting it. Overlapping paginated fetches are the normal way a fill arrives twice, and a double-counted fill corrupts every figure in the report with nothing in the output to show it. The engine rejects a repeated
trade_id; if the venue reuses one id across partial fills, key on the per-fill execution id instead.
- FIX
-
Compute the maker ratio and classify against the thresholds.
- $R_{\text{maker}} = \dfrac{W_{\text{maker}}}{W_{\text{maker}} + W_{\text{taker}}}$ where $W$ is quantity or notional per the selected basis.
- $R_{\text{maker}} \ge 0.80 \implies$
PURE_MAKER_STRATEGY; $R_{\text{maker}} \le 0.20 \implies$PURE_TAKER_STRATEGY; strictly between $\implies$HYBRID_MAKER_TAKER_STRATEGY. Both bounds are inclusive. - These thresholds are a reporting convention, not a standard. No regulator or exchange defines a maker-ratio cut-off. Override them to whatever your desk means; the engine rejects a swapped or equal pair, which would otherwise make the taker branch unreachable.
- Decision point: comparison is against the full-precision ratio, never a rounded one. A ratio of 0.79996 rounds to 0.8000 at four decimal places, and classifying the rounded value silently promotes it to
PURE_MAKER_STRATEGY. The report also flags any ratio within 0.005 of a threshold as a cut-off artefact — re-read those before acting on the label. - If no fill added or removed continuous-book liquidity, the result is
UNCLASSIFIED_NO_MAKER_TAKER_VOLUMEwith aNoneratio, not0.0(which would read as "entirely taker").
-
Attribute fees and rebates per side, not just in aggregate.
- $\text{Fee}{\text{effective_bps}} = \dfrac{F{\text{net}}}{N_{\text{gross}}} \times 10{,}000$, over all fills including excluded ones — they were still billed.
- The report also carries
maker_fees_paid_usd/taker_fees_paid_usdand each side's effective bps against its own notional. - Decision point: a positive
maker_fees_paid_usdmeans the passive side was charged, not credited. A maker-dominant posture only pays for itself where the venue's maker rate is negative at your tier — on standard crypto tier tables it is not (Binance's published spot schedule charges a positive maker rate at every VIP tier). The report warns when this happens.
-
Audit Report Generation: output the structured
StrategyClassificationReport, and readwarningsbefore quoting any figure from it.
Full procedure: see
references/workflows.md. Standards reference: seereferences/standards.md. Printable pre-flight checklist: seeassets/checklist.md.
Common Pitfalls
- Counting auction and routed-out fills as taker volume. They are billed under their own rate codes, not the continuous-book taker rate, and a single large closing-auction print can move the ratio far enough to change the label. Exclude them explicitly; do not let a boolean flag decide for you.
- Trusting a truthy liquidity flag.
is_maker="false"parsed from JSON isTruein Python. Every taker fill in the log then books as a maker fill, the ratio reads 1.00, and the fee attribution says the desk earned rebates it was actually charged. - Rounding the ratio before comparing it to the threshold. Round-then-classify promotes 0.79996 to
PURE_MAKER_STRATEGY. Classify on the exact value and round only for display. - Summing share counts across instruments. A maker ratio built from 1 BTC plus 100 AAPL shares is dominated by whichever instrument happens to have the larger unit count. Use the notional basis for multi-symbol logs.
- Reading a high maker ratio as regulatory market-making status, or as evidence the passive strategy is working. It is neither — see When NOT to Use.
- Assuming maker means rebate. On most standard crypto fee tiers the maker rate is a positive fee. Netting a "rebate capture" figure that is actually a fee inverts the sign of the conclusion.
- Submitting limit orders without a post-only flag in a maker algo. An order priced through the touch executes aggressively and is billed at the taker rate; the strategy intended to be passive and pays for the privilege. See
post-only-and-maker-taker-fee-optimization. - Reading the effective bps rate across venues. Per-share and percentage-of-value schedules do not blend.
Verification
- Instantiate
MarketMakerVsTakerClassifierEngine(ClassificationBasis.NOTIONAL). Audit 100 fills (90 maker at 100 units × $100 with $-2.00 each, 10 taker at the same size with $+8.00 each): verify $R_{\text{maker}} = 0.90$,PURE_MAKER_STRATEGY,total_gross_notional_usd$= $1{,}000{,}000$,net_fees_paid_usd$= -$100.00$, andeffective_fee_rate_bps$= -1.0$ (a rebate), withstatusSTRATEGY_CLASSIFICATION_SUCCESS. - Basis divergence: one maker fill of 100 units at $10 and one taker fill of 100 units at $190 in the same symbol must classify
HYBRID_MAKER_TAKER_STRATEGYonQUANTITY(ratio 0.50) andPURE_TAKER_STRATEGYonNOTIONAL(ratio 0.05). - Rounding regression: 79,996 maker units against 20,004 taker units must stay
HYBRID_MAKER_TAKER_STRATEGY— the exact ratio 0.79996 is below the 0.80 threshold even though it rounds to 0.8000. - Excluded liquidity: 5 maker + 5 taker + 10
AUCTIONfills of equal notional must give a ratio of 0.50 (not 0.25), report the auction notional and fees in the excluded fields, keep them in the net fee total, and emit a warning. A log of onlyROUTED_OUTfills must returnUNCLASSIFIED_NO_MAKER_TAKER_VOLUMEwithNoneratios. - Negative checks: an empty log, a raw
dictin place of a fill, a duplicatetrade_id,is_makeras a string orint, a non-positive or non-finite price or quantity, a non-finite fee, aliquidity_categorycontradictingis_maker, an unknown category or basis, swapped/equal/out-of-range thresholds, and a multi-symbol log on theQUANTITYbasis must each raiseTradeLogError(aValueError).ExecutedTradeLogis frozen, so assigning to a validated field must raiseFrozenInstanceError. - Run
python -m unittest discover -s skills/market-maker-vs-taker-strategy-classification/scriptsand confirm 100% pass rate.
Related Skills
exchange-fee-tier-and-rebate-structure-analysispost-only-and-maker-taker-fee-optimizationadverse-selection-measurement-for-passive-ordersqueue-position-modeling-for-passive-ordersorder-to-trade-ratio-fee-penalty-avoidancepost-trade-execution-quality-scorecardtransaction-cost-analysis-tca-integrationmifid-ii-algo-trading-compliance-eu