Skip to main content

Overview

The Bet Model defines data structures for tracked bets and arbitrage opportunities in the PROPPR platform.

Tracked Bet Document

Schema

bet_id
string
required
Unique bet tracking ID (ObjectId string)
alert_id
string
Reference to original alert
tracked_at
datetime
required
Timestamp when bet was tracked
player_name
string
Player name (Player Bot only)
player_id
integer
Player ID (Player Bot only)
team
string
Team name
team_id
integer
Team ID
market
string
required
Betting market (e.g., “Player Shots On Target”)
match
string
required
Match name (e.g., “Liverpool vs Man City”)
date
string
Match date (YYYY-MM-DD)
time
string
Match time (HH:MM)
threshold
float
Market threshold (e.g., 1.5)
market_direction
string
“over” or “under”
bookmaker
string
Bookmaker name
odds
float
required
Decimal odds
actual_stake
float
required
Actual stake in currency
unit_size
float
required
User’s unit size
units_staked
float
required
Stake in units
status
string
default:"pending"
“pending”, “won”, “lost”, “refund”, “half_win”, “half_loss”
manual_override
boolean
default:"false"
If true, bet was manually graded (protected from auto-grading)
returns
float
default:"0.0"
Total return amount
profit_loss
float
default:"0.0"
Profit or loss amount
fixture_id
integer
Fixture/match ID
settled_at
datetime
Timestamp when bet was settled
actual_value
float
Actual statistic value from match
result_tracking
object
Result tracking metadata for auto-grading

Result Tracking Object

result_tracking.api_type_id
integer
API type ID for statistic
result_tracking.api_location
string
“home”, “away”, “total”, or “player”
result_tracking.stat_type
string
Stat type (e.g., “shots_on_target”, “goals”)
result_tracking.expected_threshold
float
Expected threshold value
result_tracking.market_direction
string
“over” or “under”
result_tracking.result_status
string
“pending”, “won”, “lost”, “refund”, “half_win”, “half_loss”
result_tracking.actual_value
float
Actual value from grading
result_tracking.graded_at
datetime
Grading timestamp

Example Document

{
  "bet_id": "65f3b2c4e5f6a7b8c9d0e1f2",
  "alert_id": "12345|67890|Player Shots On Target|last5|Bet365",
  "tracked_at": "2024-03-10T14:30:00Z",
  "player_name": "Mohamed Salah",
  "player_id": 118748,
  "team": "Liverpool",
  "team_id": 55,
  "market": "Player Shots On Target",
  "match": "Liverpool vs Manchester City",
  "date": "2024-03-10",
  "time": "16:30",
  "threshold": 1.5,
  "market_direction": "over",
  "bookmaker": "Bet365",
  "odds": 2.10,
  "actual_stake": 10.0,
  "unit_size": 10.0,
  "units_staked": 1.0,
  "status": "won",
  "manual_override": false,
  "returns": 21.0,
  "profit_loss": 11.0,
  "fixture_id": 19245678,
  "settled_at": "2024-03-10T18:45:00Z",
  "actual_value": 3.0,
  "result_tracking": {
    "api_type_id": 86,
    "api_location": "player",
    "stat_type": "shots_on_target",
    "expected_threshold": 1.5,
    "market_direction": "over",
    "result_status": "won",
    "actual_value": 3.0,
    "graded_at": "2024-03-10T18:45:00Z"
  }
}

Arbitrage Bet Document

Schema

id
string
required
Unique arbitrage ID
eventId
integer
required
Event/fixture ID
profitMargin
float
required
Profit margin percentage
totalStake
float
required
Total stake amount
market
object
required
Market details
legs
array
required
Array of bet legs (minimum 2)
event
object
required
Event/fixture details
created_at
datetime
Creation timestamp
expires_at
datetime
Expiration timestamp
sport
string
Sport name
league
string
League name

Market Object

market.name
string
required
Market name (e.g., “Totals”, “Moneyline”)
market.hdp
float
Handicap value (e.g., 2.5)
market.totals_stat
string
Stat type for totals (e.g., “goals”, “points”)

Leg Object

legs[].bookmaker
string
required
Bookmaker name
legs[].side
string
required
Bet side (“over”, “under”, “home”, “away”, “draw”)
legs[].odds
string
required
Decimal odds as string
legs[].is_lay
boolean
default:"false"
Whether this is a LAY bet (exchange)
legs[].market_name
string
Market name for this leg
legs[].hdp
float
Handicap for this leg
legs[].updatedAt
string
Last odds update timestamp

