PHP项目结合大小球盘口判断的实现思路
在体育类PHP项目中,"大小球盘口"(Over/Under)判断通常用于足球/篮球赛事数据分析、预测系统、赔率监控等场景,下面从数据层到业务层给出一个完整的实现方案。

核心概念先理清
| 术语 | 含义 |
|---|---|
| 大小球盘口 | 庄家设定的总进球数分界线,如 2.5、3.0 |
| 大球 (Over) | 两队总进球数 > 盘口 |
| 小球 (Under) | 两队总进球数 < 盘口 |
| 水位/赔率 | 大球、小球各自对应的赔率(如 0.95 / 0.90) |
| 走水 | 盘口为整数且总进球等于盘口时,退本金 |
判断公式(简化):
- 总进球 > 盘口 → 大球赢
- 总进球 < 盘口 → 小球赢
- 总进球 == 盘口 且盘口为整数 → 走水
数据结构设计
盘口表 odds_ou
CREATE TABLE `odds_ou` ( `id` BIGINT PRIMARY KEY AUTO_INCREMENT, `match_id` INT NOT NULL COMMENT '赛事ID', `bookmaker` VARCHAR(50) COMMENT '博彩公司', `line` DECIMAL(4,2) COMMENT '盘口线 如2.5', `over_odds` DECIMAL(6,3) COMMENT '大球赔率', `under_odds` DECIMAL(6,3) COMMENT '小球赔率', `created_at` DATETIME, INDEX `idx_match` (`match_id`, `created_at`) );
赛事表 matches 需含比分
ALTER TABLE matches ADD COLUMN home_score INT, ADD COLUMN away_score INT;
核心业务逻辑(PHP)
结算函数
class OverUnderSettle
{
/**
* @param float $line 盘口 如 2.5
* @param int $homeScore 主队进球
* @param int $awayScore 客队进球
* @return array ['result' => 'over|under|push', 'total' => int]
*/
public static function settle(float $line, int $homeScore, int $awayScore): array
{
$total = $homeScore + $awayScore;
if ($total > $line) {
return ['result' => 'over', 'total' => $total];
}
if ($total < $line) {
return ['result' => 'under', 'total' => $total];
}
// 相等:整数盘走水
return ['result' => 'push', 'total' => $total];
}
}
判断某注单输赢(带本金计算)
class BetCalculator
{
/**
* @param string $side 'over' 或 'under'
* @param float $line 盘口
* @param float $odds 该方向赔率
* @param int $stake 本金
* @param int $homeScore
* @param int $awayScore
* @return array ['win' => bool, 'payout' => float, 'profit' => float]
*/
public static function calc(
string $side, float $line, float $odds,
int $stake, int $homeScore, int $awayScore
): array {
$r = OverUnderSettle::settle($line, $homeScore, $awayScore);
if ($r['result'] === 'push') {
return ['win' => null, 'payout' => $stake, 'profit' => 0];
}
$win = ($r['result'] === $side);
if ($win) {
// 亚盘/欧洲盘不同,这里按欧洲盘:盈利 = 本金 * 赔率
$payout = $stake * (1 + $odds);
return ['win' => true, 'payout' => $payout, 'profit' => $stake * $odds];
}
return ['win' => false, 'payout' => 0, 'profit' => -$stake];
}
}
结合盘口变化做"判断"(预测/预警)
实际项目中,判断不止于结算,而在于动态分析,常见三类玩法:
盘口变动趋势判断
class LineTrendAnalyzer
{
/**
* 分析盘口变化,识别"升盘/降盘/水位变化"
*/
public static function analyze(array $history): array
{
// $history 按时间倒序数组:[['line'=>2.5,'over'=>0.95,'under'=>0.90,'t'=>...], ...]
if (count($history) < 2) return ['trend' => 'stable'];
$latest = $history[0];
$prev = $history[1];
$lineDelta = $latest['line'] - $prev['line'];
$overDelta = $latest['over'] - $prev['over'];
$underDelta = $latest['under'] - $prev['under'];
$signals = [];
if ($lineDelta > 0) $signals[] = '升盘'; // 盘口抬高,偏大球
if ($lineDelta < 0) $signals[] = '降盘'; // 盘口压低,偏小球
// 水位明显下降 = 资金流入该方向
if ($overDelta < -0.05 && abs($lineDelta) < 0.01) $signals[] = '大球水位走低';
if ($underDelta < -0.05 && abs($lineDelta) < 0.01) $signals[] = '小球水位走低';
return [
'trend' => $lineDelta > 0 ? 'up' : ($lineDelta < 0 ? 'down' : 'stable'),
'signals' => $signals,
];
}
}
多公司对比找"异常盘"
class CrossBookAnalyzer
{
/**
* 找出与主流公司偏离过大的盘口(可能是陷阱或机会)
*/
public static function findOutliers(array $books, float $consensusLine): array
{
$outliers = [];
foreach ($books as $b) {
$diff = $b['line'] - $consensusLine;
if (abs($diff) >= 0.5) {
$outliers[] = [
'bookmaker' => $b['bookmaker'],
'line' => $b['line'],
'diff' => $diff,
];
}
}
return $outliers;
}
}
结合历史战绩做概率参考
class ProbabilityHelper
{
/**
* 用两队近期进球均值估算大球概率(泊松分布简化)
*/
public static function overProb(float $homeAvg, float $awayAvg, float $line): float
{
$lambda = $homeAvg + $awayAvg; // 期望总进球
$p = 0.0;
for ($k = 0; $k <= floor($line); $k++) {
$p += exp(-$lambda) * pow($lambda, $k) / self::factorial($k);
}
// P(总进球 <= floor(line)) 是小球概率
return 1 - $p; // 大球概率
}
private static function factorial(int $n): int
{
return $n <= 1 ? 1 : $n * self::factorial($n - 1);
}
}
注意:
line=2.5时floor(2.5)=2,P(X≤2) 即小球概率,1-P为大球概率,逻辑正确。
整体业务流程图
[抓取赔率] → [入库 odds_ou]
↓
[赛前/赛中定时任务]
↓
[LineTrendAnalyzer 分析变化] ─┐
[CrossBookAnalyzer 找异常] ─┼→ [生成信号 signal 表]
[ProbabilityHelper 算概率] ─┘
↓
[赛事结束后]
↓
[OverUnderSettle 结算] → [BetCalculator 计算盈亏] → [统计报表]
实战注意点
-
亚盘 vs 欧盘
- 欧盘赔率即含本金的返还系数(如 1.95),盈亏 = 本金 × 赔率(含本)。
- 亚盘赔率是净利(如 0.95),赢 = 本金 × 0.95。
- 代码里要区分,别混用。
-
走水处理
- 只有整数盘(2、3)才会走水,
.5盘口永远不会走水。 - 半一盘(2.25、2.75)要拆成两半结算。
- 只有整数盘(2、3)才会走水,
-
半球/四分之一球盘口
// 2.25 = 2 + 2.5 各投一半 // 2.75 = 2.5 + 3 各投一半
需要写拆盘函数,别直接当 2.5 处理。
-
时区与快照
- 盘口每几分钟变一次,务必记录
created_at,否则无法回溯趋势。
- 盘口每几分钟变一次,务必记录
-
数据源合规
- 只做数据分析、预警、展示,不要接入实际下注闭环,避免法律风险。
可扩展方向
- 用 Redis 缓存实时盘口,避免高频查库
- 用 WebSocket 推送盘口变动到前端
- 引入 ML 模型(XGBoost)用历史盘口 + 比分做二分类
- 定时任务用
supervisor+php think/artisan常驻
如果你告诉我具体是足球还是篮球、用的是 ThinkPHP / Laravel / 原生、以及是否需要亚盘拆盘逻辑,我可以给出更贴合你项目的完整代码。