Fraud and Market Integrity
Hannibal — Threat Assessment, Detection, and Prevention February 2026
This document covers every fraud and integrity threat relevant to Hannibal, and the defenses we build against them. It is the single source of truth — there is no separate fraud doc or manipulation doc.
1. Context
How Hannibal Works
We are a B2B2C platform. Skins are our customers. Master agents are their customers. Punters are agents' customers. We never talk to punters directly. Agents handle cash off-platform. We handle points on-platform.
This means: no KYC, no payment fraud, no chargebacks. Those threats do not exist here. But it also means we cannot see the full picture of who is who. A coordinated ring of punters under different agents looks like unrelated activity unless we actively look for it.
The Multiplier (Foundation)
Every bet arrives in points. A 10,000-point bet could be worth $10,000 or $120 depending on which master agent tree the punter sits under. The skin assigns a fixed multiplier to each master agent:
$$betUsd = stakePoints \times masterAgentMultiplier$$
This is a B2B commercial parameter. Everything below the master agent (sub-agent markups, punter-facing rates) is their commercial business and outside our risk boundary.
Without this multiplier, we cannot: calculate real exposure, compare bets across agent trees, detect coordinated rings, set meaningful velocity limits, or run any fraud detection that involves dollar amounts. The multiplier must be implemented first.
Exchange-Only Operation
For the first few months, all bets go to Betfair exchange. There is no internal book-making. Every bet is a limit order placed on Betfair.
This simplifies our defense model. The enforcement actions available to us are:
| # | Action | Description |
|---|---|---|
| 1 | REJECT | Block the bet entirely |
| 2 | CAP | Allow the bet but reduce the stake |
| 3 | FLAG | Allow the bet but mark the user for review |
| 4 | RESTRICT | Add the user to a restricted list (lower limits) |
| 5 | BAN | Refuse all bets from the user |
| 6 | ALERT | Notify admin for manual investigation |
| 7 | DELAY | Hold cancellation requests (for flagged users) |
We do not have a routing lever (no internal book to route away from). Every defense in this document uses only these seven actions.
What Betfair Actually Supports
| Order Type | Description | Used by Hannibal? |
|---|---|---|
| LIMIT | Standard limit order. You specify price and size. If matching liquidity exists at your price or better, it matches immediately. Unmatched portion sits in the book. | ✅ Yes (only type) |
| LIMIT_ON_CLOSE | Limit order for Starting Price auction. Only available on horse/greyhound racing. | ❌ No |
| MARKET_ON_CLOSE | Market order for Starting Price auction. Only available on horse/greyhound racing. | ❌ No |
There is no true market order on Betfair for immediate execution. All our orders are LIMIT orders. But large limit orders still consume depth and move the market. A $10,000 limit order on a market with $15,000 available will eat through the depth and shift the line. This matters for manipulation detection.
Fraud Tolerance Philosophy
The goal is not to block every suspicious bet. The goal is to make fraud economically unviable. A manipulator who faces stake caps, velocity limits, and device tracking will find that the cost of attempting fraud exceeds the possible payoff. We detect, limit damage, and make manipulation unprofitable.
2. Threat Catalog
Every threat is tagged with severity and mapped to the defenses that address it.
Category A: Market Manipulation
| ID | Severity | Threat | Description | Defenses |
|---|---|---|---|---|
| A1 | 🟠 HIGH | Self-Manipulation | Single user places a large bet to move the Betfair line, then bets the other side at the shifted price. Works best on thin markets where a small bet moves the line significantly. | Thin market caps (3.2), Velocity limits (3.1), Odds movement tracking (5.2) |
| A2 | 🔴 CRITICAL | Coordinated Ring | Multiple users under different agents coordinate bets. User A backs to move the line. User B (different agent tree) lays at the shifted price. Agent isolation means only the platform can see cross-agent patterns. | Opposing bet detection (5.1), Network analysis (5.6), Device/IP correlation (6.4) |
| A3 | 🟠 HIGH | Cross-Platform Manipulation | User manipulates a thin market via Hannibal, then exploits the shifted price on another platform (or directly on Betfair via a separate account). Hannibal only sees one side. | Thin market caps (3.2), Odds movement tracking (5.2), Velocity limits (3.1) |
| A4 | 🔴 CRITICAL | Thin Market Exploitation | Markets with <$1,000 available liquidity. Any bet of meaningful size moves the line. Most dangerous attack surface — cost to manipulate is lowest. | Thin market caps (3.2), Minimum liquidity gate (3.3) |
Category B: Platform Exploitation
| ID | Severity | Threat | Description | Defenses |
|---|---|---|---|---|
| B1 | 🔴 CRITICAL | Agent Settlement Gaming | Agent creates ghost punter accounts, allocates points, coordinates bets so ghosts win. At weekly settlement, platform pays the agent. The agent pockets settlement — no real person on the other end. A smart fraudulent agent sets booking to 0% to keep full payout. | Win rate monitoring (5.3), Agent-level anomaly (5.6), Punter creation velocity (3.1) |
| B2 | 🟠 HIGH | Odds Staleness Exploitation | Gap between displayed odds and live Betfair odds. In fast in-play markets, user bets at stale odds. placeOrder already fetches a live betslip before execution (mitigates partially), but persistent exploitation should be flagged. | Timing analysis (5.4), Staleness tracking |
| B3 | 🟠 HIGH | Insider / Court-Siding | Betting on outcomes already known due to delayed feeds or being at the venue. Someone at the ground (or with faster data) bets on events already happened but not yet on platform. | Timing analysis (5.4), Event correlation (5.7), Geolocation (6.1), Win rate (5.3) |
| B4 | 🟡 MEDIUM | Arbitrage | User backs on Hannibal and simultaneously lays on another exchange, exploiting margin differences. Not directly harmful but extracts consistent edge without risk. | Win rate monitoring (5.3), Mechanical pattern detection (5.4) |
| B5 | 🟡 MEDIUM | Bot / Automation | Telegram bot and AI chat are easy to script. Bots scan markets and place bets faster than humans. Sub-human response times, perfectly regular intervals, fixed stake sizing. | Rate limiting (3.1), Device fingerprinting (6.4), Behavioral biometrics (6.5) |
| B6 | 🟠 HIGH | Cancellation Exploitation | User places bet, waits for market to move, cancels if unfavorable. Effectively a free option. High cancel-to-match ratio combined with fast latency suggests systematic exploitation. | Cancellation tracking (5.5), DELAY for flagged users |
| B7 | 🟡 MEDIUM | Booking Manipulation | Agent adjusts booking % to amplify settlement gaming. Lowers booking before ghost wins, raises after. Amplifies attack B1. | Booking change correlation (5.6), Agent audit triggers |
Category C: Identity and Access
| ID | Severity | Threat | Description | Defenses |
|---|---|---|---|---|
| C1 | 🟠 HIGH | Multi-Accounting | Same person holds accounts under multiple agents to bypass per-user limits or hide coordinated betting (attack A2). | Device fingerprinting (6.4), IP correlation (6.3), Behavioral biometrics (6.5) |
| C2 | 🟠 HIGH | Geolocation Fraud | User bets from restricted jurisdiction by spoofing location. Or uses location advantage for court-siding while spoofing a different location. | Geolocation validation (6.1), GPS vs IP mismatch (6.1) |
| C3 | 🟠 HIGH | VPN / Proxy Usage | User hides real IP behind VPN, proxy, or TOR to evade geographic restrictions, avoid IP-based correlation, or mask multi-accounting. | VPN detection (6.2), Datacenter IP detection (6.2), TOR exit node blocking (6.2) |
| C4 | 🟡 MEDIUM | IP Manipulation | User rapidly switches IPs to avoid rate limiting or IP-based detection. Or multiple users share the same IP (may be legitimate agent operation). | IP analysis (6.3), IP hopping detection (6.3), Shared IP monitoring (6.3) |
| C5 | 🟡 MEDIUM | Device Spoofing | User alters device fingerprint to appear as different devices, evading multi-account detection. Tools exist to randomize browser fingerprint. | Advanced fingerprinting (6.4), Consistency tracking (6.4), Behavioral biometrics (6.5) |
| C6 | 🟢 LOW | Account Sharing / Takeover | Multiple people use the same account or account is compromised. | Session anomaly (6.5), Geographic impossibility (6.1), Device change alerts (6.4) |
Category D: Lower Priority (Monitor Only)
| ID | Severity | Threat | Description |
|---|---|---|---|
| D1 | 🟢 LOW | Settlement Period Gaming | Agent times punter activity around weekly settlement to minimize platform payment. Not directly fraudulent but exploits timing. |
| D2 | 🟢 LOW | Points Allocation Abuse | Circular allocation schemes or agents using punter balances as float. Monitor for unusual allocation patterns. |
| D3 | 🟢 LOW | AI Chat Prompt Injection | Users trying to manipulate the AI to bypass bet validation or extract system information. Already mitigated by adversarial testing (100% pass rate on 46 tests). |
3. Static Defenses (Day 1)
These run on every bet, in the hot path, before the bet reaches Betfair. They work with zero historical data. Target latency: <5ms total for all checks combined.
3.1 Velocity Limits
Cap how fast and how much a user (or agent tree) can bet. Uses Redis sorted sets with sliding windows for O(log n) lookups.
| Scope | Limit | Window |
|---|---|---|
| Per user — USD | $5,000 | Rolling 1 hour |
| Per user — count | 30 bets | Rolling 1 hour |
| Per agent tree — USD | $50,000 | Rolling 1 hour |
| Per agent tree — count | 500 bets | Rolling 1 hour |
| Per user per fixture — USD | $2,000 | Rolling 1 hour |
| Per user per fixture — count | 10 bets | Rolling 1 hour |
All amounts calculated using: betUsd = stakePoints × multiplier
When a limit is hit:
- REJECT the bet
- FLAG the user (increment risk score, Section 4)
- ALERT admin if the limit was hit by >50%
Pseudocode: checkVelocity()
function checkVelocity(userId, agentTree, fixtureId, betUsd):
userHourUsd = redis.ZRANGEBYSCORE("vel:user:{userId}:usd", now - 3600, now).sum()
if userHourUsd + betUsd > USER_HOUR_USD_LIMIT:
return REJECT("velocity_user_usd")
userHourCount = redis.ZCARD("vel:user:{userId}:count", now - 3600, now)
if userHourCount + 1 > USER_HOUR_COUNT_LIMIT:
return REJECT("velocity_user_count")
treeHourUsd = redis.ZRANGEBYSCORE("vel:tree:{agentTree}:usd", now - 3600, now).sum()
if treeHourUsd + betUsd > TREE_HOUR_USD_LIMIT:
return REJECT("velocity_tree_usd")
// Record this bet
redis.ZADD("vel:user:{userId}:usd", now, betUsd)
redis.ZADD("vel:user:{userId}:count", now, betId)
redis.ZADD("vel:tree:{agentTree}:usd", now, betUsd)
redis.EXPIRE all keys at 2 hours
return ALLOW
| Metric | Value |
|---|---|
| Time complexity | O(log n) per check (Redis sorted set) |
| Space complexity | O(n) where n = bets in window |
| Latency impact | ~1ms (Redis round trip) |
3.2 Thin Market Stake Caps
If a market has very little liquidity, cap the maximum stake to prevent any single bet from moving the line significantly.
| Liquidity Available | Action |
|---|---|
| < $500 | REJECT — market too thin for safe execution |
| $500 – $1,000 | CAP bet to 10% of available liquidity on the relevant side |
| > $1,000 | Normal processing |
- Available liquidity = sum of all depth levels on the relevant side (
backDepthfor back bets,layDepthfor lay bets) from Betfair, converted to USD. - We already fetch and cache
backDepthandlayDeptharrays inCanonicalPrice. This check reads from that cache. - FLAG user if they repeatedly hit thin markets (>5 thin market bets per day).
- Latency: ~0ms (reads from in-memory price cache)
3.3 Large Bet Stake Caps (Progressive)
For bets that consume a significant percentage of available liquidity, progressively cap the stake. Since all bets go to exchange, the lever is stake capping, not routing.
Step 1: Calculate liquidity consumption percentage:
availableLiquidity = sum of depth levels on relevant side
betUsd = stakePoints × masterAgentMultiplier
consumptionPct = betUsd / availableLiquidity
Step 2: Apply progressive caps:
| Consumption % | Cap Applied |
|---|---|
| 0% – 10% | No cap (bet goes through as-is) |
| 10% – 30% | Cap at 30% of available liquidity |
| 30% – 50% | Cap at 20% of available liquidity |
| 50%+ | Cap at 10% of available liquidity |
Step 3: If the bet was capped, return the maximum allowed stake so the user can adjust. Do not silently reduce.
Example: Betfair shows $20,000 available on Arsenal to back. Punter bets 15,000 points with multiplier 1.0 = $15,000. Consumption = 75%. Cap: 50%+ band → max 10% = $2,000. User is told the max allowed stake.
Example: Same market. Different punter bets 500,000 points with multiplier 0.012 = $6,000. Consumption = 30%. Cap: 10–30% band → max 30% = $6,000. Bet goes through at full stake (happens to equal the cap).
Latency: ~0ms (reads from price cache)
3.4 Geolocation Gate
Basic geolocation check on every bet. More advanced analysis in Section 6.
- Extract IP from request
- GeoIP lookup (MaxMind or similar, local database)
- Check against restricted country list
| Condition | Action |
|---|---|
Country in BLOCKED_COUNTRIES list | REJECT + ALERT admin |
| IP is known datacenter/hosting provider | FLAG (may be VPN — see 6.2) + increment risk score |
Latency: ~1ms (local GeoIP database lookup)
4. User Risk Score
Every user gets a numerical risk score stored in Redis. The score starts at 0 and increases based on measurable signals. It determines what enforcement actions apply. The score is checked in the bet placement hot path. Target latency: <3ms.
How the Score Is Calculated
| # | Signal | Description | Weight |
|---|---|---|---|
| 1 | Odds Movement Correlation | User's bets consistently precede favorable odds movement (>2% in their favor within 60s). Measured over rolling 50-bet window. | +5 per qualifying bet |
| 2 | Cancellation Pattern | High cancel-to-match ratio. Places bets, cancels when market moves against, keeps when favorable. | +10 if ratio >40% (20+ bets), +20 if >60% |
| 3 | Velocity Pattern | Repeated hits against velocity limits — not a one-off but a pattern of testing boundaries. | +5 per hit (decays -1/day) |
| 4 | Thin Market Concentration | Disproportionately bets on thin markets where manipulation is easiest. | +10 if >30% of bets on markets with <$2K liquidity |
| 5 | Win Rate | Sustained high win rate above statistical norms. Could indicate insider knowledge or exploitation. | +10 if >55% over 100+ bets, +20 if >65% |
| 6 | Device/IP Anomaly | IP changes frequently, VPN detected, device fingerprint mismatch. | +10 VPN, +15 shared fingerprint, +5 per IP change |
- Total score = sum of all active signals (capped at 100)
- Decay: signals from >30 days ago are halved
Enforcement Based on Score
| Score Range | Level | Enforcement |
|---|---|---|
| 0 – 30 | 🟢 NORMAL | No restrictions |
| 30 – 60 | 🟡 RESTRICT | Stake caps at 50% of normal limits |
| 60 – 80 | 🟠 RESTRICT TIGHT | Stake caps at 25% of normal limits + FLAG for manual review |
| 80+ | 🔴 BAN | Add to restricted list, reject all bets + ALERT admin |
Storage: Redis hash — risk:user:{userId} = {score: 45, signals: {...}, updatedAt: ...}
Lookup: Single HGETALL — ~1ms
Score updates happen:
- After each bet placement (async, does not block the bet)
- After each bet settlement (win/loss changes win rate signal)
- After each cancellation (updates cancel ratio signal)
- Periodically (every 15 minutes) for time-decay
5. Async Detection (Background)
These systems do not block bets. They run in the background, process events after the fact, generate alerts, and feed signals into the risk score (Section 4). They subscribe to Redis events that the bet placement flow already emits.
5.1 Opposing Bet Detection
Look for back and lay on the same outcome from different agent trees within a short time window. This is the signature of a coordinated ring (threat A2).
Match criteria (all must be true):
- Same fixtureId, marketId, outcomeId
- Opposite side (one back, one lay)
- Different agent tree (different master agent ancestry)
- Within 5-minute window
- Similar USD value (within 3× of each other)
If matched: ALERT (high priority — possible coordinated ring) + increment risk score for both users (+20)
Storage: Recent bets indexed by fixture+market+outcome in Redis with 10-minute TTL. O(1) lookup per bet.
5.2 Odds Movement Tracking
Track how the market moves after each bet. If a user's bets consistently precede large odds movements, they may have information advantage.
| Timing | Action |
|---|---|
| After each bet | Record odds at bet time |
| +30s, +60s, +300s | Check odds again, calculate movement: (newOdds - betOdds) / betOdds |
Flag conditions:
- Average movement >1.5% in user's favor over 20+ bets → feed into risk score (Signal 1)
- Any single bet followed by >5% movement within 60 seconds → ALERT
- If avgMovement >1.5% over 50 bets → ALERT + RESTRICT
5.3 Win Rate Monitoring
Track win rates at multiple granularities:
| Scope | Flag | Restrict | Alert |
|---|---|---|---|
| Per user (overall, by sport, in-play) | Win rate >55% over 100+ bets | Win rate >65% over 100+ bets | — |
| Per agent tree | — | — | Win rate >55% over 500+ bets (possible settlement gaming — threat B1) |
Note: Win rate alone is not proof of fraud. Many legitimate sharp bettors have high win rates. The win rate signal feeds into the risk score along with other signals. Only the combination triggers enforcement.
5.4 Timing and Staleness Analysis
Track the time gap between displayed odds and live odds at execution. Users who consistently bet only when displayed odds are better than live odds are exploiting staleness.
| Metric | Flag Threshold | Alert Threshold |
|---|---|---|
| Average gap (displayed vs live) | >0.05 (5 ticks) over 20+ bets | — |
| % of bets where displayed > live | — | >80% |
| Bet-to-event latency (in-play) | — | 1-2 seconds before major event (court-siding signal) |
Cross-reference bet-to-event latency with cricket/football event timestamps for court-siding detection (threat B3).
5.5 Cancellation Pattern Tracking
| Metric | Formula |
|---|---|
| Cancel ratio | cancellations / (cancellations + matched bets) |
| Avg time to cancel | Average time between placement and cancel |
| Profitable cancel ratio | Cancels where market moved against / total cancels |
| Condition | Action |
|---|---|
| cancelRatio >40% over 20+ bets | FLAG |
| cancelRatio >60% over 20+ bets | RESTRICT |
| profitableCancelRatio >70% | ALERT (systematic exploitation) |
Enforcement for flagged users: DELAY cancellation by 3 seconds. Removes the free option — user can still cancel but the 3-second window means the market has time to move. Only flagged users get the delay; normal users cancel instantly.
5.6 Network Analysis
Look for patterns that span multiple users and agent trees. Runs as a batch job every 15 minutes, scanning recent bets for cross-tree correlations.
Signals:
- Multiple users under different agents betting on the same obscure fixture within a short window
- Users with similar betting patterns (same fixtures, timing, stake ratios) across different agent trees
- Agent trees with correlated win/loss patterns
- Unusual concentration of bets from a single tree on one fixture (>$10,000 aggregate)
Actions: ALERT for manual investigation. Network graphs generated for admin dashboard.
5.7 Cricket Event Correlation
For cricket in-play markets, correlate bet timing with match events. We already subscribe to cricket event channels (cricket:ball, cricket:wicket, etc.) via cricketMonitoringService.
Events tracked: Wickets (bowled, caught, LBW, run out, stumped), Boundaries (fours, sixes), Toss result, Innings breaks, Powerplay transitions
Detection logic: For each bet on a cricket in-play market, find the nearest match event within ±5 seconds. If a bet was placed 1-3 seconds BEFORE a major event (wicket, six) and the bet profits from that event:
- Strong court-siding signal (threat B3)
- Increment risk score (+15)
- ALERT if pattern repeats >3 times for same user
Leverages existing cricket data pipeline (Roanuz feed, ball-by-ball data, cricketIdResolver).
6. Identity and Device Layer
Critical for detecting multi-accounting (C1), geolocation fraud (C2), VPN usage (C3), and coordinated rings that hide behind multiple identities.
6.1 Geolocation
Every request carries an IP address. Mobile apps can also provide GPS coordinates. Use both to build a location picture.
| Check | Condition | Action |
|---|---|---|
| Country Restriction | Country in blocked list (configurable per skin) | REJECT |
| GPS vs IP Mismatch | App GPS says Mumbai, IP geolocates to London | FLAG + risk score +10 |
| Travel Impossibility | User IP in New York at 2:00 PM, Mumbai at 2:30 PM | ALERT + RESTRICT (account sharing or VPN hop) |
| Venue Proximity | GPS within 1km of active sports venue + betting on that event | FLAG for court-siding review + risk score |
6.2 VPN and Proxy Detection
| Detection Method | Description | Action |
|---|---|---|
| Known VPN IP Ranges | Commercial VPN IP databases (IPQualityScore, ip2proxy). Updated daily. | FLAG + risk score +10 |
| Datacenter IP | IPs from AWS, GCP, Azure, DigitalOcean, OVH, Hetzner. MaxMind GeoIP includes ISP/org data. Almost never legitimate residential. | FLAG + risk score +10. If combined with other signals: RESTRICT |
| TOR Exit Nodes | Publicly listed, updated frequently. | REJECT all bets. No exceptions. |
| Residential Proxies | Harder to detect. Look for: IP serving >3 user accounts, high request rate, low IP reputation score. | FLAG if IP serves >3 different user accounts |
6.3 IP Address Analysis
| Analysis | Description | Action |
|---|---|---|
| IP Reputation Scoring | Commercial IP reputation services. Factors: abuse history, spam lists, proxy detection, hosting provider. Score cached in Redis (24h TTL). | Feed into risk assessment |
| Shared IP Detection | Multiple users from same IP under DIFFERENT agent trees is highly suspicious. Same IP + same tree may be legitimate (agent's office). | ALERT if >2 users from different trees share IP within 24h |
| IP Hopping | User changes IP >5 times within a 1-hour session. Could be VPN cycling. | FLAG + risk score +5 per change |
| IP-to-User Graph | Build graph of IP↔user relationships. Two users sharing 3+ IPs over a week = likely same person or coordinated. | ALERT for multi-accounting investigation |
6.4 Device Fingerprinting
The strongest signal for multi-accounting detection.
Web/PWA Fingerprint (collected via JavaScript):
- Canvas fingerprint (rendering differences across GPUs)
- WebGL renderer and vendor
- Installed fonts
- Screen resolution and color depth
- Timezone and language settings
- Browser plugins and extensions
- Audio context fingerprint
- Hardware concurrency (CPU cores)
- Combined into a single hash — ~95% unique across devices
Mobile Fingerprint:
- Device model and OS version
- Screen dimensions
- Advertising ID (iOS IDFA / Android GAID, if available)
- Installed keyboard language
- Battery level patterns (surprisingly distinctive)
| Detection | Condition | Action |
|---|---|---|
| Multi-Account | Same fingerprint hash under two different userIds (especially different agent trees) | ALERT (high priority) + risk score +15 for both |
| Fingerprint Spoofing | Fingerprint changes significantly between sessions (different canvas, WebGL, fonts) | FLAG + risk score +10 |
Storage: device_fingerprints table — (userId, fingerprintHash, firstSeen, lastSeen, metadata)
6.5 Behavioral Biometrics
The hardest signal to fake. Even with VPNs, device spoofing, and multi-accounting, behavioral patterns are unique.
| Category | Signals Collected |
|---|---|
| Typing Patterns (Telegram/AI chat) | Avg time between keystrokes, common typo patterns, message length distribution, response time after bot messages |
| Mouse/Touch Patterns (web/PWA) | Movement speed and curvature, click precision, scroll behavior, touch pressure and angle (mobile) |
| Session Behavior | Time of day active, session duration, navigation patterns, time between opening match card and placing bet |
Two accounts with very similar behavioral signatures under different agent trees are likely the same person.
Timeline: Month 2-3 feature. Data collection starts day 1, but analysis requires baseline accumulation.
7. ML/AI and Learning (Month 3+)
The rule-based systems (Sections 3–6) protect on day 1. But rules have gaps — they catch known patterns, not novel ones. After 2-3 months of data from the rule-based system, we layer on machine learning to catch what rules miss.
7.1 Baseline Building
Before detecting anomalies, we need to know what "normal" looks like.
Baselines to build (per sport, per market type, per match phase):
| Baseline | Description |
|---|---|
| Bet size distribution | Median, p95, p99 |
| Bet frequency | Normal bets per user per hour |
| Win rate distribution | Expected range by segment |
| Odds movement after bet | Normal movement patterns |
| Cancel ratio | Expected cancellation behavior |
| Session patterns | Duration, bet spacing, time-of-day |
| IP change frequency | Normal IP behavior |
How: Aggregate 2-3 months of data. Exclude flagged/restricted users (not "normal"). Compute statistics per segment. Storage in Postgres, refreshed weekly via batch job.
7.2 Anomaly Detection
| Method | How It Works | Action |
|---|---|---|
| Z-Score | For each metric, calculate standard deviations from baseline. | z > 3 on any metric → FLAG. z > 2 on 3+ metrics → ALERT |
| Clustering | Group users by behavioral features (bet size, frequency, sport, win rate, cancel ratio, sessions). DBSCAN or k-means on normalized vectors. | Users in same cluster but different agent trees → possible coordination (A2, C1) |
| Sequence Analysis | Look at sequences of bets, not individual bets. Pattern mining on bet histories. | Detect strategies (increasing stakes, alternating back/lay) that individual rules miss |
7.3 ML Model Ideas
| Model | Input | Output | Algorithm | Use |
|---|---|---|---|---|
| Risk Score Predictor | User's 50 most recent bet features (stake, odds, timing, liquidity, win/loss, cancel) | P(fraudulent) | XGBoost / LightGBM | Replace/augment rule-based risk score |
| Network Ring Detector | Graph of user-IP-device-fixture relationships | Clusters of coordinated users | Graph neural network or Louvain community detection | Detect coordinated rings |
| Odds Movement Predictor | Current market state, recent bets, event timeline | Expected movement in next 60s | Regression model | If actual > 2× predicted after user's bet → insider signal |
| Behavioral Fingerprint | Typing speed, click patterns, session behavior | User identity probability vector | Embedding model | Detect multi-accounting |
All models run asynchronously. They do not block bet placement. They feed into the risk score and generate alerts.
7.4 Log Post-Processing
| Report | Frequency | Contents |
|---|---|---|
| Daily | Every 24h | Users hitting velocity limits, capped bets (count + $ capped), opposing bet pairs, risk score changes >20, VPN/datacenter IPs, device fingerprint collisions |
| Weekly | Every 7d | Top 10 users by risk score, top 10 by win rate, top 10 agent trees by aggregate win rate, cross-agent patterns, thin market concentration, false positive review |
The false positive review is critical — if too many clean users are flagged, thresholds need loosening.
7.5 The Feedback Loop
This is why we build the rule-based system first. Without it, there is no training data for ML. Without ML, the rule-based system cannot improve beyond its initial heuristics. They are symbiotic.
8. Integration and Build Order
8.1 What Exists in the Codebase
| Service / Infra | File | What It Does | Relevant To | Status |
|---|---|---|---|---|
| orderService | backend/src/services/orderService.ts | Places bets, converts points to USD, calls filterEngine for routing, submits to Betfair via PAL. | All defenses — this is THE hot path. Every defense in Sections 3-4 must hook in before Betfair. | EXISTS. Needs pre-bet defense hooks. |
| filterEngine | backend/src/services/bbook/filterEngine.ts | Determines routing. Checks enabled, odds/stake eligibility, sharp list, capacity. | When B-Book is disabled, returns "route to exchange." Can be repurposed as fraud gate — allow/cap/reject decisions. | EXISTS. Needs repurposing. |
| sharpUserService | backend/src/services/bbook/sharpUserService.ts | Manages restricted user list. Redis cache + Postgres. Fast isSharp() check. | Risk score (§4) enforcement. Score 80+ → restricted list. Existing RESTRICT/BAN actions can use this. | EXISTS. Needs extension to general "restricted" + risk score integration. |
| punterMonitoringService | backend/src/services/monitoring/punterMonitoringService.ts | Rule-based monitoring. Redis counters for per-punter daily bet count, stake, per-fixture counts. Evaluates conditions, fires alerts. | Async detection (§5). Already tracks bet_placed and bet_settled events. | EXISTS. Needs new monitors for opposing bets, odds movement, cancellation, network. |
| cricketMonitoringService | backend/src/services/monitoring/cricketMonitoringService.ts | Event-driven cricket monitoring. Subscribes to cricket:ball, cricket:wicket channels. | Cricket event correlation (§5.7). Already has the event pipeline. | EXISTS. Needs bet-event timestamp correlation. |
| Redis | backend/src/services/redis.ts | Cache service with typed keys, TTLs, wrap(). | All defenses: velocity (sorted sets), risk scores (hashes), opposing bet index, IP tracking. | EXISTS. New key patterns needed. |
| Prisma Schema | backend/prisma/schema.prisma | Models: User (agentId), Agent (parentAgentId), Order, SharpUser, UserAgent. | All defenses. | EXISTS. New tables needed. |
| Agent Tree | Agent.parentAgentId | Creates tree. Walk parentAgentId to null = master agent. | Velocity limits (§3.1) need per-tree aggregation. | EXISTS. |
8.2 What Needs to Be Built
| New Component | Purpose | Storage | Runs |
|---|---|---|---|
| Fraud Gate Service | Replaces filterEngine's routing with allow/cap/reject logic. Contains: velocity, thin market, large bet cap, geo gate, risk score lookup. | — | Hot path, <5ms total |
| Risk Score Service | Manages per-user risk scores (§4). Score calculation, signal tracking, enforcement lookup, decay. | Redis hash risk:user:{id} | Lookup in hot path (<3ms). Updates async. |
| Opposing Bet Detector | Detects back+lay on same outcome from different agent trees (§5.1). | Redis sorted set per fixture+market+outcome, 10-min TTL | Async after each bet |
| Odds Movement Tracker | Records odds at bet time, checks at +30s/+60s/+300s (§5.2). | — | Async scheduled checks |
| Identity Service | GeoIP, VPN detection, IP analysis, device fingerprint storage and matching (§6). | New Postgres tables (ip_logs, device_fingerprints) | GeoIP in hot path (~1ms). Deep analysis async. |
New Postgres Tables:
| Table | Columns |
|---|---|
ip_logs | userId, ip, country, city, isVpn, isDatacenter, isTor, reputationScore, createdAt |
device_fingerprints | userId, fingerprintHash, firstSeen, lastSeen, metadata |
risk_score_history | userId, score, signals, recordedAt |
fraud_alerts | userId, alertType, severity, details, reviewedBy, reviewedAt, verdict, createdAt |
New Redis Key Patterns:
| Key Pattern | Type | Purpose |
|---|---|---|
vel:user:{userId}:usd | Sorted set | Velocity $ tracking |
vel:user:{userId}:count | Sorted set | Velocity count |
vel:tree:{treeId}:usd | Sorted set | Tree velocity |
vel:user:{userId}:fix:{id} | Sorted set | Per-fixture velocity |
risk:user:{userId} | Hash | Risk score + signals |
opposing:{fix}:{mkt}:{out} | Sorted set | Recent bets for opposing detection |
ip:user:{userId}:session | Set | IPs in current session |
device:{fingerprintHash} | Set | UserIds sharing fingerprint |
8.3 Bet Flow (Exchange-Only, With Fraud Defenses)
Post-settlement and cancellation flows:
| Event | New Processing | Existing Processing |
|---|---|---|
| Bet settlement | Update win rate tracking (§5.3), Update risk score signals (§4) | Existing settlement flow |
| Bet cancellation | If user flagged: DELAY 3s (§5.5), Update cancellation tracking | Existing cancellation flow |
8.4 Build Timeline
Phase summary:
| Phase | Timeline | Deliverable |
|---|---|---|
| Phase 0 | Week 1 | Multiplier field on Agent/Skin. pointsToUsd() uses per-agent multiplier. All USD calculations accurate. Prerequisite for everything. |
| Phase 1 | Week 1-2 | Fraud gate service. Velocity limits, thin market caps, large bet caps, geolocation gate. Every bet runs through fraud checks. Day 1 safe. |
| Phase 2 | Week 2-3 | Risk score service. 6 signals. Score lookup in fraud gate. Async updates. Admin dashboard. High-risk users get capped. |
| Phase 3 | Week 3-5 | Opposing bet detector, odds movement, win rate, cancellation tracking, cricket correlation, identity service, device fingerprinting, fraud alerts + admin review. Background detection running. |
| Phase 4 | Month 3+ | 2-3 months data accumulated. Baselines, anomaly detection, ML models, daily/weekly reports, feedback loop. ML-augmented fraud detection. |
8.5 Configurable Thresholds
All thresholds stored in platformSettings (Postgres) and cached in Redis. Admin can change them without code deployment.
Velocity
| Parameter | Default | Description |
|---|---|---|
USER_HOUR_USD_LIMIT | 5,000 | Max USD per user per rolling hour |
USER_HOUR_COUNT_LIMIT | 30 | Max bets per user per rolling hour |
TREE_HOUR_USD_LIMIT | 50,000 | Max USD per agent tree per rolling hour |
TREE_HOUR_COUNT_LIMIT | 500 | Max bets per agent tree per rolling hour |
USER_FIXTURE_HOUR_USD_LIMIT | 2,000 | Max USD per user per fixture per rolling hour |
USER_FIXTURE_HOUR_COUNT | 10 | Max bets per user per fixture per rolling hour |
Thin Market
| Parameter | Default | Description |
|---|---|---|
THIN_MARKET_THRESHOLD | $1,000 | Below this = thin market |
THIN_MARKET_CAP_PCT | 10% | Cap bet to this % of liquidity on thin markets |
ULTRA_THIN_THRESHOLD | $500 | Below this = reject entirely |
Large Bet Caps
| Parameter | Default | Description |
|---|---|---|
CAP_BAND_1_THRESHOLD | 10% | Consumption % triggering Band 1 |
CAP_BAND_1_LIMIT | 30% | Cap at this % of available liquidity |
CAP_BAND_2_THRESHOLD | 30% | Consumption % triggering Band 2 |
CAP_BAND_2_LIMIT | 20% | Cap at this % of available liquidity |
CAP_BAND_3_THRESHOLD | 50% | Consumption % triggering Band 3 |
CAP_BAND_3_LIMIT | 10% | Cap at this % of available liquidity |
Risk Score
| Parameter | Default | Description |
|---|---|---|
SCORE_RESTRICT_THRESHOLD | 30 | Score above this → 50% caps |
SCORE_RESTRICT_CAP_PCT | 50% | Stake cap for restricted users |
SCORE_RESTRICT_TIGHT | 60 | Score above this → 25% caps + FLAG |
SCORE_TIGHT_CAP_PCT | 25% | Stake cap for tight-restricted users |
SCORE_BAN_THRESHOLD | 80 | Score above this → BAN |
Geolocation & Cancellation
| Parameter | Default | Description |
|---|---|---|
BLOCKED_COUNTRIES | ["US", "GB", ...] | Per skin — configurable |
CANCEL_DELAY_SECONDS | 3 | Cancellation delay for flagged users |
CANCEL_RATIO_FLAG | 40% | Cancel ratio that triggers FLAG |
CANCEL_RATIO_RESTRICT | 60% | Cancel ratio that triggers RESTRICT |
Summary
The goal is not to block every suspicious bet. The goal is to make fraud economically unviable. A manipulator who faces stake caps, velocity limits, device tracking, and risk scoring will find that the cost of attempting fraud exceeds the possible payoff.
19 threats cataloged. 4 categories. Every threat mapped to specific defenses. Every defense mapped to existing or new codebase components. Build order ensures protection from day 1, with increasing sophistication over time.
End of document.