Event Object

event.home
string
required
Home team name
event.away
string
required
Away team name
event.date
string
required
Event date/time (ISO 8601)
event.sport
string
required
Sport name
event.league
string
required
League name

Example: Standard Arbitrage

{
  "id": "arb_12345",
  "eventId": 19245678,
  "profitMargin": 3.73,
  "totalStake": 100.0,
  "market": {
    "name": "Totals",
    "hdp": 2.5
  },
  "legs": [
    {
      "bookmaker": "Bet365",
      "side": "over",
      "odds": "2.10",
      "is_lay": false,
      "market_name": "Totals",
      "hdp": 2.5,
      "updatedAt": "2024-03-10T14:25:00Z"
    },
    {
      "bookmaker": "Pinnacle",
      "side": "under",
      "odds": "2.05",
      "is_lay": false,
      "market_name": "Totals",
      "hdp": 2.5,
      "updatedAt": "2024-03-10T14:26:00Z"
    }
  ],
  "event": {
    "home": "Liverpool",
    "away": "Manchester City",
    "date": "2024-03-10T16:30:00Z",
    "sport": "Football",
    "league": "England - Premier League"
  },
  "created_at": "2024-03-10T14:27:00Z",
  "expires_at": "2024-03-10T16:30:00Z"
}

Example: LAY Arbitrage

{
  "id": "lay_arb_67890",
  "eventId": 19245678,
  "profitMargin": 2.45,
  "totalStake": 100.0,
  "market": {
    "name": "Totals",
    "hdp": 2.5
  },
  "legs": [
    {
      "bookmaker": "Bet365",
      "side": "over",
      "odds": "2.10",
      "is_lay": false
    },
    {
      "bookmaker": "Betfair Exchange",
      "side": "over",
      "odds": "2.15",
      "is_lay": true,
      "lay_stake": 46.51,
      "liability": 53.49,
      "commission": 2.0
    }
  ],
  "event": {
    "home": "Liverpool",
    "away": "Manchester City",
    "date": "2024-03-10T16:30:00Z",
    "sport": "Football",
    "league": "England - Premier League"
  },
  "is_lay_arb": true,
  "exchange_commission": 2.0
}

User Bets Collection

Schema

user_id
integer
required
Telegram user ID
bets
array
required
Array of tracked bet documents
created_at
datetime
User document creation timestamp
last_updated
datetime
Last update timestamp
unit_size
float
User’s current unit size
username
string
Telegram username

Example Document

{
  "user_id": 123456789,
  "username": "john_doe",
  "unit_size": 10.0,
  "created_at": "2024-01-15T10:00:00Z",
  "last_updated": "2024-03-10T18:45:00Z",
  "bets": [
    {
      "bet_id": "65f3b2c4e5f6a7b8c9d0e1f2",
      "market": "Player Shots On Target",
      "player_name": "Mohamed Salah",
      "status": "won",
      "odds": 2.10,
      "units_staked": 1.0,
      "returns": 21.0,
      "profit_loss": 11.0
    }
  ]
}

Status Enums

Bet Status

  • pending - Awaiting result
  • won - Bet won
  • lost - Bet lost
  • refund - Bet refunded (push)
  • half_win - Half stake won (Asian handicap)
  • half_loss - Half stake lost (Asian handicap)

Market Direction

  • over - Over threshold
  • under - Under threshold

Bet Sides

  • over - Over (totals)
  • under - Under (totals)
  • home - Home team
  • away - Away team
  • draw - Draw
  • yes - Yes
  • no - No

Indexes

Tracked Bets Collection

// User lookup
db.user_tracked_bets.createIndex({ "user_id": 1 }, { unique: true })

// Bet ID lookup
db.user_tracked_bets.createIndex({ "bets.bet_id": 1 })

// Status filtering
db.user_tracked_bets.createIndex({ "bets.status": 1 })

Arbitrage Bets Collection

// Expiration cleanup
db.arbitrage_bets.createIndex({ "expires_at": 1 })

// Event lookup
db.arbitrage_bets.createIndex({ "eventId": 1 })

// Sport filtering
db.arbitrage_bets.createIndex({ "sport": 1 })

// League filtering
db.arbitrage_bets.createIndex({ "league": 1 })

References

Build docs developers (and LLMs) love