Skip to content

[Phase 1] Line Movement Tracking #5

@WFord26

Description

@WFord26

name: "Phase 1: Line Movement Tracking"
about: Detect and classify line movements (steam moves, reverse line movement, etc.)
title: "[Phase 1] Line Movement Tracking"
labels: enhancement, phase-1, analytics
assignees: ''

Overview

Track how betting lines move over time and classify movements to identify "steam moves" (sharp money) and "reverse line movement" (line moves against public betting).

Business Value

  • Sharp Money Detection: Identify when professional bettors are betting
  • Value Timing: Know when to place bets for best value
  • Market Sentiment: Understand what the market is saying
  • Historical Analysis: Track your success betting with/against line moves

Technical Requirements

Database Changes

// Enhanced OddsSnapshot
model OddsSnapshot {
  // ... existing fields
  capturedAt          DateTime
  
  // NEW: Movement detection
  movementType        String?  @map("movement_type") @db.VarChar(20)
  movementSize        Decimal? @map("movement_size") @db.Decimal(5,2)
  volumeIndicator     Int?     @map("volume_indicator")  // 1-10 scale
  
  @@index([capturedAt])
  @@index([movementType])
}

// New table: Line Movement Events
model LineMovement {
  id                  String   @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  gameId              String   @map("game_id") @db.Uuid
  marketType          String   @map("market_type") @db.VarChar(20)
  detectedAt          DateTime @default(now()) @map("detected_at") @db.Timestamptz(6)
  
  // Movement characteristics
  movementType        String   @map("movement_type") @db.VarChar(20)
  linesBefore         Json     @map("lines_before")
  linesAfter          Json     @map("lines_after")
  
  // Analysis
  bookmakerCount      Int      @map("bookmaker_count")
  averageMovement     Decimal  @map("average_movement") @db.Decimal(5,2)
  timeToMove          Int      @map("time_to_move")  // seconds
  suspectedCause      String?  @map("suspected_cause") @db.VarChar(100)
  
  game Game @relation(fields: [gameId], references: [id])
  
  @@index([gameId])
  @@index([movementType])
  @@index([detectedAt])
  @@map("line_movements")
}

Backend Services

File: src/services/line-movement.service.ts

class LineMovementService {
  async detectMovement(gameId: string): Promise<Movement[]>
  async classifyMovement(movement: Movement): Promise<MovementType>
  async trackSteamMove(gameId: string): Promise<boolean>
  async findReverseLineMovement(gameId: string): Promise<boolean>
  async getRecentMoves(filters): Promise<LineMovement[]>
}

Real-time Detection: Compare odds snapshots every 5 minutes

  • Query last 2 snapshots for each game
  • Calculate movement for each bookmaker
  • Detect patterns across bookmakers
  • Classify movement type

Movement Classification

Steam Move (Sharp Money):

  • 3+ major books move simultaneously (within 2 minutes)
  • Movement of 1.5+ points on spreads/totals
  • Movement of 15+ cents on moneylines
  • Same direction across all books
  • Indicator: Sharp/professional money

Reverse Line Movement:

  • Line moves opposite to public betting percentages
  • Example: Public betting 70% on favorite, line moves toward underdog
  • Indicator: Sharp money on less popular side

Gradual Drift:

  • Line moves slowly over hours/days
  • Small incremental changes
  • Indicator: Balanced public action

Injury/News Move:

  • Sudden, large movement
  • Triggered by news event
  • Books may suspend betting temporarily
  • Indicator: Information-based adjustment

Detection Algorithm

function classifyMovement(before: Odds[], after: Odds[]): MovementType {
  // Calculate changes
  const changes = after.map((odds, i) => odds - before[i]);
  const avgChange = mean(changes);
  const timeElapsed = after.timestamp - before.timestamp;
  const bookCount = changes.filter(c => abs(c) > threshold).length;
  
  // Steam move criteria
  if (bookCount >= 3 && 
      timeElapsed < 120 && 
      abs(avgChange) >= 1.5) {
    return 'steam';
  }
  
  // Reverse line movement (requires public betting data)
  if (publicBettingPct > 65 && avgChange < 0) {
    return 'reverse';
  }
  
  // Gradual drift
  if (timeElapsed > 3600 && abs(avgChange) < 1.0) {
    return 'gradual';
  }
  
  return 'normal';
}

API Endpoints

  • GET /api/analytics/movements/live - Steam moves in last 2 hours
  • GET /api/analytics/movements/game/:gameId - All movements for game
  • GET /api/analytics/movements/history - Historical movement analysis
  • GET /api/analytics/movements/bookmaker/:bookmaker - Which book moves first
  • GET /api/analytics/movements/performance - Win rate when betting with/against moves

Frontend Components

Dashboard Alert: SteamMoveAlert.tsx

  • Real-time banner for steam moves
  • Shows game, moved line, direction
  • "Bet Now" CTA
  • Dismiss to remove from view

Movement Timeline: LineMovementChart.tsx

  • Line chart showing line over time
  • Annotate with movement events
  • Color-code by movement type
  • Show bookmaker-specific lines

Historical Performance: MovementPerformance.tsx

  • Win rate when betting with steam moves
  • Win rate when betting against public
  • ROI by movement type
  • Sample size and confidence intervals

Notifications

Push Alerts (when user opts in):

  • Steam move detected on followed games/teams
  • Reverse line movement on followed games
  • Significant line swing (>3 points)

Alert Preferences:

  • Minimum movement size
  • Sports to monitor
  • Time before game (e.g., only alert if >2 hours to game)

Acceptance Criteria

  • Database migration completed
  • Line movement detection running every 5 minutes
  • Movement classification algorithm tested
  • Dashboard alerts for steam moves
  • Historical movement data visualization
  • API endpoints documented
  • Unit tests for classification logic
  • Performance metrics tracked (win rate with moves)

Dependencies

  • Odds sync must run frequently (every 5-10 minutes)
  • Multiple bookmakers required for reliable detection
  • Historical odds data for baseline comparison

Estimated Effort

  • Backend: 5 days
  • Frontend: 3 days
  • Testing & Tuning: 2 days
  • Total: 10 days

Success Metrics

  • Detect 5-10 steam moves per day
  • 95% classification accuracy (manual validation)
  • Alerts delivered within 60 seconds of detection
  • Users report steam moves are valuable (survey)
  • Positive win rate correlation with steam moves

Future Enhancements

  • Public betting percentage integration (from external source)
  • Machine learning for movement prediction
  • Bookmaker "sharpness" ranking (who moves first)
  • Player prop line movement tracking

Metadata

Metadata

Assignees

No one assigned

    Labels

    analyticsAdvanced analytics featuresenhancementNew feature or requestphase-1Phase 1: Core Analytics

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions