744 lines
26 KiB
Go
744 lines
26 KiB
Go
package result
|
|
|
|
import (
|
|
"fmt"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
|
)
|
|
|
|
// Football evaluations
|
|
func evaluateFullTimeResult(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
switch outcome.OddName {
|
|
case "1": // Home win
|
|
if score.Home > score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "Draw":
|
|
if score.Home == score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "2": // Away win
|
|
if score.Away > score.Home {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
}
|
|
}
|
|
|
|
func evaluateGoalsOverUnder(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
totalGoals := float64(score.Home + score.Away)
|
|
threshold, err := strconv.ParseFloat(outcome.OddName, 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
|
}
|
|
|
|
if outcome.OddHeader == "Over" {
|
|
if totalGoals > threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if totalGoals == threshold {
|
|
return domain.OUTCOME_STATUS_VOID, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
} else if outcome.OddHeader == "Under" {
|
|
if totalGoals < threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if totalGoals == threshold {
|
|
return domain.OUTCOME_STATUS_VOID, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
|
}
|
|
|
|
func evaluateCorrectScore(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
expectedScore := fmt.Sprintf("%d-%d", score.Home, score.Away)
|
|
if outcome.OddName == expectedScore {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
func evaluateHalfTimeResult(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
return evaluateFullTimeResult(outcome, score)
|
|
}
|
|
|
|
// This is a multiple outcome checker for the asian handicap and other kinds of bets
|
|
// The only outcome that are allowed are "Both Bets win", "Both Bets Lose", "Half Win and Half Void"
|
|
func checkMultiOutcome(outcome domain.OutcomeStatus, secondOutcome domain.OutcomeStatus) (domain.OutcomeStatus, error) {
|
|
switch outcome {
|
|
case domain.OUTCOME_STATUS_PENDING:
|
|
return secondOutcome, nil
|
|
case domain.OUTCOME_STATUS_WIN:
|
|
if secondOutcome == domain.OUTCOME_STATUS_WIN {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if secondOutcome == domain.OUTCOME_STATUS_VOID {
|
|
return domain.OUTCOME_STATUS_HALF, nil
|
|
} else {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid multi outcome")
|
|
}
|
|
case domain.OUTCOME_STATUS_LOSS:
|
|
if secondOutcome == domain.OUTCOME_STATUS_LOSS {
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
} else if secondOutcome == domain.OUTCOME_STATUS_VOID {
|
|
return domain.OUTCOME_STATUS_HALF, nil
|
|
} else {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid multi outcome")
|
|
}
|
|
case domain.OUTCOME_STATUS_VOID:
|
|
if secondOutcome == domain.OUTCOME_STATUS_WIN || secondOutcome == domain.OUTCOME_STATUS_LOSS {
|
|
return domain.OUTCOME_STATUS_HALF, nil
|
|
} else {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid multi outcome")
|
|
}
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid multi outcome")
|
|
}
|
|
}
|
|
|
|
func evaluateAsianHandicap(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
handicapList := strings.Split(outcome.OddHandicap, ",")
|
|
newOutcome := domain.OUTCOME_STATUS_PENDING
|
|
for _, handicapStr := range handicapList {
|
|
handicap, err := strconv.ParseFloat(handicapStr, 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid handicap: %s", outcome.OddHandicap)
|
|
}
|
|
adjustedHomeScore := float64(score.Home)
|
|
adjustedAwayScore := float64(score.Away)
|
|
if outcome.OddHeader == "1" { // Home team
|
|
adjustedHomeScore += handicap
|
|
} else if outcome.OddHeader == "2" { // Away team
|
|
adjustedAwayScore += handicap
|
|
} else {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
|
}
|
|
|
|
if adjustedHomeScore > adjustedAwayScore {
|
|
if outcome.OddHeader == "1" {
|
|
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_WIN)
|
|
if err != nil {
|
|
fmt.Printf("multi outcome check error")
|
|
return domain.OUTCOME_STATUS_PENDING, err
|
|
}
|
|
}
|
|
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS)
|
|
if err != nil {
|
|
fmt.Printf("multi outcome check error")
|
|
return domain.OUTCOME_STATUS_PENDING, err
|
|
}
|
|
} else if adjustedHomeScore < adjustedAwayScore {
|
|
if outcome.OddHeader == "2" {
|
|
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_WIN)
|
|
if err != nil {
|
|
fmt.Printf("multi outcome check error")
|
|
return domain.OUTCOME_STATUS_PENDING, err
|
|
}
|
|
}
|
|
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_LOSS)
|
|
if err != nil {
|
|
fmt.Printf("multi outcome check error")
|
|
return domain.OUTCOME_STATUS_PENDING, err
|
|
}
|
|
}
|
|
newOutcome, err = checkMultiOutcome(newOutcome, domain.OUTCOME_STATUS_VOID)
|
|
if err != nil {
|
|
fmt.Printf("multi outcome check error")
|
|
return domain.OUTCOME_STATUS_PENDING, err
|
|
}
|
|
}
|
|
return newOutcome, nil
|
|
}
|
|
|
|
func evaluateGoalLine(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
return evaluateGoalsOverUnder(outcome, score)
|
|
}
|
|
|
|
func evaluateFirstTeamToScore(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) {
|
|
for _, event := range events {
|
|
if strings.Contains(event["text"], "1st Goal") || strings.Contains(event["text"], "Goal 1") {
|
|
if strings.Contains(event["text"], outcome.HomeTeamName) && outcome.OddName == "1" {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if strings.Contains(event["text"], outcome.AwayTeamName) && outcome.OddName == "2" {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
}
|
|
return domain.OUTCOME_STATUS_VOID, nil // No goals scored
|
|
}
|
|
|
|
func evaluateGoalsOddEven(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
totalGoals := score.Home + score.Away
|
|
isOdd := totalGoals%2 == 1
|
|
if outcome.OddName == "Odd" && isOdd {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if outcome.OddName == "Even" && !isOdd {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
func evaluateDoubleChance(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
isHomeWin := score.Home > score.Away
|
|
isDraw := score.Home == score.Away
|
|
isAwayWin := score.Away > score.Home
|
|
|
|
switch outcome.OddName {
|
|
case "1 or Draw", (outcome.HomeTeamName + " or " + "Draw"):
|
|
if isHomeWin || isDraw {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "Draw or 2", ("Draw" + " or " + outcome.AwayTeamName):
|
|
if isDraw || isAwayWin {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "1 or 2", (outcome.HomeTeamName + " or " + outcome.AwayTeamName):
|
|
if isHomeWin || isAwayWin {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
}
|
|
}
|
|
|
|
func evaluateDrawNoBet(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
if score.Home == score.Away {
|
|
return domain.OUTCOME_STATUS_VOID, nil
|
|
}
|
|
if outcome.OddName == "1" && score.Home > score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if outcome.OddName == "2" && score.Away > score.Home {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
// basketball evaluations
|
|
|
|
func evaluateGameLines(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
switch outcome.OddName {
|
|
case "Money Line":
|
|
return evaluateMoneyLine(outcome, score)
|
|
|
|
case "Spread", "Line":
|
|
// Since Spread betting is essentially the same thing
|
|
return evaluateAsianHandicap(outcome, score)
|
|
case "Total":
|
|
return evaluateTotalOverUnder(outcome, score)
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
}
|
|
}
|
|
|
|
func evaluateMoneyLine(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
switch outcome.OddHeader {
|
|
case "1":
|
|
if score.Home > score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
|
|
case "2":
|
|
if score.Home < score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "Tie", "Draw":
|
|
if score.Home == score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
}
|
|
}
|
|
|
|
func evaluateTotalOverUnder(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
|
|
// The handicap will be in the format "U {float}" or "O {float}"
|
|
// U and O denoting over and under for this case
|
|
overUnderStr := strings.Split(outcome.OddHandicap, " ")
|
|
if len(overUnderStr) != 2 {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
|
}
|
|
threshold, err := strconv.ParseFloat(overUnderStr[1], 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
|
}
|
|
|
|
// Since the threshold will come in a xx.5 format, there is no VOID for this kind of bet
|
|
totalScore := float64(score.Home + score.Away)
|
|
|
|
if overUnderStr[0] == "O" {
|
|
if totalScore > threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
} else if overUnderStr[0] == "U" {
|
|
if totalScore < threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
} else if overUnderStr[0] == "E" {
|
|
if totalScore == threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
|
}
|
|
|
|
func evaluateTotalLegs(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
total, err := strconv.ParseFloat(outcome.OddName, 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid : %s", outcome.OddName)
|
|
}
|
|
|
|
totalLegs := float64(score.Home + score.Away)
|
|
|
|
switch outcome.OddHeader {
|
|
case "Over":
|
|
if totalLegs > total {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "Under":
|
|
if totalLegs < total {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
|
}
|
|
|
|
func evaluateResultAndTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
|
|
// The handicap will be in the format "U {float}" or "O {float}"
|
|
// U and O denoting over and under for this case
|
|
overUnderStr := strings.Split(outcome.OddHandicap, " ")
|
|
if len(overUnderStr) != 2 {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
|
}
|
|
|
|
overUnder := overUnderStr[0]
|
|
|
|
if overUnder != "Over" && overUnder != "Under" {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("failed parsing over under: %s", outcome.OddHeader)
|
|
}
|
|
threshold, err := strconv.ParseFloat(overUnderStr[1], 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
|
}
|
|
|
|
// Since the threshold will come in a xx.5 format, there is no VOID for this kind of bet
|
|
totalScore := float64(score.Home + score.Away)
|
|
|
|
switch outcome.OddHeader {
|
|
case "1":
|
|
if overUnder == "Over" && totalScore > threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if overUnder == "Under" && totalScore < threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "2":
|
|
if overUnder == "Over" && totalScore > threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if overUnder == "Under" && totalScore < threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("failed to parse over and under: %s", outcome.OddName)
|
|
}
|
|
}
|
|
|
|
func evaluateTeamTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
|
|
// The handicap will be in the format "U {float}" or "O {float}"
|
|
// U and O denoting over and under for this case
|
|
overUnderStr := strings.Split(outcome.OddHandicap, " ")
|
|
if len(overUnderStr) != 2 {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddHandicap)
|
|
}
|
|
|
|
overUnder := overUnderStr[0]
|
|
|
|
if overUnder != "Over" && overUnder != "Under" {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("failed parsing over under: %s", outcome.OddHeader)
|
|
}
|
|
threshold, err := strconv.ParseFloat(overUnderStr[1], 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddHandicap)
|
|
}
|
|
|
|
// Since the threshold will come in a xx.5 format, there is no VOID for this kind of bet
|
|
HomeScore := float64(score.Home)
|
|
AwayScore := float64(score.Away)
|
|
|
|
switch outcome.OddHeader {
|
|
case "1":
|
|
if overUnder == "Over" && HomeScore > threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if overUnder == "Under" && HomeScore < threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "2":
|
|
if overUnder == "Over" && AwayScore > threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if overUnder == "Under" && AwayScore < threshold {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("failed to parse over and under: %s", outcome.OddName)
|
|
}
|
|
}
|
|
|
|
// Evaluate Result and Both Teams To Score X Points
|
|
func evaluateResultAndBTTSX(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
|
|
// The name parameter will hold value "name": "{team_name} and {Yes | No}"
|
|
// The best way to do this is to evaluate backwards since there might be
|
|
// teams with 'and' in their name
|
|
// We know that there is going to be "Yes" and "No "
|
|
oddNameSplit := strings.Split(outcome.OddName, " ")
|
|
|
|
scoreCheckSplit := oddNameSplit[len(oddNameSplit)-1]
|
|
var isScorePoints bool
|
|
if scoreCheckSplit == "Yes" {
|
|
isScorePoints = true
|
|
} else if scoreCheckSplit == "No" {
|
|
isScorePoints = false
|
|
} else {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
}
|
|
|
|
teamName := strings.TrimSpace(strings.Join(oddNameSplit[:len(oddNameSplit)-2], ""))
|
|
|
|
threshold, err := strconv.ParseInt(outcome.OddHeader, 10, 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddHeader)
|
|
}
|
|
|
|
switch teamName {
|
|
case outcome.HomeTeamName:
|
|
if score.Home > score.Away {
|
|
if isScorePoints && score.Home >= int(threshold) && score.Away >= int(threshold) {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if !isScorePoints && score.Home < int(threshold) && score.Away < int(threshold) {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
}
|
|
case outcome.AwayTeamName:
|
|
if score.Away > score.Home {
|
|
if isScorePoints && score.Home >= int(threshold) && score.Away >= int(threshold) {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if !isScorePoints && score.Home < int(threshold) && score.Away < int(threshold) {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
}
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("team name error: %s", teamName)
|
|
}
|
|
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
|
|
}
|
|
|
|
// Both Teams To Score X Points
|
|
func evaluateBTTSX(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
threshold, err := strconv.ParseInt(outcome.OddName, 10, 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
|
}
|
|
|
|
switch outcome.OddHeader {
|
|
case "Yes":
|
|
if score.Home >= int(threshold) && score.Away >= int(threshold) {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "No":
|
|
if score.Home < int(threshold) && score.Away < int(threshold) {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
|
}
|
|
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
func evaluateMoneyLine3Way(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
switch outcome.OddName {
|
|
case "1": // Home win
|
|
if score.Home > score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "Tie":
|
|
if score.Home == score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "2": // Away win
|
|
if score.Away > score.Home {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
}
|
|
}
|
|
|
|
func evaluateDoubleResult(outcome domain.BetOutcome, firstHalfScore struct{ Home, Away int }, secondHalfScore struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
halfWins := strings.Split(outcome.OddName, "-")
|
|
if len(halfWins) != 2 {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
}
|
|
firstHalfWinner := strings.TrimSpace(halfWins[0])
|
|
secondHalfWinner := strings.TrimSpace(halfWins[1])
|
|
|
|
if firstHalfWinner != outcome.HomeTeamName && firstHalfWinner != outcome.AwayTeamName && firstHalfWinner != "Tie" {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", firstHalfWinner)
|
|
}
|
|
if secondHalfWinner != outcome.HomeTeamName && secondHalfWinner != outcome.AwayTeamName && secondHalfWinner != "Tie" {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", firstHalfWinner)
|
|
}
|
|
|
|
switch {
|
|
case firstHalfWinner == outcome.HomeTeamName && firstHalfScore.Home < firstHalfScore.Away:
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case firstHalfWinner == outcome.AwayTeamName && firstHalfScore.Away < firstHalfScore.Home:
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case firstHalfWinner == "Tie" && firstHalfScore.Home != firstHalfScore.Away:
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
switch {
|
|
case secondHalfWinner == outcome.HomeTeamName && firstHalfScore.Home < firstHalfScore.Away:
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case secondHalfWinner == outcome.AwayTeamName && firstHalfScore.Away < firstHalfScore.Home:
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case secondHalfWinner == "Tie" && firstHalfScore.Home != firstHalfScore.Away:
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
|
|
func evaluateHighestScoringHalf(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
firstHalfTotal := firstScore.Home + firstScore.Away
|
|
secondHalfTotal := secondScore.Home + secondScore.Away
|
|
switch outcome.OddName {
|
|
case "1st Half":
|
|
if firstHalfTotal > secondHalfTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "2nd Half":
|
|
if firstHalfTotal < secondHalfTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "Tie":
|
|
if firstHalfTotal == secondHalfTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
func evaluateHighestScoringQuarter(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }, thirdScore struct{ Home, Away int }, fourthScore struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
firstQuarterTotal := firstScore.Home + firstScore.Away
|
|
secondQuarterTotal := secondScore.Home + secondScore.Away
|
|
thirdQuarterTotal := thirdScore.Home + thirdScore.Away
|
|
fourthQuarterTotal := fourthScore.Home + fourthScore.Away
|
|
|
|
switch outcome.OddName {
|
|
case "1st Quarter":
|
|
if firstQuarterTotal > secondQuarterTotal && firstQuarterTotal > thirdQuarterTotal && firstQuarterTotal > fourthQuarterTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "2nd Quarter":
|
|
if secondQuarterTotal > firstQuarterTotal && secondQuarterTotal > thirdQuarterTotal && secondQuarterTotal > fourthQuarterTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "3rd Quarter":
|
|
if thirdQuarterTotal > firstQuarterTotal && thirdQuarterTotal > secondQuarterTotal && thirdQuarterTotal > fourthQuarterTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "4th Quarter":
|
|
if fourthQuarterTotal > firstQuarterTotal && fourthQuarterTotal > secondQuarterTotal && fourthQuarterTotal > thirdQuarterTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "Tie":
|
|
if firstQuarterTotal == secondQuarterTotal || secondQuarterTotal == thirdQuarterTotal || thirdQuarterTotal == fourthQuarterTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
func evaluateHandicapAndTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
|
|
nameSplit := strings.Split(outcome.OddName, " ")
|
|
// Evaluate from bottom to get the threshold and find out if its over or under
|
|
threshold, err := strconv.ParseFloat(nameSplit[len(nameSplit)-1], 10)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("failed parsing threshold: %s", outcome.OddName)
|
|
}
|
|
total := float64(score.Home + score.Away)
|
|
overUnder := nameSplit[len(nameSplit)-2]
|
|
if overUnder == "Over" {
|
|
if total < threshold {
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
} else if overUnder == "Under" {
|
|
if total > threshold {
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
} else {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("failed parsing over and under: %s", outcome.OddName)
|
|
}
|
|
|
|
handicap, err := strconv.ParseFloat(nameSplit[len(nameSplit)-4], 10)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("failed parsing handicap: %s", outcome.OddName)
|
|
}
|
|
|
|
teamName := strings.TrimSpace(strings.Join(nameSplit[:len(nameSplit)-4], ""))
|
|
|
|
adjustedHomeScore := float64(score.Home)
|
|
adjustedAwayScore := float64(score.Away)
|
|
|
|
switch teamName {
|
|
case outcome.HomeTeamName:
|
|
adjustedHomeScore += handicap
|
|
if adjustedHomeScore > adjustedAwayScore {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case outcome.AwayTeamName:
|
|
adjustedAwayScore += handicap
|
|
if adjustedAwayScore > adjustedHomeScore {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("failed parsing team name: %s", outcome.OddName)
|
|
}
|
|
|
|
}
|
|
|
|
func evaluateWinningMargin(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
|
|
marginSplit := strings.Split(outcome.OddName, "")
|
|
if len(marginSplit) < 1 {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
}
|
|
|
|
margin, err := strconv.ParseInt(marginSplit[0], 10, 64)
|
|
if err != nil {
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
}
|
|
|
|
isGtr := false
|
|
if len(marginSplit) == 2 {
|
|
isGtr = marginSplit[1] == "+"
|
|
}
|
|
switch outcome.OddHeader {
|
|
case "1":
|
|
if score.Home == (score.Away + int(margin)) {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if isGtr && score.Home > (score.Away+int(margin)) {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "2":
|
|
if (score.Home + int(margin)) == score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
} else if isGtr && (score.Home+int(margin)) > score.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddheader: %s", outcome.OddHeader)
|
|
}
|
|
|
|
func evaluateHighestScoringPeriod(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }, thirdScore struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
firstPeriodTotal := firstScore.Home + firstScore.Away
|
|
secondPeriodTotal := secondScore.Home + secondScore.Away
|
|
thirdPeriodTotal := thirdScore.Home + thirdScore.Away
|
|
|
|
switch outcome.OddName {
|
|
case "Period 1":
|
|
if firstPeriodTotal > secondPeriodTotal && firstPeriodTotal > thirdPeriodTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "Period 2":
|
|
if secondPeriodTotal > firstPeriodTotal && secondPeriodTotal > thirdPeriodTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "Period 3":
|
|
if thirdPeriodTotal > firstPeriodTotal && thirdPeriodTotal > secondPeriodTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
case "Tie":
|
|
if firstPeriodTotal == secondPeriodTotal || secondPeriodTotal == thirdPeriodTotal {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
func evaluateTiedAfterRegulation(outcome domain.BetOutcome, scores []struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
totalScore := struct{ Home, Away int }{0, 0}
|
|
for _, score := range scores {
|
|
totalScore.Home += score.Home
|
|
totalScore.Away += score.Away
|
|
}
|
|
switch outcome.OddName {
|
|
case "Yes":
|
|
if totalScore.Home == totalScore.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
case "No":
|
|
if totalScore.Home != totalScore.Away {
|
|
return domain.OUTCOME_STATUS_WIN, nil
|
|
}
|
|
return domain.OUTCOME_STATUS_LOSS, nil
|
|
}
|
|
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
}
|
|
|
|
func evaluateVolleyballGamelines(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
switch outcome.OddName {
|
|
case "Total":
|
|
return evaluateTotalOverUnder(outcome, score)
|
|
default:
|
|
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
}
|
|
}
|