unit test bet outcome
This commit is contained in:
parent
c637ddb321
commit
95eaed18ad
|
|
@ -4177,7 +4177,7 @@ const docTemplate = `{
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Virtual Games"
|
"Veli Games"
|
||||||
],
|
],
|
||||||
"summary": "Veli Games webhook handler",
|
"summary": "Veli Games webhook handler",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|
|
||||||
|
|
@ -4169,7 +4169,7 @@
|
||||||
"application/json"
|
"application/json"
|
||||||
],
|
],
|
||||||
"tags": [
|
"tags": [
|
||||||
"Virtual Games"
|
"Veli Games"
|
||||||
],
|
],
|
||||||
"summary": "Veli Games webhook handler",
|
"summary": "Veli Games webhook handler",
|
||||||
"parameters": [
|
"parameters": [
|
||||||
|
|
|
||||||
|
|
@ -4258,7 +4258,7 @@ paths:
|
||||||
type: object
|
type: object
|
||||||
summary: Veli Games webhook handler
|
summary: Veli Games webhook handler
|
||||||
tags:
|
tags:
|
||||||
- Virtual Games
|
- Veli Games
|
||||||
securityDefinitions:
|
securityDefinitions:
|
||||||
Bearer:
|
Bearer:
|
||||||
in: header
|
in: header
|
||||||
|
|
|
||||||
|
|
@ -498,491 +498,491 @@ func evaluateTotalOverUnder(outcome domain.BetOutcome, score struct{ Home, Away
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Result and Total betting is a type of bet where the bettor predicts
|
// Result and Total betting is a type of bet where the bettor predicts
|
||||||
// the outcome of a match and whether the total number of points scored will be over or under a specified number.
|
// the outcome of a match and whether the total number of points scored will be over or under a specified number.
|
||||||
func evaluateResultAndTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
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}"
|
// The handicap will be in the format "U {float}" or "O {float}"
|
||||||
// U and O denoting over and under for this case
|
// U and O denoting over and under for this case
|
||||||
overUnderStr := strings.Split(outcome.OddHandicap, " ")
|
overUnderStr := strings.Split(outcome.OddHandicap, " ")
|
||||||
if len(overUnderStr) != 2 {
|
if len(overUnderStr) != 2 {
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
||||||
}
|
|
||||||
|
|
||||||
overUnder := overUnderStr[0]
|
|
||||||
|
|
||||||
if overUnder != "Over" && overUnder != "Under" {
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing over under: %s", outcome.OddHeader)
|
|
||||||
}
|
|
||||||
threshold, err := strconv.ParseFloat(overUnderStr[1], 64)
|
|
||||||
if err != nil {
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, 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 score.Home < score.Away {
|
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if overUnder == "Over" && totalScore > threshold {
|
overUnder := overUnderStr[0]
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
|
||||||
} else if overUnder == "Under" && totalScore < threshold {
|
if overUnder != "Over" && overUnder != "Under" {
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing over under: %s", outcome.OddHeader)
|
||||||
}
|
}
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
threshold, err := strconv.ParseFloat(overUnderStr[1], 64)
|
||||||
case "2":
|
if err != nil {
|
||||||
if score.Away < score.Home {
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
|
||||||
}
|
|
||||||
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
|
// Since the threshold will come in a xx.5 format, there is no VOID for this kind of bet
|
||||||
default:
|
totalScore := float64(score.Home + score.Away)
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed to parse over and under: %s", outcome.OddName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Team Total betting is a type of bet where the bettor predicts the total number of points scored by a specific team in a match
|
switch outcome.OddHeader {
|
||||||
// is over or under a specified number.
|
case "1":
|
||||||
func evaluateTeamTotal(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
if score.Home < score.Away {
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
// The handicap will be in the format "U {float}" or "O {float}"
|
if overUnder == "Over" && totalScore > threshold {
|
||||||
// U and O denoting over and under for this case
|
|
||||||
overUnderStr := strings.Split(outcome.OddHandicap, " ")
|
|
||||||
if len(overUnderStr) != 2 {
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddHandicap)
|
|
||||||
}
|
|
||||||
|
|
||||||
overUnder := overUnderStr[0]
|
|
||||||
|
|
||||||
if overUnder != "Over" && overUnder != "Under" {
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing over under: %s", outcome.OddHeader)
|
|
||||||
}
|
|
||||||
threshold, err := strconv.ParseFloat(overUnderStr[1], 64)
|
|
||||||
if err != nil {
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, 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_ERROR, fmt.Errorf("failed to parse over and under: %s", outcome.OddName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Result and Both Teams To Score X Points is a type of bet where the bettor predicts whether both teams will score a certain number of points
|
|
||||||
// and also the result fo the match
|
|
||||||
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_ERROR, 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_ERROR, 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
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
} else if !isScorePoints && score.Home < int(threshold) && score.Away < int(threshold) {
|
} else if overUnder == "Under" && totalScore < threshold {
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
}
|
}
|
||||||
}
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
case outcome.AwayTeamName:
|
case "2":
|
||||||
if score.Away > score.Home {
|
if score.Away < score.Home {
|
||||||
if isScorePoints && score.Home >= int(threshold) && score.Away >= int(threshold) {
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
if overUnder == "Over" && totalScore > threshold {
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
} else if !isScorePoints && score.Home < int(threshold) && score.Away < int(threshold) {
|
} else if overUnder == "Under" && totalScore < threshold {
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed to parse over and under: %s", outcome.OddName)
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("team name error: %s", teamName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
// Team Total betting is a type of bet where the bettor predicts the total number of points scored by a specific team in a match
|
||||||
|
// is over or under a specified number.
|
||||||
|
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_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddHandicap)
|
||||||
|
}
|
||||||
|
|
||||||
// Both Teams To Score X Points is a type of bet where the bettor predicts whether both teams will score a certain number of points.
|
overUnder := overUnderStr[0]
|
||||||
func evaluateBTTSX(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
|
||||||
threshold, err := strconv.ParseInt(outcome.OddName, 10, 64)
|
if overUnder != "Over" && overUnder != "Under" {
|
||||||
if err != nil {
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing over under: %s", outcome.OddHeader)
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
}
|
||||||
|
threshold, err := strconv.ParseFloat(overUnderStr[1], 64)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, 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_ERROR, fmt.Errorf("failed to parse over and under: %s", outcome.OddName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
switch outcome.OddHeader {
|
// Result and Both Teams To Score X Points is a type of bet where the bettor predicts whether both teams will score a certain number of points
|
||||||
case "Yes":
|
// and also the result fo the match
|
||||||
if score.Home >= int(threshold) && score.Away >= int(threshold) {
|
func evaluateResultAndBTTSX(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
|
||||||
}
|
// The name parameter will hold value "name": "{team_name} and {Yes | No}"
|
||||||
case "No":
|
// The best way to do this is to evaluate backwards since there might be
|
||||||
if score.Home < int(threshold) && score.Away < int(threshold) {
|
// teams with 'and' in their name
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
// 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_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
teamName := strings.TrimSpace(strings.Join(oddNameSplit[:len(oddNameSplit)-2], ""))
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
threshold, err := strconv.ParseInt(outcome.OddHeader, 10, 64)
|
||||||
}
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddHeader)
|
||||||
// Money Line 3 Way betting is a type of bet where the bettor predicts the outcome of a match with three possible outcomes: home win, away win, or draw.
|
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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_ERROR, fmt.Errorf("team name error: %s", teamName)
|
||||||
|
}
|
||||||
|
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
case "Tie":
|
|
||||||
if score.Home == score.Away {
|
}
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
|
||||||
|
// Both Teams To Score X Points is a type of bet where the bettor predicts whether both teams will score a certain number of 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_ERROR, fmt.Errorf("invalid threshold: %s", outcome.OddName)
|
||||||
}
|
}
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
|
||||||
case "2": // Away win
|
switch outcome.OddHeader {
|
||||||
if score.Away > score.Home {
|
case "Yes":
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
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_ERROR, fmt.Errorf("invalid odd header: %s", outcome.OddHeader)
|
||||||
}
|
}
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
|
||||||
default:
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Double Result betting is a type of bet where the bettor predicts the outcome of a match at both half-time and full-time.
|
|
||||||
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_ERROR, 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_ERROR, fmt.Errorf("invalid oddname: %s", firstHalfWinner)
|
|
||||||
}
|
|
||||||
if secondHalfWinner != outcome.HomeTeamName && secondHalfWinner != outcome.AwayTeamName && secondHalfWinner != "Tie" {
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, 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
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
// Money Line 3 Way betting is a type of bet where the bettor predicts the outcome of a match with three possible outcomes: home win, away win, or draw.
|
||||||
case secondHalfWinner == outcome.HomeTeamName && firstHalfScore.Home < firstHalfScore.Away:
|
func evaluateMoneyLine3Way(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
switch outcome.OddName {
|
||||||
case secondHalfWinner == outcome.AwayTeamName && firstHalfScore.Away < firstHalfScore.Home:
|
case "1": // Home win
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
if score.Home > score.Away {
|
||||||
case secondHalfWinner == "Tie" && firstHalfScore.Home != firstHalfScore.Away:
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
return domain.OUTCOME_STATUS_LOSS, 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_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
// Double Result betting is a type of bet where the bettor predicts the outcome of a match at both half-time and full-time.
|
||||||
}
|
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_ERROR, fmt.Errorf("invalid odd name: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
firstHalfWinner := strings.TrimSpace(halfWins[0])
|
||||||
|
secondHalfWinner := strings.TrimSpace(halfWins[1])
|
||||||
|
|
||||||
// Highest Scoring Half betting is a type of bet where the bettor predicts which half of the match will have the highest total score.
|
if firstHalfWinner != outcome.HomeTeamName && firstHalfWinner != outcome.AwayTeamName && firstHalfWinner != "Tie" {
|
||||||
func evaluateHighestScoringHalf(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", firstHalfWinner)
|
||||||
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 secondHalfWinner != outcome.HomeTeamName && secondHalfWinner != outcome.AwayTeamName && secondHalfWinner != "Tie" {
|
||||||
if firstHalfTotal < secondHalfTotal {
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", firstHalfWinner)
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
|
||||||
}
|
}
|
||||||
case "Tie":
|
|
||||||
if firstHalfTotal == secondHalfTotal {
|
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
||||||
}
|
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highest Scoring Quarter betting is a type of bet where the bettor predicts which quarter of the match will have the highest score.
|
switch {
|
||||||
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) {
|
case firstHalfWinner == outcome.HomeTeamName && firstHalfScore.Home < firstHalfScore.Away:
|
||||||
firstQuarterTotal := firstScore.Home + firstScore.Away
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
secondQuarterTotal := secondScore.Home + secondScore.Away
|
case firstHalfWinner == outcome.AwayTeamName && firstHalfScore.Away < firstHalfScore.Home:
|
||||||
thirdQuarterTotal := thirdScore.Home + thirdScore.Away
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
fourthQuarterTotal := fourthScore.Home + fourthScore.Away
|
case firstHalfWinner == "Tie" && firstHalfScore.Home != firstHalfScore.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_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
||||||
}
|
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Team With Highest Scoring Quarter betting is a type of bet where the bettor predicts which team will have the highest score in a specific quarter.
|
|
||||||
func evaluateTeamWithHighestScoringQuarter(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) {
|
|
||||||
homeTeamHighestQuarter := max(firstScore.Home, secondScore.Home, thirdScore.Home, fourthScore.Home)
|
|
||||||
awayTeamHighestQuarter := max(firstScore.Away, secondScore.Away, thirdScore.Away, fourthScore.Away)
|
|
||||||
|
|
||||||
switch outcome.OddName {
|
|
||||||
case "1":
|
|
||||||
if homeTeamHighestQuarter > awayTeamHighestQuarter {
|
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
|
||||||
}
|
|
||||||
case "2":
|
|
||||||
if awayTeamHighestQuarter > homeTeamHighestQuarter {
|
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
|
||||||
}
|
|
||||||
case "Tie":
|
|
||||||
if homeTeamHighestQuarter == awayTeamHighestQuarter {
|
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
||||||
}
|
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handicap and Total betting is a combination of spread betting and total points betting
|
|
||||||
// where the bettor predicts the outcome of a match with a point spread and the total number of points scored is over or under a specified number.
|
|
||||||
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_ERROR, 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
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
}
|
}
|
||||||
} else if overUnder == "Under" {
|
|
||||||
if total > threshold {
|
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_LOSS, nil
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing over and under: %s", outcome.OddName)
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
handicap, err := strconv.ParseFloat(nameSplit[len(nameSplit)-4], 10)
|
// Highest Scoring Half betting is a type of bet where the bettor predicts which half of the match will have the highest total score.
|
||||||
if err != nil {
|
func evaluateHighestScoringHalf(outcome domain.BetOutcome, firstScore struct{ Home, Away int }, secondScore struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("failed parsing handicap: %s", outcome.OddName)
|
firstHalfTotal := firstScore.Home + firstScore.Away
|
||||||
}
|
secondHalfTotal := secondScore.Home + secondScore.Away
|
||||||
|
switch outcome.OddName {
|
||||||
teamName := strings.TrimSpace(strings.Join(nameSplit[:len(nameSplit)-4], ""))
|
case "1st Half":
|
||||||
|
if firstHalfTotal > secondHalfTotal {
|
||||||
adjustedHomeScore := float64(score.Home)
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
adjustedAwayScore := float64(score.Away)
|
}
|
||||||
|
case "2nd Half":
|
||||||
switch teamName {
|
if firstHalfTotal < secondHalfTotal {
|
||||||
case outcome.HomeTeamName:
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
adjustedHomeScore += handicap
|
}
|
||||||
if adjustedHomeScore > adjustedAwayScore {
|
case "Tie":
|
||||||
return domain.OUTCOME_STATUS_WIN, nil
|
if firstHalfTotal == secondHalfTotal {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
||||||
}
|
}
|
||||||
return domain.OUTCOME_STATUS_LOSS, 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_ERROR, fmt.Errorf("failed parsing team name: %s", outcome.OddName)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// Highest Scoring Quarter betting is a type of bet where the bettor predicts which quarter of the match will have the highest score.
|
||||||
|
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
|
||||||
|
|
||||||
// Winning Margin betting is a type of bet where the bettor predicts the margin of victory in a match.
|
switch outcome.OddName {
|
||||||
func evaluateWinningMargin(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
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_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Team With Highest Scoring Quarter betting is a type of bet where the bettor predicts which team will have the highest score in a specific quarter.
|
||||||
|
func evaluateTeamWithHighestScoringQuarter(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) {
|
||||||
|
homeTeamHighestQuarter := max(firstScore.Home, secondScore.Home, thirdScore.Home, fourthScore.Home)
|
||||||
|
awayTeamHighestQuarter := max(firstScore.Away, secondScore.Away, thirdScore.Away, fourthScore.Away)
|
||||||
|
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "1":
|
||||||
|
if homeTeamHighestQuarter > awayTeamHighestQuarter {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
case "2":
|
||||||
|
if awayTeamHighestQuarter > homeTeamHighestQuarter {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
case "Tie":
|
||||||
|
if homeTeamHighestQuarter == awayTeamHighestQuarter {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handicap and Total betting is a combination of spread betting and total points betting
|
||||||
|
// where the bettor predicts the outcome of a match with a point spread and the total number of points scored is over or under a specified number.
|
||||||
|
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_ERROR, 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_ERROR, 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_ERROR, 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_ERROR, fmt.Errorf("failed parsing team name: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// Winning Margin betting is a type of bet where the bettor predicts the margin of victory in a match.
|
||||||
|
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_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
margin, err := strconv.ParseInt(marginSplit[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, 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_ERROR, fmt.Errorf("invalid oddheader: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Highest Scoring Period betting is a type of bet where the bettor predicts which period of the match will have the highest total score.
|
||||||
|
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_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tied After Regulation is a type of bet where the bettor predicts whether the match will end in a tie after regulation time.
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
marginSplit := strings.Split(outcome.OddName, "")
|
|
||||||
if len(marginSplit) < 1 {
|
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
||||||
}
|
}
|
||||||
|
|
||||||
margin, err := strconv.ParseInt(marginSplit[0], 10, 64)
|
// evaluateRugbyOutcome evaluates the outcome of a Rugby bet
|
||||||
if err != nil {
|
func evaluateRugbyOutcome(outcome domain.BetOutcome, result *domain.RugbyResultResponse) (domain.OutcomeStatus, error) {
|
||||||
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
isGtr := false
|
// evaluateBaseballOutcome evaluates the outcome of a Baseball bet
|
||||||
if len(marginSplit) == 2 {
|
func evaluateBaseballOutcome(outcome domain.BetOutcome, result *domain.BaseballResultResponse) (domain.OutcomeStatus, error) {
|
||||||
isGtr = marginSplit[1] == "+"
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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_ERROR, fmt.Errorf("invalid oddheader: %s", outcome.OddHeader)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Highest Scoring Period betting is a type of bet where the bettor predicts which period of the match will have the highest total score.
|
|
||||||
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_ERROR, fmt.Errorf("invalid oddname: %s", outcome.OddName)
|
|
||||||
}
|
|
||||||
return domain.OUTCOME_STATUS_LOSS, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tied After Regulation is a type of bet where the bettor predicts whether the match will end in a tie after regulation time.
|
|
||||||
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_ERROR, 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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
49
internal/services/result/service_test.go
Normal file
49
internal/services/result/service_test.go
Normal file
|
|
@ -0,0 +1,49 @@
|
||||||
|
package result
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/SamuelTariku/FortuneBet-Backend/internal/domain"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestEvaluateFootballOutcome(t *testing.T) {
|
||||||
|
service := &Service{} // or your real logger
|
||||||
|
|
||||||
|
// Mock outcome
|
||||||
|
outcome := domain.BetOutcome{
|
||||||
|
ID: 1,
|
||||||
|
BetID: 1,
|
||||||
|
EventID: 1001,
|
||||||
|
OddID: 2001,
|
||||||
|
SportID: 1, // Assuming 1 = Football
|
||||||
|
HomeTeamName: "Manchester",
|
||||||
|
AwayTeamName: "Liverpool",
|
||||||
|
MarketID: int64(domain.FOOTBALL_FULL_TIME_RESULT),
|
||||||
|
MarketName: "Full Time Result",
|
||||||
|
Odd: 1.75,
|
||||||
|
OddName: "2", // Home win
|
||||||
|
OddHeader: "1",
|
||||||
|
OddHandicap: "",
|
||||||
|
Status: domain.OUTCOME_STATUS_PENDING, // Initial status
|
||||||
|
Expires: time.Now().Add(24 * time.Hour),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Parsed result (simulate Bet365 JSON)
|
||||||
|
finalScore := struct{ Home, Away int }{Home: 2, Away: 1}
|
||||||
|
firstHalfScore := struct{ Home, Away int }{Home: 1, Away: 1}
|
||||||
|
secondHalfScore := struct{ Home, Away int }{Home: 1, Away: 0}
|
||||||
|
corners := struct{ Home, Away int }{Home: 5, Away: 3}
|
||||||
|
halfTimeCorners := struct{ Home, Away int }{Home: 2, Away: 2}
|
||||||
|
events := []map[string]string{
|
||||||
|
{"type": "goal", "team": "home", "minute": "23"},
|
||||||
|
{"type": "goal", "team": "away", "minute": "34"},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Act
|
||||||
|
status, _ := service.evaluateFootballOutcome(outcome, finalScore, firstHalfScore, secondHalfScore, corners, halfTimeCorners, events)
|
||||||
|
|
||||||
|
fmt.Printf("\n\nBet Outcome: %v\n\n", &status)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -42,7 +42,7 @@ func (h *Handler) LaunchVeliGame(c *fiber.Ctx) error {
|
||||||
// HandleVeliCallback godoc
|
// HandleVeliCallback godoc
|
||||||
// @Summary Veli Games webhook handler
|
// @Summary Veli Games webhook handler
|
||||||
// @Description Processes game round settlements from Veli
|
// @Description Processes game round settlements from Veli
|
||||||
// @Tags Virtual Games
|
// @Tags Veli Games
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param payload body domain.VeliCallback true "Callback payload"
|
// @Param payload body domain.VeliCallback true "Callback payload"
|
||||||
|
|
|
||||||
|
|
@ -194,11 +194,11 @@ func (a *App) initAppRoutes() {
|
||||||
group.Post("/webhooks/alea-play", a.authMiddleware, h.HandleAleaCallback)
|
group.Post("/webhooks/alea-play", a.authMiddleware, h.HandleAleaCallback)
|
||||||
|
|
||||||
//Veli Virtual Game Routes
|
//Veli Virtual Game Routes
|
||||||
group.Get("/veli-games/launch", a.authMiddleware, h.LaunchVeliGame)
|
group.Get("/veli-games/launch", h.LaunchVeliGame)
|
||||||
group.Post("/webhooks/veli-games", a.authMiddleware, h.HandleVeliCallback)
|
group.Post("/webhooks/veli-games", h.HandleVeliCallback)
|
||||||
|
|
||||||
// Recommendation Routes
|
// Recommendation Routes
|
||||||
group.Get("/virtual-games/recommendations/:userID", a.authMiddleware, h.GetRecommendations)
|
group.Get("/virtual-games/recommendations/:userID", h.GetRecommendations)
|
||||||
|
|
||||||
// Transactions /transactions
|
// Transactions /transactions
|
||||||
a.fiber.Post("/transaction", a.authMiddleware, h.CreateTransaction)
|
a.fiber.Post("/transaction", a.authMiddleware, h.CreateTransaction)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user