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 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) } // evaluateRugbyOutcome evaluates the outcome of a Rugby bet func evaluateRugbyOutcome(outcome domain.BetOutcome, result *domain.RugbyResultResponse) (domain.OutcomeStatus, error) { finalScore := parseSS(result.SS) switch outcome.MarketName { case "Money Line": return evaluateRugbyMoneyLine(outcome, finalScore) case "Spread": return evaluateRugbySpread(outcome, finalScore) case "Total Points": return evaluateRugbyTotalPoints(outcome, finalScore) default: return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported rugby market: %s", outcome.MarketName) } } // evaluateBaseballOutcome evaluates the outcome of a Baseball bet func evaluateBaseballOutcome(outcome domain.BetOutcome, result *domain.BaseballResultResponse) (domain.OutcomeStatus, error) { finalScore := parseSS(result.SS) switch outcome.MarketName { case "Money Line": return evaluateBaseballMoneyLine(outcome, finalScore) case "Spread": return evaluateBaseballSpread(outcome, finalScore) case "Total Runs": return evaluateBaseballTotalRuns(outcome, finalScore) default: return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("unsupported baseball market: %s", outcome.MarketName) } }