feat: added new odd markets to football
This commit is contained in:
parent
485cba3c9c
commit
aa893ddf74
|
|
@ -36,12 +36,18 @@ type OddsMarket struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
type FootballOddsResponse struct {
|
type FootballOddsResponse struct {
|
||||||
EventID string `json:"event_id"`
|
EventID string `json:"event_id"`
|
||||||
FI string `json:"FI"`
|
FI string `json:"FI"`
|
||||||
Main OddsSection `json:"main"`
|
AsianLines OddsSection `json:"asian_lines"`
|
||||||
AsianLines OddsSection `json:"asian_lines"`
|
Cards OddsSection `json:"cards"`
|
||||||
Goals OddsSection `json:"goals"`
|
Corners OddsSection `json:"corners"`
|
||||||
Half OddsSection `json:"half"`
|
Goals OddsSection `json:"goals"`
|
||||||
|
Half OddsSection `json:"half"`
|
||||||
|
Main OddsSection `json:"main"`
|
||||||
|
Minutes OddsSection `json:"minutes"`
|
||||||
|
Others []OddsSection `json:"others"`
|
||||||
|
Player OddsSection `json:"player"`
|
||||||
|
Specials OddsSection `json:"specials"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type BasketballOddsResponse struct {
|
type BasketballOddsResponse struct {
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,8 @@ package domain
|
||||||
type FootballMarket int64
|
type FootballMarket int64
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
|
||||||
|
// Main
|
||||||
FOOTBALL_FULL_TIME_RESULT FootballMarket = 40 //"full_time_result"
|
FOOTBALL_FULL_TIME_RESULT FootballMarket = 40 //"full_time_result"
|
||||||
FOOTBALL_DOUBLE_CHANCE FootballMarket = 10114 //"double_chance"
|
FOOTBALL_DOUBLE_CHANCE FootballMarket = 10114 //"double_chance"
|
||||||
FOOTBALL_GOALS_OVER_UNDER FootballMarket = 981 //"goals_over_under"
|
FOOTBALL_GOALS_OVER_UNDER FootballMarket = 981 //"goals_over_under"
|
||||||
|
|
@ -10,21 +12,122 @@ const (
|
||||||
FOOTBALL_ASIAN_HANDICAP FootballMarket = 938 //"asian_handicap"
|
FOOTBALL_ASIAN_HANDICAP FootballMarket = 938 //"asian_handicap"
|
||||||
FOOTBALL_GOAL_LINE FootballMarket = 10143 //"goal_line"
|
FOOTBALL_GOAL_LINE FootballMarket = 10143 //"goal_line"
|
||||||
|
|
||||||
FOOTBALL_HALF_TIME_RESULT FootballMarket = 1579 //"half_time_result"
|
// Main New
|
||||||
FOOTBALL_FIRST_HALF_ASIAN_HANDICAP FootballMarket = 50137 //"1st_half_asian_handicap"
|
FOOTBALL_FULL_TIME_RESULT_ENHANCED FootballMarket = 4001 //"full_time_result_–_enhanced_prices"
|
||||||
FOOTBALL_FIRST_HALF_GOAL_LINE FootballMarket = 50136 //"1st_half_goal_line"
|
FOOTBALL_BOTH_TEAMS_TO_SCORE FootballMarket = 10150 //"both_teams_to_score"
|
||||||
FOOTBALL_FIRST_TEAM_TO_SCORE FootballMarket = 1178 //"first_team_to_score"
|
FOOTBALL_RESULT_BOTH_TEAMS_TO_SCORE FootballMarket = 50404 //"result_both_teams_to_score"
|
||||||
FOOTBALL_GOALS_ODD_EVEN FootballMarket = 10111 //"goals_odd_even"
|
FOOTBALL_MATCH_GOAL_RANGE FootballMarket = 177816 //"match_goals_range"
|
||||||
FOOTBALL_DRAW_NO_BET FootballMarket = 10544 //"draw_no_bet"
|
FOOTBALL_TEAM_GOAL_RANGE FootballMarket = 177817 //"team_goals_range"
|
||||||
|
FOOTBALL_BOTH_TEAMS_TO_RECEIVE_CARDS FootballMarket = 50942 //"both_teams_to_receive_cards"
|
||||||
|
FOOTBALL_FIRST_HALF_GOAL_RANGE FootballMarket = 177819 //"1st_half_goals_range"
|
||||||
|
FOOTBALL_SECOND_HALF_GOAL_RANGE FootballMarket = 177820 //"2nd_half_goals_range"
|
||||||
|
FOOTBALL_RESULT_GOAL_RANGE FootballMarket = 177821 //"results_goals_range"
|
||||||
|
FOOTBALL_DOUBLE_CHANCE_GOAL_RANGE FootballMarket = 177822 //"double_chance_goals_range"
|
||||||
|
|
||||||
FOOTBALL_CORNERS FootballMarket = 760 //"corners"
|
// Half
|
||||||
FOOTBALL_CORNERS_TWO_WAY FootballMarket = 10235 //"corners_2_way"
|
FOOTBALL_HALF_TIME_RESULT FootballMarket = 1579 //"half_time_result"
|
||||||
FOOTBALL_FIRST_HALF_CORNERS FootballMarket = 10539 //"first_half_corners"
|
FOOTBALL_FIRST_HALF_ASIAN_HANDICAP FootballMarket = 50137 //"1st_half_asian_handicap"
|
||||||
FOOTBALL_ASIAN_TOTAL_CORNERS FootballMarket = 10164 //"asian_total_corners"
|
FOOTBALL_FIRST_HALF_GOAL_LINE FootballMarket = 50136 //"1st_half_goal_line"
|
||||||
FOOTBALL_FIRST_HALF_ASIAN_CORNERS FootballMarket = 10233 //"1st_half_asian_corners"
|
FOOTBALL_FIRST_TEAM_TO_SCORE FootballMarket = 1178 //"first_team_to_score"
|
||||||
FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN FootballMarket = 10206 //"1st_half_goals_odd_even"
|
FOOTBALL_GOALS_ODD_EVEN FootballMarket = 10111 //"goals_odd_even"
|
||||||
FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN FootballMarket = 50433 //"2nd_half_goals_odd_even"
|
FOOTBALL_DRAW_NO_BET FootballMarket = 10544 //"draw_no_bet"
|
||||||
|
FOOTBALL_HALF_TIME_DOUBLE_CHANCE FootballMarket = 10257 //"half_time_double_chance"
|
||||||
|
FOOTBALL_HALF_TIME_RESULT_BOTH_TEAMS_TO_SCORE FootballMarket = 50425 //"half_time_result_both_teams_to_score"
|
||||||
|
FOOTBALL_ALTERNATE_FIRST_HALF_ASIAN_HANDICAP FootballMarket = 50265 //"alternative_1st_half_asian_handicap"
|
||||||
|
FOOTBALL_ALTERNATE_FIRST_HALF_GOAL_LINE FootballMarket = 50266 //"alternative_1st_half_goal_line"
|
||||||
|
FOOTBALL_FIRST_HALF_HANDICAP FootballMarket = 50264 //"1st_half_handicap"
|
||||||
|
FOOTBALL_ALTERNATE_FIRST_HALF_HANDICAP FootballMarket = 10207 //"alternative_1st_half_handicap_result"
|
||||||
|
FOOTBALL_FIRST_HALF_GOAL FootballMarket = 10538 //"first_half_goals"
|
||||||
|
FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN FootballMarket = 10206 //"1st_half_goals_odd_even"
|
||||||
|
FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN FootballMarket = 50433 //"2nd_half_goals_odd_even"
|
||||||
|
FOOTBALL_HALF_TIME_CORRECT_SCORE FootballMarket = 10540 //"half_time_correct_score"
|
||||||
|
FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF FootballMarket = 50424 //"both_teams_to_score_in_1st_half"
|
||||||
|
FOOTBALL_BOTH_TEAMS_TO_SCORE_SECOND_HALF FootballMarket = 50432 //"both_teams_to_score_in_2nd_half"
|
||||||
|
FOOTBALL_TO_SCORE_IN_HALF FootballMarket = 50419 //"to_score_in_half"
|
||||||
|
FOOTBALL_HALF_WITH_MOST_GOALS FootballMarket = 10537 //"half_with_most_goals"
|
||||||
|
FOOTBALL_HOME_TEAM_WITH_HIGHEST_SCORING_HALF FootballMarket = 50417 //"home_team_highest_scoring_half"
|
||||||
|
FOOTBALL_AWAY_TEAM_WITH_HIGHEST_SCORING_HALF FootballMarket = 50418 //"away_team_highest_scoring_half"
|
||||||
|
FOOTBALL_SECOND_HALF_RESULT FootballMarket = 10208 //"2nd_half_result"
|
||||||
|
FOOTBALL_SECOND_HALF_GOALS FootballMarket = 10209 //"2nd_half_goals"
|
||||||
|
|
||||||
|
// Minutes
|
||||||
|
FOOTBALL_TEN_MINUTE_RESULT FootballMarket = 10244 //"10_minute_result"
|
||||||
|
FOOTBALL_FIRST_TEN_MINUTE FootballMarket = 10245 //"first_10_minutes_(00:00_09:59)"
|
||||||
|
|
||||||
|
// Others
|
||||||
|
FOOTBALL_TEAM_PERFORMANCE FootballMarket = 10110 //"team_performances"
|
||||||
|
FOOTBALL_TEAM_TOTAL_GOALS FootballMarket = 10127 //"team_total_goals"
|
||||||
|
FOOTBALL_ASIAN_TOTAL_CARDS FootballMarket = 10166 //"asian_total_cards"
|
||||||
|
FOOTBALL_EXACT_TOTAL_GOALS FootballMarket = 10203 //"asian_total_cards"
|
||||||
|
FOOTBALL_ALTERNATIVE_HANDICAP_RESULT FootballMarket = 10204 //"alternative_handicap_result"
|
||||||
|
FOOTBALL_EXACT_FIRST_HALF_GOALS FootballMarket = 10205 //"exact_1st_half_goals"
|
||||||
|
FOOTBALL_CLEAN_SHEET FootballMarket = 10210 //"clean_sheet"
|
||||||
|
FOOTBALL_TEAMS_TO_SCORE FootballMarket = 10211 //"teams_to_score"
|
||||||
|
FOOTBALL_TIME_OF_FIRST_TEAM_GOAL FootballMarket = 10214 //"time_of_1st_team_goal"
|
||||||
|
FOOTBALL_FIRST_GOAL_METHOD FootballMarket = 10216 //"first_goal_method"
|
||||||
|
FOOTBALL_MULTI_SCORERS FootballMarket = 10217 //"multi_scorers"
|
||||||
|
FOOTBALL_OWN_GOAL FootballMarket = 10223 //"own_goal"
|
||||||
|
FOOTBALL_TO_SCORE_PENALTY FootballMarket = 10229 //"to_score_a_penalty"
|
||||||
|
FOOTBALL_TO_MISS_PENALTY FootballMarket = 10230 //"to_miss_a_penalty"
|
||||||
|
FOOTBALL_ASIAN_HANDICAP_CARDS FootballMarket = 10239 //"asian_handicap_cards"
|
||||||
|
FOOTBALL_CARD_HANDICAP FootballMarket = 10240 //"card_handicap"
|
||||||
|
FOOTBALL_ALTERNATIVE_CARD_HANDICAP FootballMarket = 10241 //"alternative_card_handicap"
|
||||||
|
FOOTBALL_TEAM_CARDS FootballMarket = 10242 //"team_cards"
|
||||||
|
FOOTBALL_EXACT_SECOND_HALF_GOALS FootballMarket = 10252 //"exact_2nd_half_goals"
|
||||||
|
FOOTBALL_EARLY_GOAL FootballMarket = 10258 //"early_goal"
|
||||||
|
FOOTBALL_LATE_GOAL FootballMarket = 10259 //"late_goal"
|
||||||
|
FOOTBALL_FIRST_MATCH_CORNER FootballMarket = 10519 //"first_match_corner"
|
||||||
|
FOOTBALL_LAST_MATCH_CORNER FootballMarket = 10520 //"last_match_corner"
|
||||||
|
FOOTBALL_LAST_TEAM_TO_SCORE FootballMarket = 10534 //"last_team_to_score"
|
||||||
|
FOOTBALL_CORNER_HANDICAP FootballMarket = 10535 //"corner_handicap"
|
||||||
|
FOOTBALL_NUMBER_OF_GOALS_IN_MATCH FootballMarket = 10536 //"number_of_goals_in_match"
|
||||||
|
FOOTBALL_TIME_OF_FIRST_GOAL_BRACKETS FootballMarket = 10541 //"time_of_first_goal_brackets"
|
||||||
|
FOOTBALL_CORNER_MATCH_BET FootballMarket = 1175 //"corner_match_bet"
|
||||||
|
FOOTBALL_MULTI_CORNERS FootballMarket = 1181 //"Multicorners"
|
||||||
|
FOOTBALL_TIME_OF_FIRST_CARD FootballMarket = 1183 //"time_of_first_card"
|
||||||
|
FOOTBALL_HANDICAP_RESULT FootballMarket = 171 //"handicap_result"
|
||||||
|
FOOTBALL_TOTAL_GOAL_MINUTES FootballMarket = 1776 //"total_goal_minutes"
|
||||||
|
FOOTBALL_PLAYER_TO_SCORE_ASSIST FootballMarket = 177704 //"player_to_score_or_assist"
|
||||||
|
FOOTBALL_TEAM_TO_GET_MOST FootballMarket = 177790 //"team_to_get_most"
|
||||||
|
FOOTBALL_GOALSCORER FootballMarket = 45 //"goalscorers"
|
||||||
|
FOOTBALL_FIRST_CARD_RECEIVED FootballMarket = 476 //"first_card_received"
|
||||||
|
FOOTBALL_PLAYER_CARD FootballMarket = 50135 //"player_cards"
|
||||||
|
FOOTBALL_ALTERNATIVE_ASIAN_HANDICAP FootballMarket = 50138 //"alternative_asian_handicap"
|
||||||
|
FOOTBALL_ALTERNATIVE_GOAL_LINE FootballMarket = 50139 //"alternative_goal_line"
|
||||||
|
FOOTBALL_HOME_TEAM_ODD_EVEN_GOALS FootballMarket = 50406 //"home_team_odd_even_goals"
|
||||||
|
FOOTBALL_AWAY_TEAM_ODD_EVEN_GOALS FootballMarket = 50407 //"away_team_odd_even_goals"
|
||||||
|
FOOTBALL_HOME_TEAM_EXACT_GOALS FootballMarket = 50415 //"home_team_exact_goals"
|
||||||
|
FOOTBALL_AWAY_TEAM_EXACT_GOALS FootballMarket = 50416 //"away_team_exact_goals"
|
||||||
|
FOOTBALL_HALF_TIME_RESULT_TOTAL_GOALS FootballMarket = 50426 //"half_time_result_total_goals"
|
||||||
|
FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF_SECOND_HALF FootballMarket = 50435 //"both_teams_to_score_1st_half_2nd_half"
|
||||||
|
FOOTBALL_MATCH_SHOTS_ON_TARGET FootballMarket = 50527 //"match_shots_on_target"
|
||||||
|
FOOTBALL_MATCH_SHOTS FootballMarket = 50528 //"match_shots"
|
||||||
|
FOOTBALL_TEAM_SHOTS_ON_TARGET FootballMarket = 50530 //"team_shots_on_target"
|
||||||
|
FOOTBALL_TEAM_SHOTS FootballMarket = 50532 //"team_shots"
|
||||||
|
FOOTBALL_GOAL_METHOD FootballMarket = 50962 //"goal_method"
|
||||||
|
FOOTBALL_WINNING_MARGIN FootballMarket = 56 //"winning_margin"
|
||||||
|
FOOTBALL_TIME_OF_FIRST_CORNER FootballMarket = 761 //"time_of_first_corner"
|
||||||
|
|
||||||
|
// Player
|
||||||
|
FOOTBALL_TEAM_GOALSCORER FootballMarket = 10151 //"team_goalscorer"
|
||||||
|
FOOTBALL_PLAYER_SHOTS_ON_TARGET FootballMarket = 50920 //"player_shots_on_target"
|
||||||
|
FOOTBALL_PLAYER_SHOTS FootballMarket = 50921 //"player_shots"
|
||||||
|
|
||||||
|
// Specials
|
||||||
|
FOOTBALL_SPECIALS FootballMarket = 10224 //"specials
|
||||||
|
|
||||||
|
// Corner
|
||||||
|
FOOTBALL_CORNERS FootballMarket = 760 //"corners"
|
||||||
|
FOOTBALL_CORNERS_TWO_WAY FootballMarket = 10235 //"corners_2_way"
|
||||||
|
FOOTBALL_FIRST_HALF_CORNERS FootballMarket = 10539 //"first_half_corners"
|
||||||
|
FOOTBALL_ASIAN_TOTAL_CORNERS FootballMarket = 10164 //"asian_total_corners"
|
||||||
|
FOOTBALL_FIRST_HALF_ASIAN_CORNERS FootballMarket = 10233 //"1st_half_asian_corners"
|
||||||
|
FOOTBALL_ASIAN_HANDICAP_CORNERS FootballMarket = 10165 //"asian_handicap_corners"
|
||||||
|
FOOTBALL_ALTERNATIVE_CORNER FootballMarket = 10234 //"alternative_corners"
|
||||||
|
FOOTBALL_CORNER_RACE FootballMarket = 10238 //"corners_race"
|
||||||
|
|
||||||
|
// Cards
|
||||||
|
FOOTBALL_NUMBER_OF_CARDS_IN_MATCH FootballMarket = 10542 //"number_of_cards_in_match"
|
||||||
)
|
)
|
||||||
|
|
||||||
type BasketBallMarket int64
|
type BasketBallMarket int64
|
||||||
|
|
|
||||||
|
|
@ -328,11 +328,17 @@ func (s *ServiceImpl) ParseOddSections(ctx context.Context, res json.RawMessage,
|
||||||
return domain.ParseOddSectionsRes{}, err
|
return domain.ParseOddSectionsRes{}, err
|
||||||
}
|
}
|
||||||
eventFI = footballRes.FI
|
eventFI = footballRes.FI
|
||||||
|
OtherRes = footballRes.Others
|
||||||
sections = map[string]domain.OddsSection{
|
sections = map[string]domain.OddsSection{
|
||||||
"main": footballRes.Main,
|
"main": footballRes.Main,
|
||||||
"asian_lines": footballRes.AsianLines,
|
"asian_lines": footballRes.AsianLines,
|
||||||
"goals": footballRes.Goals,
|
"goals": footballRes.Goals,
|
||||||
"half": footballRes.Half,
|
"half": footballRes.Half,
|
||||||
|
"cards": footballRes.Cards,
|
||||||
|
"corners": footballRes.Corners,
|
||||||
|
"player": footballRes.Player,
|
||||||
|
"minutes": footballRes.Minutes,
|
||||||
|
"specials": footballRes.Specials,
|
||||||
}
|
}
|
||||||
case domain.BASKETBALL:
|
case domain.BASKETBALL:
|
||||||
var basketballRes domain.BasketballOddsResponse
|
var basketballRes domain.BasketballOddsResponse
|
||||||
|
|
|
||||||
|
|
@ -10,6 +10,25 @@ import (
|
||||||
|
|
||||||
// Football evaluations
|
// Football evaluations
|
||||||
|
|
||||||
|
// helper function to parse minute from event string like "77'" or "90+1'"
|
||||||
|
func parseEventMinute(eventText string) (int, error) {
|
||||||
|
parts := strings.Split(eventText, "'")
|
||||||
|
if len(parts) == 0 {
|
||||||
|
return 0, fmt.Errorf("invalid event text format")
|
||||||
|
}
|
||||||
|
timeStr := strings.TrimSpace(parts[0])
|
||||||
|
if strings.Contains(timeStr, "+") {
|
||||||
|
timeParts := strings.Split(timeStr, "+")
|
||||||
|
base, err1 := strconv.Atoi(timeParts[0])
|
||||||
|
extra, err2 := strconv.Atoi(timeParts[1])
|
||||||
|
if err1 != nil || err2 != nil {
|
||||||
|
return 0, fmt.Errorf("invalid injury time format")
|
||||||
|
}
|
||||||
|
return base + extra, nil
|
||||||
|
}
|
||||||
|
return strconv.Atoi(timeStr)
|
||||||
|
}
|
||||||
|
|
||||||
// Full Time Result betting is a type of bet where the bettor predicts the outcome of a match at the end of the full 90 minutes of play.
|
// Full Time Result betting is a type of bet where the bettor predicts the outcome of a match at the end of the full 90 minutes of play.
|
||||||
func evaluateFullTimeResult(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
func evaluateFullTimeResult(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
switch outcome.OddName {
|
switch outcome.OddName {
|
||||||
|
|
@ -428,6 +447,585 @@ func evaluateCorners(outcome domain.BetOutcome, corners struct{ Home, Away int }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// evaluateBothTeamsToScore checks if both teams scored in the match.
|
||||||
|
func evaluateBothTeamsToScore(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
btts := score.Home > 0 && score.Away > 0
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "Yes":
|
||||||
|
if btts {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
case "No":
|
||||||
|
if !btts {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for BTTS: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateResultAndBTTS checks for a combination of the match result and if both teams scored.
|
||||||
|
func evaluateResultAndBTTS(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
btts := score.Home > 0 && score.Away > 0
|
||||||
|
var bttsMatch bool
|
||||||
|
switch outcome.OddHeader {
|
||||||
|
case "Yes":
|
||||||
|
bttsMatch = btts
|
||||||
|
case "No":
|
||||||
|
bttsMatch = !btts
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Result/BTTS: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
var resultMatch bool
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "1":
|
||||||
|
resultMatch = score.Home > score.Away
|
||||||
|
case "2":
|
||||||
|
resultMatch = score.Away > score.Home
|
||||||
|
case "Draw":
|
||||||
|
resultMatch = score.Home == score.Away
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Result/BTTS: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if bttsMatch && resultMatch {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateCleanSheet checks if a selected team did not concede any goals.
|
||||||
|
func evaluateCleanSheet(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
var cleanSheet bool
|
||||||
|
switch outcome.OddHeader {
|
||||||
|
case "1": // Corresponds to Home team
|
||||||
|
cleanSheet = (score.Away == 0)
|
||||||
|
case "2": // Corresponds to Away team
|
||||||
|
cleanSheet = (score.Home == 0)
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Clean Sheet: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
betOnYes := outcome.OddHandicap == "Yes"
|
||||||
|
|
||||||
|
if cleanSheet == betOnYes {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateLastTeamToScore finds the last team that scored by checking events in reverse.
|
||||||
|
func evaluateLastTeamToScore(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) {
|
||||||
|
var lastGoalTeam string
|
||||||
|
for i := len(events) - 1; i >= 0; i-- {
|
||||||
|
event := events[i]
|
||||||
|
// A simple check for "Goal" in the event text
|
||||||
|
if strings.Contains(event["text"], "Goal") && !strings.Contains(event["text"], "disallowed") {
|
||||||
|
if strings.Contains(event["text"], outcome.HomeTeamName) {
|
||||||
|
lastGoalTeam = "1"
|
||||||
|
break
|
||||||
|
} else if strings.Contains(event["text"], outcome.AwayTeamName) {
|
||||||
|
lastGoalTeam = "2"
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "1":
|
||||||
|
if lastGoalTeam == "1" {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
case "2":
|
||||||
|
if lastGoalTeam == "2" {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
case "No Goal", "No Goals":
|
||||||
|
if lastGoalTeam == "" {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Last Team to Score: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateWinningMargin checks the margin of victory.
|
||||||
|
func evaluateFootballWinningMargin(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
homeWin := score.Home > score.Away
|
||||||
|
awayWin := score.Away > score.Home
|
||||||
|
margin := score.Home - score.Away
|
||||||
|
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "Score Draw":
|
||||||
|
if margin == 0 && score.Home > 0 {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
case "No Goal":
|
||||||
|
if margin == 0 && score.Home == 0 {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// Handles margins like "1", "2", "3", "4+"
|
||||||
|
var expectedMargin int
|
||||||
|
isPlus := strings.HasSuffix(outcome.OddName, "+")
|
||||||
|
marginStr := strings.TrimSuffix(outcome.OddName, "+")
|
||||||
|
|
||||||
|
_, err := fmt.Sscanf(marginStr, "%d", &expectedMargin)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("could not parse winning margin: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
teamWon := (outcome.OddHeader == "1" && homeWin) || (outcome.OddHeader == "2" && awayWin)
|
||||||
|
if !teamWon {
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
actualMargin := abs(margin)
|
||||||
|
if isPlus {
|
||||||
|
if actualMargin >= expectedMargin {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if actualMargin == expectedMargin {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func abs(x int) int {
|
||||||
|
if x < 0 {
|
||||||
|
return -x
|
||||||
|
}
|
||||||
|
return x
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateBothTeamsToReceiveCards checks if both teams received at least one card.
|
||||||
|
func evaluateBothTeamsToReceiveCards(outcome domain.BetOutcome, yellowCards, redCards struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
homeCards := yellowCards.Home + redCards.Home
|
||||||
|
awayCards := yellowCards.Away + redCards.Away
|
||||||
|
|
||||||
|
var conditionMet bool
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "a Card":
|
||||||
|
conditionMet = homeCards > 0 && awayCards > 0
|
||||||
|
case "2+ Cards":
|
||||||
|
conditionMet = homeCards >= 2 && awayCards >= 2
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Both Teams To Receive Cards: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
isBetOnYes := outcome.OddHeader == "Yes"
|
||||||
|
|
||||||
|
if conditionMet == isBetOnYes {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateHalfWithMostGoals compares total goals in each half.
|
||||||
|
func evaluateHalfWithMostGoals(outcome domain.BetOutcome, firstHalf, secondHalf struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
firstHalfGoals := firstHalf.Home + firstHalf.Away
|
||||||
|
secondHalfGoals := secondHalf.Home + secondHalf.Away
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "1st Half":
|
||||||
|
won = firstHalfGoals > secondHalfGoals
|
||||||
|
case "2nd Half":
|
||||||
|
won = secondHalfGoals > firstHalfGoals
|
||||||
|
case "Tie":
|
||||||
|
won = firstHalfGoals == secondHalfGoals
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Half with Most Goals: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateTeamHighestScoringHalf checks which half a specific team scored more goals in.
|
||||||
|
func evaluateTeamHighestScoringHalf(outcome domain.BetOutcome, firstHalf, secondHalf struct{ Home, Away int }, team string) (domain.OutcomeStatus, error) {
|
||||||
|
var first, second int
|
||||||
|
if team == "home" {
|
||||||
|
first = firstHalf.Home
|
||||||
|
second = secondHalf.Home
|
||||||
|
} else {
|
||||||
|
first = firstHalf.Away
|
||||||
|
second = secondHalf.Away
|
||||||
|
}
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "1st Half":
|
||||||
|
won = first > second
|
||||||
|
case "2nd Half":
|
||||||
|
won = second > first
|
||||||
|
case "Tie":
|
||||||
|
won = first == second
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Team Highest Scoring Half: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateTeamTotalGoals checks the total goals for a single team.
|
||||||
|
func evaluateTeamTotalGoals(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
var teamGoals int
|
||||||
|
if outcome.OddHeader == "1" {
|
||||||
|
teamGoals = score.Home
|
||||||
|
} else if outcome.OddHeader == "2" {
|
||||||
|
teamGoals = score.Away
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Team Total Goals: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
handicapStr := strings.TrimPrefix(outcome.OddHandicap, "Over ")
|
||||||
|
handicapStr = strings.TrimPrefix(handicapStr, "Under ")
|
||||||
|
handicap, err := strconv.ParseFloat(handicapStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap for Team Total Goals: %s", outcome.OddHandicap)
|
||||||
|
}
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
if strings.HasPrefix(outcome.OddHandicap, "Over") {
|
||||||
|
won = float64(teamGoals) > handicap
|
||||||
|
} else if strings.HasPrefix(outcome.OddHandicap, "Under") {
|
||||||
|
won = float64(teamGoals) < handicap
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap type for Team Total Goals: %s", outcome.OddHandicap)
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
// evaluateExactTotalGoals checks for the exact number of goals scored.
|
||||||
|
func evaluateExactTotalGoals(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
totalGoals := score.Home + score.Away
|
||||||
|
betGoalsStr := strings.TrimSuffix(strings.Fields(outcome.OddName)[0], "+")
|
||||||
|
betGoals, err := strconv.Atoi(betGoalsStr)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid bet value for Exact Total Goals: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
if strings.HasSuffix(outcome.OddName, "+") {
|
||||||
|
won = totalGoals >= betGoals
|
||||||
|
} else {
|
||||||
|
won = totalGoals == betGoals
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateTeamsToScore checks which teams scored in the match.
|
||||||
|
func evaluateTeamsToScore(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
homeScored := score.Home > 0
|
||||||
|
awayScored := score.Away > 0
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "Both Teams":
|
||||||
|
won = homeScored && awayScored
|
||||||
|
case "No Goal":
|
||||||
|
won = !homeScored && !awayScored
|
||||||
|
default:
|
||||||
|
if strings.HasSuffix(outcome.OddName, "Only") {
|
||||||
|
teamName := strings.TrimSuffix(outcome.OddName, " Only")
|
||||||
|
if teamName == outcome.HomeTeamName {
|
||||||
|
won = homeScored && !awayScored
|
||||||
|
} else if teamName == outcome.AwayTeamName {
|
||||||
|
won = !homeScored && awayScored
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Teams to Score: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateFirstMatchCorner checks which team took the first corner.
|
||||||
|
func evaluateFirstMatchCorner(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) {
|
||||||
|
for _, event := range events {
|
||||||
|
if strings.Contains(event["text"], "Corner") {
|
||||||
|
var firstCornerTeam string
|
||||||
|
if strings.Contains(event["text"], outcome.HomeTeamName) {
|
||||||
|
firstCornerTeam = "1"
|
||||||
|
} else if strings.Contains(event["text"], outcome.AwayTeamName) {
|
||||||
|
firstCornerTeam = "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
if outcome.OddName == firstCornerTeam {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil // No corners in the match
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateLastMatchCorner checks which team took the last corner.
|
||||||
|
func evaluateLastMatchCorner(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) {
|
||||||
|
for i := len(events) - 1; i >= 0; i-- {
|
||||||
|
event := events[i]
|
||||||
|
if strings.Contains(event["text"], "Corner") {
|
||||||
|
var lastCornerTeam string
|
||||||
|
if strings.Contains(event["text"], outcome.HomeTeamName) {
|
||||||
|
lastCornerTeam = "1"
|
||||||
|
} else if strings.Contains(event["text"], outcome.AwayTeamName) {
|
||||||
|
lastCornerTeam = "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
if outcome.OddName == lastCornerTeam {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil // No corners in the match
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateCornerMatchBet determines which team had more corners.
|
||||||
|
func evaluateCornerMatchBet(outcome domain.BetOutcome, corners struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
var won bool
|
||||||
|
switch outcome.OddName {
|
||||||
|
case "1":
|
||||||
|
won = corners.Home > corners.Away
|
||||||
|
case "Tie":
|
||||||
|
won = corners.Home == corners.Away
|
||||||
|
case "2":
|
||||||
|
won = corners.Away > corners.Home
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd name for Corner Match Bet: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateMultiCorners multiplies 1st half corners by 2nd half corners.
|
||||||
|
func evaluateMultiCorners(outcome domain.BetOutcome, totalCorners, halfTimeCorners struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
firstHalfTotal := halfTimeCorners.Home + halfTimeCorners.Away
|
||||||
|
secondHalfTotal := (totalCorners.Home + totalCorners.Away) - firstHalfTotal
|
||||||
|
multiCornerValue := firstHalfTotal * secondHalfTotal
|
||||||
|
|
||||||
|
handicap, err := strconv.ParseFloat(outcome.OddName, 64)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap for Multi Corners: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
if outcome.OddHeader == "Over" {
|
||||||
|
won = float64(multiCornerValue) > handicap
|
||||||
|
} else if outcome.OddHeader == "Under" {
|
||||||
|
won = float64(multiCornerValue) < handicap
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Multi Corners: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateMatchShotsOnTarget evaluates over/under for total shots on target.
|
||||||
|
func evaluateMatchShotsOnTarget(outcome domain.BetOutcome, onTarget struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
totalSOT := onTarget.Home + onTarget.Away
|
||||||
|
handicap, err := strconv.ParseFloat(outcome.OddName, 64)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap for Match Shots on Target: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
if outcome.OddHeader == "Over" {
|
||||||
|
won = float64(totalSOT) > handicap
|
||||||
|
} else if outcome.OddHeader == "Under" {
|
||||||
|
won = float64(totalSOT) < handicap
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Match Shots on Target: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateTeamShotsOnTarget evaluates over/under for a single team's shots on target.
|
||||||
|
func evaluateTeamShotsOnTarget(outcome domain.BetOutcome, onTarget struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
var teamSOT int
|
||||||
|
if outcome.OddHeader == "1" {
|
||||||
|
teamSOT = onTarget.Home
|
||||||
|
} else if outcome.OddHeader == "2" {
|
||||||
|
teamSOT = onTarget.Away
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid odd header for Team Shots on Target: %s", outcome.OddHeader)
|
||||||
|
}
|
||||||
|
|
||||||
|
handicapStr := strings.TrimPrefix(outcome.OddHandicap, "Over ")
|
||||||
|
handicapStr = strings.TrimPrefix(handicapStr, "Under ")
|
||||||
|
handicap, err := strconv.ParseFloat(handicapStr, 64)
|
||||||
|
if err != nil {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap for Team Shots on Target: %s", outcome.OddHandicap)
|
||||||
|
}
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
if strings.HasPrefix(outcome.OddHandicap, "Over") {
|
||||||
|
won = float64(teamSOT) > handicap
|
||||||
|
} else if strings.HasPrefix(outcome.OddHandicap, "Under") {
|
||||||
|
won = float64(teamSOT) < handicap
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("invalid handicap type for Team Shots on Target: %s", outcome.OddHandicap)
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateTimeOfFirstGoal checks if the first goal's timing matches the bet.
|
||||||
|
func evaluateTimeOfFirstGoal(outcome domain.BetOutcome, events []map[string]string) (domain.OutcomeStatus, error) {
|
||||||
|
firstGoalMin := -1
|
||||||
|
for _, event := range events {
|
||||||
|
if strings.Contains(event["text"], "1st Goal") {
|
||||||
|
min, err := parseEventMinute(event["text"])
|
||||||
|
if err == nil {
|
||||||
|
firstGoalMin = min
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic for Late Goal / Early Goal
|
||||||
|
if strings.Contains(outcome.OddName, "before") || strings.Contains(outcome.OddName, "after") {
|
||||||
|
var timeMarker int
|
||||||
|
var isBefore, betOnYes bool
|
||||||
|
|
||||||
|
if _, err := fmt.Sscanf(outcome.OddName, "Goal before %d:%d", &timeMarker, new(int)); err == nil {
|
||||||
|
isBefore = true
|
||||||
|
} else if _, err := fmt.Sscanf(outcome.OddName, "No Goal before %d:%d", &timeMarker, new(int)); err == nil {
|
||||||
|
isBefore = true
|
||||||
|
betOnYes = false
|
||||||
|
} else if _, err := fmt.Sscanf(outcome.OddName, "Goal after %d:%d", &timeMarker, new(int)); err == nil {
|
||||||
|
isBefore = false
|
||||||
|
} else if _, err := fmt.Sscanf(outcome.OddName, "No Goal after %d:%d", &timeMarker, new(int)); err == nil {
|
||||||
|
isBefore = false
|
||||||
|
betOnYes = false
|
||||||
|
} else {
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("could not parse time from odd name: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
var conditionMet bool
|
||||||
|
if isBefore {
|
||||||
|
conditionMet = firstGoalMin != -1 && firstGoalMin <= timeMarker
|
||||||
|
} else { // isAfter
|
||||||
|
// This requires finding the last goal, not just the first. This implementation is simplified for first goal.
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("late goal logic not fully implemented for all cases")
|
||||||
|
}
|
||||||
|
|
||||||
|
if conditionMet == betOnYes {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Logic for Time Brackets
|
||||||
|
if firstGoalMin == -1 { // No Goal
|
||||||
|
if outcome.OddName == "No Goal" {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
parts := strings.Split(outcome.OddName, "-")
|
||||||
|
if len(parts) == 2 {
|
||||||
|
start, _ := strconv.Atoi(strings.TrimSpace(parts[0]))
|
||||||
|
endStr := strings.Fields(parts[1])[0]
|
||||||
|
end, _ := strconv.Atoi(endStr)
|
||||||
|
|
||||||
|
if firstGoalMin >= start && firstGoalMin <= end {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unhandled time of goal format: %s", outcome.OddName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// evaluateSpecials handles various markets grouped under the "Specials" ID.
|
||||||
|
func evaluateSpecials(outcome domain.BetOutcome, finalScore, firstHalfScore, secondHalfScore struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
var team, betType string
|
||||||
|
team = outcome.OddHeader // "1" for home, "2" for away
|
||||||
|
betType = outcome.OddName
|
||||||
|
|
||||||
|
var won bool
|
||||||
|
switch betType {
|
||||||
|
case "To Win From Behind":
|
||||||
|
if team == "1" {
|
||||||
|
won = firstHalfScore.Home < firstHalfScore.Away && finalScore.Home > finalScore.Away
|
||||||
|
} else {
|
||||||
|
won = firstHalfScore.Away < firstHalfScore.Home && finalScore.Away > finalScore.Home
|
||||||
|
}
|
||||||
|
case "To Win to Nil":
|
||||||
|
if team == "1" {
|
||||||
|
won = finalScore.Home > finalScore.Away && finalScore.Away == 0
|
||||||
|
} else {
|
||||||
|
won = finalScore.Away > finalScore.Home && finalScore.Home == 0
|
||||||
|
}
|
||||||
|
case "To Win Either Half":
|
||||||
|
if team == "1" {
|
||||||
|
won = (firstHalfScore.Home > firstHalfScore.Away) || (secondHalfScore.Home > secondHalfScore.Away)
|
||||||
|
} else {
|
||||||
|
won = (firstHalfScore.Away > firstHalfScore.Home) || (secondHalfScore.Away > secondHalfScore.Home)
|
||||||
|
}
|
||||||
|
case "To Win Both Halves":
|
||||||
|
if team == "1" {
|
||||||
|
won = (firstHalfScore.Home > firstHalfScore.Away) && (secondHalfScore.Home > secondHalfScore.Away)
|
||||||
|
} else {
|
||||||
|
won = (firstHalfScore.Away > firstHalfScore.Home) && (secondHalfScore.Away > secondHalfScore.Home)
|
||||||
|
}
|
||||||
|
case "To Score in Both Halves":
|
||||||
|
if team == "1" {
|
||||||
|
won = firstHalfScore.Home > 0 && secondHalfScore.Home > 0
|
||||||
|
} else {
|
||||||
|
won = firstHalfScore.Away > 0 && secondHalfScore.Away > 0
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unsupported special market: %s", betType)
|
||||||
|
}
|
||||||
|
|
||||||
|
if won {
|
||||||
|
return domain.OUTCOME_STATUS_WIN, nil
|
||||||
|
}
|
||||||
|
return domain.OUTCOME_STATUS_LOSS, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Basketball evaluations
|
// Basketball evaluations
|
||||||
|
|
||||||
// Game Lines is an aggregate of money line, spread and total betting markets in one
|
// Game Lines is an aggregate of money line, spread and total betting markets in one
|
||||||
|
|
@ -629,6 +1227,9 @@ func evaluateTeamTotal(outcome domain.BetOutcome, score struct{ Home, Away int }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// 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
|
// 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
|
// and also the result fo the match
|
||||||
func evaluateResultAndBTTSX(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
func evaluateResultAndBTTSX(outcome domain.BetOutcome, score struct{ Home, Away int }) (domain.OutcomeStatus, error) {
|
||||||
|
|
|
||||||
|
|
@ -1027,7 +1027,13 @@ func (s *Service) parseFootball(resultRes json.RawMessage, outcome domain.BetOut
|
||||||
|
|
||||||
corners := parseStats(result.Stats.Corners)
|
corners := parseStats(result.Stats.Corners)
|
||||||
halfTimeCorners := parseStats(result.Stats.HalfTimeCorners)
|
halfTimeCorners := parseStats(result.Stats.HalfTimeCorners)
|
||||||
status, err := s.evaluateFootballOutcome(outcome, finalScore, firstHalfScore, secondHalfScore, corners, halfTimeCorners, result.Events)
|
|
||||||
|
yellowCards := parseStats(result.Stats.YellowCards)
|
||||||
|
redCards := parseStats(result.Stats.RedCards)
|
||||||
|
onTarget := parseStats(result.Stats.OnTarget)
|
||||||
|
offTarget := parseStats(result.Stats.OffTarget)
|
||||||
|
|
||||||
|
status, err := s.evaluateFootballOutcome(outcome, finalScore, firstHalfScore, secondHalfScore, corners, halfTimeCorners, yellowCards, redCards, onTarget, offTarget, result.Events)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
||||||
s.mongoLogger.Error(
|
s.mongoLogger.Error(
|
||||||
|
|
@ -1478,10 +1484,15 @@ func parseStats(stats []string) struct{ Home, Away int } {
|
||||||
return struct{ Home, Away int }{Home: home, Away: away}
|
return struct{ Home, Away int }{Home: home, Away: away}
|
||||||
}
|
}
|
||||||
|
|
||||||
// evaluateOutcome determines the outcome status based on market type and odd
|
// evaluateFootballOutcome determines the outcome status based on market type and odd.
|
||||||
|
// It uses helper functions to process the logic for each specific market.
|
||||||
func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore,
|
func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore,
|
||||||
firstHalfScore struct{ Home, Away int }, secondHalfScore struct{ Home, Away int },
|
firstHalfScore, secondHalfScore struct{ Home, Away int },
|
||||||
corners struct{ Home, Away int }, halfTimeCorners struct{ Home, Away int },
|
corners, halfTimeCorners struct{ Home, Away int },
|
||||||
|
yellowCards struct{ Home, Away int },
|
||||||
|
redCards struct{ Home, Away int },
|
||||||
|
onTarget struct{ Home, Away int },
|
||||||
|
offTarget struct{ Home, Away int },
|
||||||
events []map[string]string) (domain.OutcomeStatus, error) {
|
events []map[string]string) (domain.OutcomeStatus, error) {
|
||||||
|
|
||||||
if !domain.SupportedMarkets[outcome.MarketID] {
|
if !domain.SupportedMarkets[outcome.MarketID] {
|
||||||
|
|
@ -1494,7 +1505,7 @@ func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore,
|
||||||
}
|
}
|
||||||
|
|
||||||
switch outcome.MarketID {
|
switch outcome.MarketID {
|
||||||
case int64(domain.FOOTBALL_FULL_TIME_RESULT):
|
case int64(domain.FOOTBALL_FULL_TIME_RESULT), int64(domain.FOOTBALL_FULL_TIME_RESULT_ENHANCED):
|
||||||
return evaluateFullTimeResult(outcome, finalScore)
|
return evaluateFullTimeResult(outcome, finalScore)
|
||||||
case int64(domain.FOOTBALL_GOALS_OVER_UNDER):
|
case int64(domain.FOOTBALL_GOALS_OVER_UNDER):
|
||||||
return evaluateGoalsOverUnder(outcome, finalScore)
|
return evaluateGoalsOverUnder(outcome, finalScore)
|
||||||
|
|
@ -1502,13 +1513,13 @@ func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore,
|
||||||
return evaluateCorrectScore(outcome, finalScore)
|
return evaluateCorrectScore(outcome, finalScore)
|
||||||
case int64(domain.FOOTBALL_HALF_TIME_RESULT):
|
case int64(domain.FOOTBALL_HALF_TIME_RESULT):
|
||||||
return evaluateHalfTimeResult(outcome, firstHalfScore)
|
return evaluateHalfTimeResult(outcome, firstHalfScore)
|
||||||
case int64(domain.FOOTBALL_ASIAN_HANDICAP):
|
case int64(domain.FOOTBALL_ASIAN_HANDICAP), int64(domain.FOOTBALL_ALTERNATIVE_ASIAN_HANDICAP):
|
||||||
return evaluateAsianHandicap(outcome, finalScore)
|
return evaluateAsianHandicap(outcome, finalScore)
|
||||||
case int64(domain.FOOTBALL_GOAL_LINE):
|
case int64(domain.FOOTBALL_GOAL_LINE), int64(domain.FOOTBALL_ALTERNATIVE_GOAL_LINE):
|
||||||
return evaluateGoalLine(outcome, finalScore)
|
return evaluateGoalLine(outcome, finalScore)
|
||||||
case int64(domain.FOOTBALL_FIRST_HALF_ASIAN_HANDICAP):
|
case int64(domain.FOOTBALL_FIRST_HALF_ASIAN_HANDICAP), int64(domain.FOOTBALL_ALTERNATE_FIRST_HALF_ASIAN_HANDICAP):
|
||||||
return evaluateAsianHandicap(outcome, firstHalfScore)
|
return evaluateAsianHandicap(outcome, firstHalfScore)
|
||||||
case int64(domain.FOOTBALL_FIRST_HALF_GOAL_LINE):
|
case int64(domain.FOOTBALL_FIRST_HALF_GOAL_LINE), int64(domain.FOOTBALL_ALTERNATE_FIRST_HALF_GOAL_LINE):
|
||||||
return evaluateGoalLine(outcome, firstHalfScore)
|
return evaluateGoalLine(outcome, firstHalfScore)
|
||||||
case int64(domain.FOOTBALL_FIRST_TEAM_TO_SCORE):
|
case int64(domain.FOOTBALL_FIRST_TEAM_TO_SCORE):
|
||||||
return evaluateFirstTeamToScore(outcome, events)
|
return evaluateFirstTeamToScore(outcome, events)
|
||||||
|
|
@ -1518,20 +1529,128 @@ func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore,
|
||||||
return evaluateDoubleChance(outcome, finalScore)
|
return evaluateDoubleChance(outcome, finalScore)
|
||||||
case int64(domain.FOOTBALL_DRAW_NO_BET):
|
case int64(domain.FOOTBALL_DRAW_NO_BET):
|
||||||
return evaluateDrawNoBet(outcome, finalScore)
|
return evaluateDrawNoBet(outcome, finalScore)
|
||||||
case int64(domain.FOOTBALL_CORNERS):
|
case int64(domain.FOOTBALL_CORNERS), int64(domain.FOOTBALL_CORNERS_TWO_WAY), int64(domain.FOOTBALL_ASIAN_TOTAL_CORNERS), int64(domain.FOOTBALL_ALTERNATIVE_CORNER):
|
||||||
return evaluateCorners(outcome, corners)
|
return evaluateCorners(outcome, corners)
|
||||||
case int64(domain.FOOTBALL_CORNERS_TWO_WAY):
|
case int64(domain.FOOTBALL_FIRST_HALF_CORNERS), int64(domain.FOOTBALL_FIRST_HALF_ASIAN_CORNERS):
|
||||||
return evaluateCorners(outcome, corners)
|
|
||||||
case int64(domain.FOOTBALL_FIRST_HALF_CORNERS):
|
|
||||||
return evaluateCorners(outcome, halfTimeCorners)
|
|
||||||
case int64(domain.FOOTBALL_ASIAN_TOTAL_CORNERS):
|
|
||||||
return evaluateCorners(outcome, corners)
|
|
||||||
case int64(domain.FOOTBALL_FIRST_HALF_ASIAN_CORNERS):
|
|
||||||
return evaluateCorners(outcome, halfTimeCorners)
|
return evaluateCorners(outcome, halfTimeCorners)
|
||||||
case int64(domain.FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN):
|
case int64(domain.FOOTBALL_FIRST_HALF_GOALS_ODD_EVEN):
|
||||||
return evaluateGoalsOddEven(outcome, firstHalfScore)
|
return evaluateGoalsOddEven(outcome, firstHalfScore)
|
||||||
case int64(domain.FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN):
|
case int64(domain.FOOTBALL_SECOND_HALF_GOALS_ODD_EVEN):
|
||||||
return evaluateGoalsOddEven(outcome, secondHalfScore)
|
return evaluateGoalsOddEven(outcome, secondHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_BOTH_TEAMS_TO_SCORE):
|
||||||
|
return evaluateBothTeamsToScore(outcome, finalScore)
|
||||||
|
case int64(domain.FOOTBALL_RESULT_BOTH_TEAMS_TO_SCORE):
|
||||||
|
return evaluateResultAndBTTS(outcome, finalScore)
|
||||||
|
case int64(domain.FOOTBALL_HALF_TIME_CORRECT_SCORE):
|
||||||
|
return evaluateCorrectScore(outcome, firstHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF):
|
||||||
|
return evaluateBothTeamsToScore(outcome, firstHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_BOTH_TEAMS_TO_SCORE_SECOND_HALF):
|
||||||
|
return evaluateBothTeamsToScore(outcome, secondHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_SECOND_HALF_RESULT):
|
||||||
|
return evaluateFullTimeResult(outcome, secondHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_CLEAN_SHEET):
|
||||||
|
return evaluateCleanSheet(outcome, finalScore)
|
||||||
|
case int64(domain.FOOTBALL_LAST_TEAM_TO_SCORE):
|
||||||
|
return evaluateLastTeamToScore(outcome, events)
|
||||||
|
case int64(domain.FOOTBALL_WINNING_MARGIN):
|
||||||
|
return evaluateFootballWinningMargin(outcome, finalScore)
|
||||||
|
case int64(domain.FOOTBALL_BOTH_TEAMS_TO_RECEIVE_CARDS):
|
||||||
|
return evaluateBothTeamsToReceiveCards(outcome, yellowCards, redCards)
|
||||||
|
case int64(domain.FOOTBALL_HALF_TIME_DOUBLE_CHANCE):
|
||||||
|
return evaluateDoubleChance(outcome, firstHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_HALF_TIME_RESULT_BOTH_TEAMS_TO_SCORE):
|
||||||
|
return evaluateResultAndBTTS(outcome, firstHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_HALF_WITH_MOST_GOALS):
|
||||||
|
return evaluateHalfWithMostGoals(outcome, firstHalfScore, secondHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_HOME_TEAM_WITH_HIGHEST_SCORING_HALF):
|
||||||
|
return evaluateTeamHighestScoringHalf(outcome, firstHalfScore, secondHalfScore, "home")
|
||||||
|
case int64(domain.FOOTBALL_AWAY_TEAM_WITH_HIGHEST_SCORING_HALF):
|
||||||
|
return evaluateTeamHighestScoringHalf(outcome, firstHalfScore, secondHalfScore, "away")
|
||||||
|
case int64(domain.FOOTBALL_SECOND_HALF_GOALS):
|
||||||
|
return evaluateGoalsOverUnder(outcome, secondHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_TEAM_TOTAL_GOALS):
|
||||||
|
return evaluateTeamTotalGoals(outcome, finalScore)
|
||||||
|
case int64(domain.FOOTBALL_EXACT_TOTAL_GOALS):
|
||||||
|
return evaluateExactTotalGoals(outcome, finalScore)
|
||||||
|
case int64(domain.FOOTBALL_EXACT_FIRST_HALF_GOALS):
|
||||||
|
return evaluateExactTotalGoals(outcome, firstHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_TEAMS_TO_SCORE):
|
||||||
|
return evaluateTeamsToScore(outcome, finalScore)
|
||||||
|
case int64(domain.FOOTBALL_EXACT_SECOND_HALF_GOALS):
|
||||||
|
return evaluateExactTotalGoals(outcome, secondHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_FIRST_MATCH_CORNER):
|
||||||
|
return evaluateFirstMatchCorner(outcome, events)
|
||||||
|
case int64(domain.FOOTBALL_LAST_MATCH_CORNER):
|
||||||
|
return evaluateLastMatchCorner(outcome, events)
|
||||||
|
case int64(domain.FOOTBALL_CORNER_MATCH_BET):
|
||||||
|
return evaluateCornerMatchBet(outcome, corners)
|
||||||
|
case int64(domain.FOOTBALL_MULTI_CORNERS):
|
||||||
|
return evaluateMultiCorners(outcome, corners, halfTimeCorners)
|
||||||
|
case int64(domain.FOOTBALL_MATCH_SHOTS_ON_TARGET):
|
||||||
|
return evaluateMatchShotsOnTarget(outcome, onTarget)
|
||||||
|
case int64(domain.FOOTBALL_TEAM_SHOTS_ON_TARGET):
|
||||||
|
return evaluateTeamShotsOnTarget(outcome, onTarget)
|
||||||
|
case int64(domain.FOOTBALL_SPECIALS):
|
||||||
|
return evaluateSpecials(outcome, finalScore, firstHalfScore, secondHalfScore)
|
||||||
|
case int64(domain.FOOTBALL_ASIAN_HANDICAP_CORNERS), int64(domain.FOOTBALL_CORNER_HANDICAP):
|
||||||
|
return evaluateAsianHandicap(outcome, corners) // Re-use AsianHandicap logic with corner data
|
||||||
|
case int64(domain.FOOTBALL_ASIAN_TOTAL_CARDS), int64(domain.FOOTBALL_NUMBER_OF_CARDS_IN_MATCH):
|
||||||
|
// Assuming 1 point for a yellow card and 2 for a red card.
|
||||||
|
totalCards := yellowCards.Home + yellowCards.Away + redCards.Home + redCards.Away
|
||||||
|
cardScore := struct{ Home, Away int }{totalCards, 0}
|
||||||
|
return evaluateGoalLine(outcome, cardScore) // Re-use GoalLine logic
|
||||||
|
case int64(domain.FOOTBALL_TIME_OF_FIRST_GOAL_BRACKETS), int64(domain.FOOTBALL_EARLY_GOAL), int64(domain.FOOTBALL_LATE_GOAL):
|
||||||
|
return evaluateTimeOfFirstGoal(outcome, events)
|
||||||
|
|
||||||
|
// --- Unimplemented Markets ---
|
||||||
|
// Reason: The logic for these markets often requires specific data points that are not available
|
||||||
|
// or not clearly structured in the provided result JSON and BetOutcome data structure.
|
||||||
|
case int64(domain.FOOTBALL_MATCH_GOAL_RANGE),
|
||||||
|
int64(domain.FOOTBALL_TEAM_GOAL_RANGE),
|
||||||
|
int64(domain.FOOTBALL_FIRST_HALF_GOAL_RANGE),
|
||||||
|
int64(domain.FOOTBALL_SECOND_HALF_GOAL_RANGE),
|
||||||
|
int64(domain.FOOTBALL_RESULT_GOAL_RANGE),
|
||||||
|
int64(domain.FOOTBALL_DOUBLE_CHANCE_GOAL_RANGE):
|
||||||
|
// Unimplemented: The logic requires a goal range (e.g., "1-2", "3-4"), often specified in odds data as 'ED'.
|
||||||
|
// This range data is not available as a field in the domain.BetOutcome struct provided to this function.
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: data for range (ED) not in BetOutcome")
|
||||||
|
case int64(domain.FOOTBALL_FIRST_HALF_HANDICAP),
|
||||||
|
int64(domain.FOOTBALL_ALTERNATE_FIRST_HALF_HANDICAP),
|
||||||
|
int64(domain.FOOTBALL_ALTERNATIVE_HANDICAP_RESULT),
|
||||||
|
int64(domain.FOOTBALL_HANDICAP_RESULT):
|
||||||
|
// Unimplemented: Standard handicap markets (3-way) require specific logic to handle the "Tie" outcome based on the handicap,
|
||||||
|
// which differs from Asian Handicap. This logic needs to be built out.
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: 3-way handicap logic is not yet built")
|
||||||
|
case int64(domain.FOOTBALL_TO_SCORE_IN_HALF),
|
||||||
|
int64(domain.FOOTBALL_BOTH_TEAMS_TO_SCORE_FIRST_HALF_SECOND_HALF):
|
||||||
|
// Unimplemented: The BetOutcome struct does not clearly distinguish which half the bet applies to for this market type.
|
||||||
|
// A field indicating "1st Half", "2nd Half", or "Yes/No" for each half is needed.
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: BetOutcome struct lacks required fields for half-specific bets")
|
||||||
|
case int64(domain.FOOTBALL_TEN_MINUTE_RESULT),
|
||||||
|
int64(domain.FOOTBALL_FIRST_TEN_MINUTE),
|
||||||
|
int64(domain.FOOTBALL_TIME_OF_FIRST_TEAM_GOAL),
|
||||||
|
int64(domain.FOOTBALL_TIME_OF_FIRST_CARD),
|
||||||
|
int64(domain.FOOTBALL_TIME_OF_FIRST_CORNER):
|
||||||
|
// Unimplemented: Requires parsing event timestamps and comparing them to specific minute markers.
|
||||||
|
// While event data is available, the specific logic for each of these time-based markets needs to be implemented.
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: requires detailed time-based event parsing logic")
|
||||||
|
case int64(domain.FOOTBALL_FIRST_GOAL_METHOD),
|
||||||
|
int64(domain.FOOTBALL_OWN_GOAL),
|
||||||
|
int64(domain.FOOTBALL_TO_MISS_PENALTY):
|
||||||
|
// Unimplemented: The event result data does not specify the method of the goal (e.g., Shot, Header, Penalty, Own Goal)
|
||||||
|
// or provide details about penalties that were missed.
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: result data lacks goal method or missed penalty info")
|
||||||
|
case int64(domain.FOOTBALL_GOALSCORER),
|
||||||
|
int64(domain.FOOTBALL_MULTI_SCORERS),
|
||||||
|
int64(domain.FOOTBALL_PLAYER_TO_SCORE_ASSIST),
|
||||||
|
int64(domain.FOOTBALL_PLAYER_CARD),
|
||||||
|
int64(domain.FOOTBALL_PLAYER_SHOTS_ON_TARGET),
|
||||||
|
int64(domain.FOOTBALL_PLAYER_SHOTS),
|
||||||
|
int64(domain.FOOTBALL_TEAM_GOALSCORER):
|
||||||
|
// Unimplemented: All player-specific markets require data mapping player names to actions (goals, cards, shots).
|
||||||
|
// The provided event result data is anonymous (e.g., "Goal - (Temperley)") and does not contain this information.
|
||||||
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("unimplemented market: requires player-specific data not present in results")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
s.mongoLogger.Warn(
|
s.mongoLogger.Warn(
|
||||||
|
|
@ -1540,7 +1659,7 @@ func (s *Service) evaluateFootballOutcome(outcome domain.BetOutcome, finalScore,
|
||||||
zap.Int64("market_id", outcome.MarketID),
|
zap.Int64("market_id", outcome.MarketID),
|
||||||
zap.Int64("outcome_id", outcome.ID),
|
zap.Int64("outcome_id", outcome.ID),
|
||||||
)
|
)
|
||||||
return domain.OUTCOME_STATUS_PENDING, fmt.Errorf("market type not implemented: %s", outcome.MarketName)
|
return domain.OUTCOME_STATUS_ERROR, fmt.Errorf("market type not implemented: %s", outcome.MarketName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue
Block a user