Every 30 minutes the rebalancer walks through the same pipeline for every Bittensor subnet:
Once a position is open, monitor.py runs every 15 minutes and checks exit rules (trailing stop, score decay, flow reversal, EMA breakdown, take-profit checkpoints, hard stop loss, kill switch).
| Component | Category | Max Pts | What It Measures | How It Works |
|---|---|---|---|---|
| Slope Acceleration | Momentum | 11 | Is the price trend accelerating? | Compares recent 4h candle slope to earlier slope. A steepening uptrend scores high. Flat or decelerating trends score low. Uses a 325× multiplier on the slope difference. |
| Price Position 7D | Momentum | 11 | Where is the price within its 7-day range? | (current − 7d_low) / (7d_high − 7d_low). Near the top of the 7-day range = strong. Near the bottom = weak. Scores linearly. |
| Monthly/Weekly Momentum | Momentum | 10 | Is the trend sustained across timeframes? | Combines 30-day and 7-day price changes. Rewards subnets where BOTH timeframes are positive (sustained trend, not a single spike). Penalises divergence. |
| EMA Freshness | Momentum | 8 | How recently did price cross above the EMA? | Uses the 18-period 4h EMA (≈72h trend). Price above EMA = bullish. Recently crossed above = extra points (fresh breakout). Below EMA = 0–1 points. |
| Flow Ratio | Buy Pressure | 34 | Are buyers outweighing sellers? | 24h TAO buy volume vs sell volume. Strong buy-to-sell ratio (e.g. 2:1) scores high. Also factors wallet counts and volume consistency. This is the single heaviest-weighted component — capital flow is the strongest leading signal we have. |
| Flow Acceleration | Buy Pressure | 16 | Is buying pressure accelerating? | Compares recent flow to earlier flow. If buying is ramping UP (not just steady), it scores high. Uses a 600× multiplier on the acceleration. Catches early-stage buy surges before they show in price. |
| Wallet Ratio | Buy Pressure | 10 | More unique buyers than sellers? | Distinct buyer wallets vs seller wallets in 24h. A high buyer/seller ratio means broad-based demand (not just one whale). Capped at 10 points. |
use_rolling_current_candle: true), so the score reflects what's happening live, not just the last closed candle.Score maps to a tier. Tier sets the target percentage of the portfolio that position should occupy. Anything scoring below 30 is dropped — no allocation.
| Tier | Score Range | Target % | Per-Position Hard Cap | Entry Behaviour |
|---|---|---|---|---|
| Conviction | 75 – 100 | 7% | 10% | Forced entry: can evict the lowest-conviction open position to make room, unless the evict target is protected (active trailing stop or > 30% ROI). |
| Tier 1 | 50 – 74 | 5% | 10% | Standard entry when a slot is available. |
| Tier 2 | 40 – 49 | 3% | 10% | Standard entry when a slot is available. |
| Tier 3 | 30 – 39 | 1.5% | 10% | Standard entry when a slot is available. Tier 3 is "probe size" — a small toehold while we wait for the trend to confirm. |
Sizing rule layered on top of the tier table above. Borrowed from Gordy: enter small, scale up only as the trend keeps confirming.
A subnet must pass all of these to be eligible for scoring. Anything that fails is dropped — no further evaluation.
| Filter | Rule | Why |
|---|---|---|
| Price Cap | Price ≤ 0.04 TAO | Avoid mature/expensive subnets where 5% moves take days. |
| Price Floor | Price > 0 | Must have a real, non-zero price. |
| Min Pool Depth | Pool ≥ 2,800 TAO | Enough liquidity to enter and exit without crushing the price. |
| Max Pool Depth | Pool ≤ 50,000 TAO | Too large = too mature, moves are slow. |
| Gini Coefficient | Gini ≤ 0.85 | Not too concentrated in a few wallets (whale-controlled). |
| Monthly Pump Cap | 30d change ≤ 500% | Already pumped — late to the party. |
| All-Zero Guard | Must show non-zero price changes | Skip dormant/dead subnets. |
| Accelerating Sell | NOT (1d < −5% AND 7d < −5%) | Don't catch a falling knife that's accelerating down. |
| Flat Momentum | NOT (30d < 3% AND 7d < 3%) | Going nowhere — no edge. |
| Dual Downtrend | NOT (7d < 0 AND 30d < 0) | Confirmed downtrend on both timeframes. |
| Structural Decline | 30d change > −10% | In structural collapse. |
| Day Crash | 1d change > −20% | Flash-crashing — wait for it to find a floor. |
| Immunity Period | Not in startup/immunity mode | Brand-new subnets are protected from emission and not yet tradeable normally. |
| Blacklist | Not in config/blacklist.json | Permanent skips (broken subnets, known scams, etc). |
Even after a subnet scores well, these filters block the actual buy if the entry timing looks unsafe. All run on the surviving candidates after scoring.
| Filter | What It Checks | Why |
|---|---|---|
| EMA Direction | Price must be above the 18-period 4h EMA. | Don't buy into a downtrend. The 18 EMA on 4h candles ≈ a 3-day trend filter. |
| EMA Spike | Price must have been above the EMA for at least 2 consecutive 4h closes. | One candle above isn't a trend — it's a wick. Wait for confirmation. Bypassed by dip-buy below. |
| Parabolic Guard | Latest 4h candle range can't exceed 1.5× the recent average range. | A single super-wide candle is the top of a parabolic move more often than the start of one. |
| RSI Overbought | RSI(25) must be below 75 — relaxed to 82 for mature trends (12+ candles above EMA). | Don't buy at peak exhaustion. Mature trends earn more rope. |
| Price Drift | Live price can't have drifted more than 5% from the price used in scoring. | If price moved a lot between scoring and the buy, our data is stale — abort and re-evaluate next cycle. |
| Re-entry Cooldown | Must wait 4 hours after selling a subnet before re-buying it. | Prevents whipsawing into the same name on noise. |
| BB Squeeze | If Bollinger Bands are squeezed (< 5% width), require a clear breakout above the upper band before entry. | Avoid buying inside a coil — wait for direction. |
Tier caps run after scoring but before the buy. They can either downgrade a position's tier or skip the entry entirely, even when the score is good. The strictest applicable cap wins.
| Cap | Status | Rule | Effect |
|---|---|---|---|
| Extended Above EMA extended_above_ema_block_v1 | enabled | Price more than 7% above the 10-period 4h EMA. | Hard skip. Catches "we're chasing a candle that already ran" entries. Sweep showed it blocks 11 of 99 entries, removing 3 of the 12 worst KDJ-only losers. |
| BB Top Block bb_top_block_v1 | disabled | Price above the upper Bollinger band. | Would cap entry size to T3 (small probe) rather than hard-skip. Kept on the shelf in case we want softer behavior. |
| Bear Structure Skip bear_structure_skip_v1 | disabled | 10 EMA below 18 EMA AND last bullish crossover more than 30 candles ago. | Would hard-skip "dead structure" subnets. Off pending more data — risk of skipping early bottom reversals. |
Lets us enter on a fresh "crossing up through the EMA from below" setup that the standard EMA Spike filter would otherwise block. The earliest, lowest-risk entry pattern Gordy uses.
Triggers a bypass when ALL of these hold:
When all five fire, the EMA Spike filter is skipped for that subnet on that candle. EMA Direction, Parabolic Guard, RSI, drift, cooldown, and tier caps all still apply.
dip-elig tag, so you can see what we'd have caught if another filter hadn't fired.Layered scoring + exit logic that tracks whether a position is riding a clean, confirmed trend off either the 10 EMA or the 18 EMA. Lets us be patient with real trend riders and aggressive about cutting fake ones.
For each subnet we pick a primary EMA (10 or 18) based on which one price has been bouncing off cleanly. "Bouncing" = a candle body within 0.8% of the EMA, followed by a green candle that leaves it. Wicks don't count.
Before any buy, we look at the last 30 4h candles and check whether the setup qualifies as a confirmed trend on entry:
If price is more than 10% above the 10 EMA, entry is blocked — we're not chasing a parabolic move regardless of score.
Replaces the legacy flat flow-reversal exit. Behavior depends on where price sits relative to the primary EMA:
If the position is CONFIRMED and a 4h candle closes below the 18 EMA, the position is full-sold immediately. Trend is broken.
Monitor cycles every 15 minutes. A position is held for at least 2 hours minimum before any exit rule (other than the hard stop loss / kill switch) can fire. Then the rules below evaluate in order.
Activates once a position has hit +10% ROI. Then sells if price drops by the trail amount from the running peak:
| Tier at activation | Trail % |
|---|---|
| Conviction / Tier 1 | 14% |
| Tier 2 | 10% |
| Tier 3 | 7% |
As gains grow, the trailing stop tightens automatically — we protect a bigger share of the move:
| ROI | Trail tightens to |
|---|---|
| +55% | 12% |
| +75% | 8% |
| +90% | 6% |
| ROI Hit | Trim | Notes |
|---|---|---|
| +14% | 15% of position | First skim — locks in some win even if it reverses. |
| +29% | 15% of position | Second skim. |
| +60% | 20% of position | Big skim — by here we've locked 50% of the position. The rest rides on the trailing stop. |
After a TP trim, the rebalancer cannot top the position back up to the same tier — only to a higher tier earned via ladder upgrade. Stops trims from being silently undone.
If the score drops below the threshold for 2 consecutive checks, the position is sold — its fundamentals have deteriorated.
| Tier at entry | Decay threshold |
|---|---|
| Conviction / Tier 1 / Tier 2 | 20.5 |
| Tier 3 | 22 |
3 consecutive 4h candles of net outflow AND score has slipped below the tier threshold → exit. (See Trend Confirmed above for the zone-based variant that's live now.)
| Tier | Score must be below |
|---|---|
| Conviction / Tier 1 | 28 |
| Tier 2 | 30 |
| Tier 3 | 35 |
Price closes below the 18-period 4h EMA for 5 consecutive candles → exit. The trend is broken — get out.
Position drops −12% from entry → exit immediately. No min-hold delay applies here.
If a single position grows past 20% of the portfolio (a runner), trim back to 10%. Prevents concentration risk.
Every buy now delegates the stake to the highest-yield validator on that subnet, rather than staying on our own hotkey. That picks up emission yield Gordy was earning and we weren't.
api.get_best_validator(netuid).--include-hotkeys validator_ss58 so externally-delegated stakes still unstake cleanly.