PRTL - Internal Trader Portal
URL: prtl.altsportsleagues.ai
Purpose: Internal trading tools for ASD staff to manage odds, markets, and live trading operations.
Overview
PRTL (Portal) is the internal-facing trading platform where AltSportsData traders create, manage, and approve betting markets before they're published to external sportsbook operators.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β PRTL Architecture β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β Sidebar β β Event Tree β β Main β β
β β Navigation β β Sidebar β β Content β β
β βββββββββββββββ βββββββββββββββ βββββββββββββββ β
β β
β Trader Context Bar: Name | Role | Email | Phone β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββUser Roles
| Role | Access Level | Capabilities |
|---|---|---|
| Trader | Full | Create/edit odds, manage markets, suspend/resume |
| Risk Manager | Review | Approve high-exposure markets, set limits |
| Data Scientist | Read + Analytics | Monitor ML models, analyze performance |
| Admin | Full + Settings | User management, system configuration |
Key Features
1. Trading Layout
The main trading interface uses a three-column layout:
- Left Sidebar: Primary navigation (sports, leagues, settings)
- Event Tree Sidebar: Hierarchical event/fight navigation with expand/collapse
- Main Content: Market details, odds tables, and controls
// TradingLayout component structure
<TradingLayout title="UFC 305" subtitle="November 25, 2024" showEventTree>
<EventDetailPage />
</TradingLayout>2. Event Tree Navigation
The EventTreeSidebar provides hierarchical navigation:
βΌ MMA
βΌ UFC
β UFC 305 (Live)
β UFC 306 (Upcoming)
βΌ BKB Extreme Fighting
β BKB 48 Brawl in the Pines V
βΌ Motorsports
βΌ Formula 1
β Monaco Grand Prix (Live)Features:
- Collapsible sport/league sections
- Live event indicators (pulsing dot)
- Active event highlighting
- One-click navigation to event details
3. Trader Context Bar
The header displays trader information for accountability:
interface TraderInfo {
name: string; // "John Smith"
role: string; // "Senior Trader"
email: string; // "john.smith@altsportsdata.com"
phone?: string; // "+1 (555) 123-4567"
avatar?: string;
}4. Fight Cards with Edit Controls
Fight cards display matchup information with inline editing:
<FightCard
fight={fight}
isSelected={selectedFightId === fight.id}
onClick={() => setSelectedFightId(fight.id)}
onEdit={() => openEditModal(fight)}
/>Elements:
- Blue/Red corner indicators
- Fighter names, nicknames, records
- Weight class badge
- Status indicator (scheduled/live/completed)
- Edit button for traders
5. Odds Table
The OddsTable component provides a full editing interface:
| Blue Corner | Blue Odds | Bias | Red Odds | Red Corner | Market Status |
|---|---|---|---|---|---|
| Max Holloway | -150 β | [-] [0] [+] | +130 β | Alexander Volkanovski | Open |
Features:
- American odds display (positive/negative)
- Movement indicators (β green / β red)
- Bias adjustment steppers (-5 to +5)
- Market status toggles (Open/Suspended/Closed)
- Real-time update highlighting
6. Market Activation Panel
Tree-based market activation with toggle controls:
βΌ Main Card Markets
β Fight 1: Holloway vs Volkanovski (Moneyline)
β Fight 1: Method of Victory
β Fight 1: Round Totals (Draft)
βΌ Preliminary Card Markets
β Fight 5: Jones vs Martinez7. Odds Footer (Hold/Margin Display)
Shows calculated hold percentage and implied probabilities:
Hold: 4.76% | Blue: 48.5% | Red: 43.5% | Margin: +8.0%Component Reference
Layout Components
| Component | Location | Purpose |
|---|---|---|
TradingLayout | components/layout/trading-layout.tsx | Main page wrapper with sidebar |
Header | components/layout/header.tsx | Top bar with trader context |
Sidebar | components/layout/sidebar.tsx | Primary navigation |
EventTreeSidebar | components/layout/event-tree-sidebar.tsx | Event hierarchy nav |
UI Components
| Component | Location | Purpose |
|---|---|---|
FightCard | components/ui/fight-card.tsx | Matchup display with edit |
OddsTable | components/ui/odds-table.tsx | Full odds editing table |
OddsFooter | components/ui/odds-footer.tsx | Hold/margin display |
NumberStepper | components/ui/number-stepper.tsx | Bias adjustment control |
CornerIndicator | components/ui/corner-indicator.tsx | Blue/Red dot indicator |
StatusBadge | components/ui/status-badge.tsx | Market status pills |
MarketActivationPanel | components/ui/market-activation-panel.tsx | Market toggle tree |
Types
// src/types/event.ts
type Sport = 'mma' | 'jai_alai' | 'f1' | 'esports' | 'drone_racing' | 'arm_wrestling' | 'combat';
type EventStatus = 'live' | 'upcoming' | 'scheduled' | 'completed' | 'cancelled';
type FightStatus = 'scheduled' | 'live' | 'completed' | 'cancelled' | 'postponed';
type MarketStatus = 'draft' | 'pending_review' | 'approved' | 'published' | 'suspended' | 'closed' | 'settled';
type WeightClass = 'flyweight' | 'bantamweight' | 'featherweight' | 'lightweight' | 'welterweight' | 'middleweight' | 'light_heavyweight' | 'heavyweight';
interface Fighter {
id: string;
name: string;
nickname?: string;
record?: string;
country?: string;
}
interface Fight {
id: string;
event_id: string;
fight_number: number;
weight_class: WeightClass;
scheduled_rounds: number;
status: FightStatus;
blue_corner?: Fighter;
red_corner?: Fighter;
result?: FightResult;
}
interface OddsMarket {
id: string;
type: string;
name: string;
status: 'open' | 'suspended' | 'closed';
selections: OddsSelection[];
last_updated: string;
}
interface OddsSelection {
id: string;
name: string;
odds: number;
implied_probability: number;
movement?: 'up' | 'down' | 'none';
}Sport-Specific Components
PRTL implements specialized event cards for each sport category, automatically selecting the appropriate UI based on sport type.
Sport Categories
| Category | Sports | Key UI Elements |
|---|---|---|
| Combat | MMA, Boxing, Slap Fighting, Arm Wrestling | Blue/Red corners, weight class, rounds, method of victory |
| Racquet | Jai Alai, Tennis, Badminton | Sets, games, player pairs, lean controls |
| Motorsport | F1, NASCAR, MotoGP, NHRA | Sessions (practice/qualifying/race), positions, lap times |
| Extreme | Bull Riding, Surfing, Skateboarding | Heats, scores, advancing competitors |
| Esports | League of Legends, CS:GO, Valorant | Best-of series, game maps, pick/ban phases |
Universal Event Renderer
The SportEventRenderer automatically selects the appropriate component:
import { SportEventRenderer, type UniversalEvent } from '@/components/sports';
// Automatically renders correct component based on sport type
<SportEventRenderer
event={{ sport: 'mma', data: fightData }}
isSelected={isSelected}
onSelect={handleSelect}
showOdds={true}
variant="full" // or "compact" for sidebar lists
/>Combat Event Card
For combat sports (MMA, Boxing, Slap Fighting):
import { CombatEventCard } from '@/components/sports';
<CombatEventCard
fight={fight}
isSelected={isSelected}
onSelect={handleSelect}
showOdds={true}
blueOdds={-150}
redOdds={+130}
onEdit={() => openEditModal(fight)}
/>Features:
- Blue/red corner indicators
- Fighter name, nickname, record display
- Weight class badge
- Rounds and scheduled time
- Result badge (KO/TKO/SUB/DEC)
- Edit button for traders
Racquet Match Card (Jai Alai)
For set-based sports with player pairs:
import { RacquetMatchCard } from '@/components/sports';
<RacquetMatchCard
match={match}
isSelected={isSelected}
onSelect={handleSelect}
showLean={true}
onLeanChange={(playerId, lean) => updateLean(playerId, lean)}
/>Features:
- Set-by-set scoring table
- Player/team names
- Score indicators with winner highlighting
- Lean adjustment controls (-10 to +10)
- Match navigation tabs
Motorsport Event Card
For racing events with sessions:
import { MotorsportEventCard, MotorsportResultsTable } from '@/components/sports';
<MotorsportEventCard
event={raceEvent}
isSelected={isSelected}
onSelect={handleSelect}
/>
// Results table for completed sessions
<MotorsportResultsTable
results={qualifyingResults}
showTeam={true}
showTime={true}
showPoints={true}
/>Features:
- Circuit and country display
- Session list (Practice, Qualifying, Race)
- Live session indicators
- Results table with positions
- Driver matchup comparisons
- Gold/Silver/Bronze podium styling
Extreme Event Card
For action sports with heats and scores:
import { ExtremeEventCard, HeatCard } from '@/components/sports';
<ExtremeEventCard
event={extremeEvent}
isSelected={isSelected}
onSelect={handleSelect}
showLean={true}
/>
// For surfing/action sports heats
<HeatCard
heat={heat}
isSelected={isSelected}
onSelect={handleSelect}
/>Features:
- Competitor list with scores
- Round/heat indicators
- Score components breakdown
- Advancing competitor highlighting
- Bull riding: ride time, buck-off indicator
Esports Match Card
For competitive gaming matches:
import { EsportsMatchCard, GameTabs } from '@/components/sports';
<EsportsMatchCard
match={esportsMatch}
isSelected={isSelected}
onSelect={handleSelect}
team1Odds={-120}
team2Odds={+100}
/>
// Game-by-game navigation
<GameTabs
games={match.games}
activeGameNumber={currentGame}
onSelectGame={setCurrentGame}
bestOf={5}
/>Features:
- Team logos/initials with colors
- Best-of series progress dots
- Game-by-game results
- Live game indicator
- Map veto display
- Duration tracking
Sport Filter Tabs
For filtering events by sport:
import { SportFilterTabs } from '@/components/sports';
<SportFilterTabs
sports={['mma', 'jai_alai', 'f1', 'bull_riding']}
activeSport={currentSport}
onSelectSport={setCurrentSport}
showCounts={true}
eventCounts={{ mma: 12, jai_alai: 8, f1: 3, bull_riding: 5 }}
/>Sport Event List
For grouped event display:
import { SportEventList } from '@/components/sports';
<SportEventList
events={allEvents}
selectedEventId={selectedId}
onSelectEvent={handleSelectEvent}
groupBySport={true}
variant="full" // or "compact"
/>Design Patterns
Blue/Red Corner Convention
Combat sports consistently use blue/red corners:
// Corner colors (Tailwind)
const cornerColors = {
blue: 'bg-blue-500',
red: 'bg-red-500'
};
// Corner indicator usage
<span className="w-3 h-3 rounded-full bg-blue-500" /> // Blue corner
<span className="w-3 h-3 rounded-full bg-red-500" /> // Red cornerOdds Format Display
American odds formatting:
function formatOdds(odds: number): string {
if (odds >= 0) return `+${odds}`;
return odds.toString();
}
// Display: +150 (underdog) or -200 (favorite)Status Badge Colors
| Status | Background | Text |
|---|---|---|
| Live | bg-green-500/10 | text-green-600 |
| Upcoming | bg-blue-500/10 | text-blue-400 |
| Suspended | bg-yellow-500/10 | text-yellow-600 |
| Closed | bg-gray-500/10 | text-gray-400 |
API Integration
PRTL connects to the backend API for all operations:
// Market lifecycle operations
POST /v1/prtl/markets // Create market
PUT /v1/prtl/markets/{id}/odds/{lineId} // Update odds
POST /v1/prtl/markets/{id}/suspend // Suspend market
POST /v1/prtl/markets/{id}/publish // Publish to clients
POST /v1/prtl/markets/{id}/settle // Settle with resultSecurity
- Internal SSO authentication required
- Role-based access control
- 2FA enforcement for traders
- IP whitelist/VPN restriction
- Full audit logging of all actions
- Session timeout after 30 minutes
Related Documentation
- Portal (Client View) - Read-only client interface
- API Reference - PRTL API endpoints
- Requirements Spec - Full requirements document