Fraud Detection System — Rules Reference
Architecture Overview
The fraud detection pipeline evaluates every bet through 26 rules across three tiers:
- Dimension Rules (5) — Always run. Produce a score 0–100 that feeds the severity engine.
- Deterministic Rules (18) — Conditionally triggered. Return a binary triggered/not-triggered result with severity.
- Fraud Signatures (3) — Composite detectors combining multiple signals for high-confidence detection.
Evaluation flow:
Bet placed → TimescaleDB (fraud_events)
→ EvaluationJob picks up bet (60s cycle live, 300s idle)
→ Run 5 dimension rules → DimensionScores
→ Run 18 deterministic rules → triggered RuleResults
→ Run 3 signatures → triggered RuleResults
→ ScoringEngine computes final severity
→ FlaggingService creates case / blocks user / publishes alert
Severity escalation:
| Condition | Severity |
|---|---|
| Any dimension ≥ 80 | RED |
| Two correlated dimensions both ≥ 60 | RED |
| Any triggered rule with severity RED | RED |
| Any dimension 60–79 | ORANGE |
| Any dimension 40–59 | YELLOW |
| All dimensions < 40, no rules triggered | GREEN |
Correlated dimension pairs (both ≥ 60 = RED):
- exchangeVsBookmaker + liquidityExploitation
- priceMovement + repetition
- identityLinkage + exchangeVsBookmaker
- identityLinkage + liquidityExploitation
Post-flag actions:
| Severity | Action |
|---|---|
| RED | User blocked 24h, fraud case created, alert published, 4h SLA |
| ORANGE | Fraud case created, alert published, 24h SLA |
| YELLOW | Visible in dashboard only |
| GREEN | No action |
Dimension Rules
These run on every bet and produce a continuous score from 0 to 100.
1. Exchange vs Bookmaker (dim_exchange_vs_bookmaker)
File: dimensions/exchangeVsBookmaker.ts
Detects: Price arbitrage — user betting at odds significantly better than the exchange midpoint.
How it works:
- Compares the user's bet odds against the exchange midpoint price at the time of the bet.
- Calculates the edge percentage:
(betOdds - exchangeMid) / exchangeMid. - Applies a staleness penalty — if the bookmaker price is stale (>30s old = score 0, 15–30s = 0.5x, 5–15s = 0.8x).
- Back bets getting better-than-mid odds are suspicious.
- Score scales with edge size: small edges score low, large edges score high.
Data used: Exchange ticks (EXCHANGE_TICK events from TimescaleDB), bookmaker ticks, bet odds.
2. Price Movement (dim_price_movement)
File: dimensions/priceMovement.ts
Detects: Courtsiding — betting before a favorable real-world event causes a price shift.
How it works:
- Compares the exchange midpoint at T-1 (before bet) and T+1 (after bet).
- If the price moves favorably for the user's position after the bet, this is suspicious.
- Requires a T+1 event marker (BALL, WICKET, GOAL, etc.) to complete evaluation.
- Accounts for market suspension timing between T and T+1.
- Returns
nullif no T+1 event exists yet (evaluation is deferred).
Data used: Exchange ticks, event markers (BALL, WICKET, GOAL, CARD, MILESTONE, OVER_COMPLETE).
3. Liquidity Exploitation (dim_liquidity_exploitation)
File: dimensions/liquidityExploitation.ts
Detects: Users consistently extracting value from thin or inefficient markets.
How it works:
- Calculates the ratio of available volume to total market volume at the time of the bet.
- Scores higher when the user bets into markets with low available liquidity.
- Timing relative to volume spikes is factored in — betting just before liquidity dries up is suspicious.
Data used: available_volume, total_market_volume from exchange tick events.
4. Repetition (dim_repetition)
File: dimensions/repetition.ts
Detects: Users repeating the same suspicious pattern across multiple bets.
How it works:
- Analyzes the user's 30-day bet history for consistency in market selection, bet side, and odds range.
- If >60% of bets follow the same pattern (same market type, same side, similar odds), the score increases.
- Higher consistency = higher score. Random/varied betting patterns score low.
Data used: User bet history (30 days from TimescaleDB), Prisma queries.
5. Identity Linkage (dim_identity_linkage)
File: dimensions/identityLinkage.ts
Detects: Multi-accounting — multiple accounts controlled by the same person.
How it works:
- Queries the
FraudIdentityClustertable for active clusters that include this user. - Scores based on cluster confidence (0–1 scaled to 0–100).
- Clusters are built from shared device fingerprints, IP addresses, payment methods, and betting pattern similarity.
Data used: FraudIdentityCluster (Prisma), cluster confidence scores.
Deterministic Rules
These run conditionally and return a binary triggered/not-triggered result.
6. Impossible Travel (DET_IMPOSSIBLE_TRAVEL)
File: rules/impossibleTravelRule.ts
Detects: Account compromise or sharing — two logins from geographically distant locations in an impossibly short time.
Logic: 2+ logins from different countries within 1 hour. Uses V3IpAccessLog and USER_LOGIN events.
7. Stake Escalation (DET_STAKE_ESCALATION)
File: rules/stakeEscalationRule.ts
Detects: Sudden increase in bet size suggesting inside information or compromised account.
Logic: Bet stake compared to user's 7-day rolling average. Thresholds: 3x = YELLOW, 5x = ORANGE, 10x = RED. Requires at least 3 historical bets for baseline.
8. Win Rate (DET_WIN_RATE)
File: rules/winRateRule.ts
Detects: Probability-adjusted win rate significantly higher than expected.
Logic: Compares actual win rate against expected win rate given the odds of bets placed. Uses V3ClvEntry data for CLV-adjusted analysis.
9. Opposite Side Same Market (DET_OPPOSITE_SIDE)
File: rules/oppositeSideRule.ts
Detects: Classic arbitrage/hedging — user bets both BACK and LAY on the same selection.
Logic: User places bets on both sides of the same selection within 30 seconds. Queries Prisma for matching bets.
10. Pattern Clone (DET_PATTERN_CLONE)
File: rules/patternCloneRule.ts
Detects: Coordinated betting — one user replicating another user's exact bet pattern.
Logic: Checks if this user's market/odds/stake combination matches another user's recent bet pattern on the same fixture. Queries fraud_events for market-level patterns.
11. Liquidity Dominance (DET_LIQUIDITY_DOMINANCE)
File: rules/liquidityDominanceRule.ts
Detects: Market manipulation — a single user's stake dominating available liquidity.
Logic: User's stake exceeds 30% of total market liquidity. Uses total_market_volume from exchange tick events.
12. Market Concentration (DET_MARKET_CONCENTRATION)
File: rules/marketConcentrationRule.ts
Detects: Unnatural focus on a single market suggesting information advantage.
Logic: Over 70% of user's bet portfolio concentrated on a single market across multiple fixtures.
13. Rapid Cashout (DET_RAPID_CASHOUT)
File: rules/rapidCashoutRule.ts
Detects: Low-risk arbitrage — placing a bet and immediately cashing out for guaranteed profit.
Logic: Cashout event within 5 seconds of bet placement.
14. Suspension Probing (DET_SUSPENSION_PROBING)
File: rules/suspensionProbingRule.ts
Detects: Information leakage — user placing bets just before markets get suspended.
Logic: Bets placed 2–5 seconds before a market suspension event. Suggests the user knows a suspension is coming (e.g., a wicket, goal, or injury is about to happen).
15. Pre-toss Concentration (DET_PRE_TOSS_CONCENTRATION)
File: rules/preTossConcentrationRule.ts
Detects: Insider knowledge of toss outcomes in cricket.
Logic: Heavy betting volume concentrated immediately before a TOSS event marker. Analyzed via TimescaleDB pool queries on fixture timeline.
16. Post-toss Sharp Bet (DET_POST_TOSS_SHARP)
File: rules/postTossSharpBetRule.ts
Detects: Sharp bettor exploiting toss result before market adjusts.
Logic: Large bet placed at high odds immediately after TOSS marker, before odds have fully adjusted.
17. Session Break Timing (DET_SESSION_BREAK_TIMING)
File: rules/sessionBreakTimingRule.ts
Detects: Exploiting predictable market transitions.
Logic: Bets placed exactly at session breaks (over changes, innings starts, session starts). Uses event markers to identify break timing.
18. Reverse Bet (DET_REVERSE_BET)
File: rules/reverseBetRule.ts
Detects: Hedging or bet correction behavior.
Logic: User places a bet on the opposite outcome on the same market shortly after an initial bet.
19. Agent Multiplier Spike (DET_AGENT_MULTIPLIER_SPIKE)
File: rules/agentMultiplierSpikeRule.ts
Detects: Agent-level manipulation — abnormal spike in multiplier requests.
Logic: An individual agent's multiplier requests spike 5x above their baseline within a short time window.
20. Cross-agent Migration (DET_CROSS_AGENT_MIGRATION)
File: rules/crossAgentMigrationRule.ts
Detects: Users hopping between agents to evade detection.
Logic: User moves between 2+ agents within 24 hours. Checks FraudAgentScore entries for migration patterns.
21. Deposit-Bet-Withdraw (DET_DEPOSIT_BET_WITHDRAW)
File: rules/depositBetWithdrawRule.ts
Detects: Money laundering or fraud churn — rapid deposit/bet/withdrawal cycle.
Logic: Complete deposit → bet → withdrawal cycle within 1–2 hours.
22. Persistent CLV Beater (DET_PERSISTENT_CLV_BEATER)
File: rules/persistentClvBeaterRule.ts
Detects: Professional/sharp bettor consistently beating closing line value.
Logic: Average CLV > 3% sustained over 50+ bets in a 30-day window. Uses V3ClvEntry data.
23. Steam Move Exploitation (DET_STEAM_MOVE)
File: rules/steamMoveExploitationRule.ts
Detects: Betting ahead of sharp market movements (steam moves).
Logic: User places bets just before sudden market movement spikes. Requires exchange volume spike detection from tick data.
Fraud Signatures
Composite detectors that combine multiple signals for high-confidence flagging.
24. Courtsiding Signature (SIG_COURTSIDING)
File: signatures/courtsiding.ts
Detects: Confirmed courtsiding pattern using multiple corroborating signals.
Logic:
- Primary: priceMovement dimension ≥ 60 AND market suspension occurs between T and T+1.
- Confirming: repetition dimension ≥ 60 OR >60% of user's in-play bets show pre-suspension pattern.
- Win rate check on in-play bets for statistical confirmation.
25. Multiplier Manipulation Signature (SIG_MULTIPLIER_MANIPULATION)
File: signatures/multiplierManipulation.ts
Detects: Systematic exploitation of the multiplier/odds system.
Logic:
- Multiplier edge > 5% on average.
- Consistent positive edge across 10+ bets.
- User payout ratio > 120% of expected value given the odds they received.
26. Multi-Accounting Signature (SIG_MULTI_ACCOUNTING)
File: signatures/multiAccounting.ts
Detects: Confirmed multi-accounting using multiple corroborating signals.
Logic:
- Identity linkage cluster confidence > 0.7.
- Device fingerprint or IP address matches across accounts.
- Similar betting patterns across linked accounts.
- Shared payment method detected.
How to Add a New Rule
Step 1: Create the rule file
Create a new file in backend/src/services/fraud/rules/ (for deterministic) or signatures/ (for composite):
// backend/src/services/fraud/rules/yourNewRule.ts
import type {
FraudRule,
EnrichedBet,
BetContext,
DimensionScores,
RuleResult,
} from '../types.js';
const RULE_ID = 'DET_YOUR_RULE_NAME';
const RULE_NAME = 'Your Rule Name';
export class YourNewRule implements FraudRule {
readonly id = RULE_ID;
readonly name = RULE_NAME;
readonly type = 'deterministic' as const;
readonly requiredContext = ['userBetHistory30d'];
// Pass prisma or tsdbPool via constructor if you need DB access
constructor(private prisma?: any) {}
async evaluate(
bet: EnrichedBet,
context: BetContext,
_scores?: DimensionScores,
): Promise<RuleResult> {
const base = { ruleId: this.id, ruleName: this.name };
// ── Your detection logic ──
const suspicious = false;
if (!suspicious) {
return {
...base,
triggered: false,
severity: 'GREEN',
confidence: 0,
details: { reason: 'Not triggered' },
};
}
return {
...base,
triggered: true,
severity: 'RED', // RED | ORANGE | YELLOW
confidence: 0.85, // 0.0 – 1.0
details: {
reason: 'Why this triggered',
// Include any supporting data for the case detail view
},
};
}
}
Step 2: Register in the rule registry
Edit backend/src/services/fraud/rules/ruleRegistry.ts:
// Add import at the top
import { YourNewRule } from './yourNewRule.js';
// Inside createRuleRegistry(), add the registration:
registry.register(new YourNewRule(prisma));
Step 3: Deploy
cd /root/bbook
docker compose -f docker-compose.bbook.yml up -d --build backend
No other changes needed. The evaluation pipeline automatically picks up registered rules.
Available data in evaluate()
| Parameter | Contents |
|---|---|
bet.userId | User who placed the bet |
bet.stake | Bet amount |
bet.odds | Bet odds |
bet.side | back or lay |
bet.fixtureId | Fixture identifier |
bet.sportId | Sport identifier |
bet.marketId | Market identifier |
bet.betTime | When the bet was placed |
bet.orderId | Order identifier |
bet.agentId | Agent who placed the bet (if applicable) |
context.userBetHistory30d | All fraud_events for this user (last 30 days) |
context.priceTicks | Exchange/bookmaker price ticks for the fixture |
context.eventMarkers | Timeline events: BALL, WICKET, GOAL, TOSS, CARD, etc. |
context.v3Bet | Full V3Bet Prisma record |
context.auditTrail | V3AuditTrail entries for this bet |
scores | DimensionScores object (only for deterministic/signature rules) |
Rule types
| Type | Prefix | When it runs | What it returns |
|---|---|---|---|
dimension | dim_ | Always, on every bet | Score 0–100 in details.score |
deterministic | DET_ | Always, checked conditionally | triggered: true/false with severity |
signature | SIG_ | After dimensions and deterministic rules | triggered: true/false with severity |
Tips
- One rule failure doesn't block others. All rules run inside
Promise.allSettled, so exceptions are caught silently. - Confidence (0–1) indicates how certain the rule is. Higher confidence = more weight in case priority.
- Details object is stored in the FraudBetScore metadata and displayed in the admin case detail view.
- If your rule needs TimescaleDB queries, accept
tsdbPoolin the constructor (seePreTossConcentrationRulefor an example). - If your rule needs Prisma models, accept
prismain the constructor (seeImpossibleTravelRulefor an example).