package daemon import ( "fmt" "gordenko.dev/dima/flashscore" "gordenko.dev/dima/onexbet" "gordenko.dev/dima/tipper/model" ) func FootballHistoryProcessor(match *TeamMatch) { } /* Алгоритм 2 ТБ 0.5 в Матче До матча: ТБ 2.5 (в матче) <=1.7 В 75% игр был гол В Лайве: Сумма атак обычных и опасных >= 135 6 ударов в сторону ворот OFF TARGET 4 удара в створ ON TARGET До 70 минуты */ type FootballOver05 struct { id int64 notifyInTelegram bool isChampAccepted func(string) bool } func NewFootballOver05(opt StrategyOptions) FootballOver05 { if opt.ID == 0 { panic("StrategyID not defined") } s := FootballOver05{} s.id = opt.ID s.notifyInTelegram = opt.NotifyInTelegram s.isChampAccepted = opt.IsChampAccepted return s } func (s FootballOver05) ID() int64 { return s.id } func (s FootballOver05) ShortName() string { return fmt.Sprintf("#%d FT Over 0.5 M", s.id) } func (s FootballOver05) IsNotifyInTelegram() bool { return s.notifyInTelegram } func (s FootballOver05) GetTelegramTipPattern() string { return `Сигнал # %d. Будет ГОЛ! Алгоритм %d Тотал Больше 0.5 Футбол. %s. %s %s - %s Коэф. %.3f` } func (s FootballOver05) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool { return true } type FootballOver05Notes struct { PriceOver25 float64 `json:"Over 2.5 price"` MatchesAnalysed int `json:"matchesAnalysed"` HasGoalInMatches int `json:"hasGoalInMatches"` GoalsPercent float64 `json:"goalsPercent"` ZeroDrawsInH2H int `json:"zeroDrawsInH2H"` ZeroDrawsPercentInH2H float64 `json:"zeroDrawsPercentInH2H"` } func (s FootballOver05) IsInteresting(report *TeamMatch) (notes interface{}, _ bool) { var ( priceOver25 float64 foundPriceOver25 bool //f flashscore.TotalOffer ) for _, total := range report.Odds.FullTimeTotal { if total.Total != "2.5" { continue } // 2.5 for _, offer := range total.Offers { if offer.Bookmaker == flashscore.Bookmaker1xBet { priceOver25 = offer.Over foundPriceOver25 = true break } } if foundPriceOver25 { break } else { // 1xBet не нашли // Берем первый коэф. if len(total.Offers) > 0 { priceOver25 = total.Offers[0].Over foundPriceOver25 = true } break } } if priceOver25 > 1.7 { return } var ( matchCount int hasGoalsInMatchCount int zeroDrawsInH2HCount int zeroDrawsPercent float64 ) for _, match := range report.HomeTeamMatches { if match.HomeGoals > 0 || match.AwayGoals > 0 { hasGoalsInMatchCount++ } matchCount++ } for _, match := range report.AwayTeamMatches { if match.HomeGoals > 0 || match.AwayGoals > 0 { hasGoalsInMatchCount++ } matchCount++ } for _, match := range report.H2H { if match.HomeGoals > 0 || match.AwayGoals > 0 { hasGoalsInMatchCount++ } else { zeroDrawsInH2HCount++ } matchCount++ } if matchCount == 0 { return } // h2hMatchCount - 100% // zeroDrawsInH2HCount - x% if zeroDrawsInH2HCount > 0 { // Не более 2 сухих ничьи if zeroDrawsInH2HCount > 2 { return } zeroDrawsPercent = float64(len(report.H2H)*100) / float64(zeroDrawsInH2HCount) // Не более 20% сухих ничьих if zeroDrawsPercent > 20 { return } } // matchCount - 100% // goalsInMatchCount - x% goalsPercent := float64(hasGoalsInMatchCount*100) / float64(matchCount) if goalsPercent < 80 { return } obj := FootballOver05Notes{ PriceOver25: priceOver25, MatchesAnalysed: matchCount, HasGoalInMatches: hasGoalsInMatchCount, GoalsPercent: goalsPercent, ZeroDrawsInH2H: zeroDrawsInH2HCount, ZeroDrawsPercentInH2H: zeroDrawsPercent, } //buf, _ := json.MarshalIndent(obj, "", " ") return obj, true } func (s FootballOver05) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (_ string, _ *BetDetails, _ TipCode) { if upd.CurrentTime == 0 { // ВАЖНО! // Матч еще не начался. Избегаем деления на 0 при вычислении attacksRatio return "матч не начался", nil, Waiting } // Прогнозы даем до 70 минуты включительно. maxTime := 70 * 60 if upd.CurrentTime > maxTime { return "70 минут уже отыграли", nil, Unwatch } if upd.HomeGoals > 0 || upd.AwayGoals > 0 { // Если гол уже забили - выходим return "гол уже забили", nil, Unwatch } // Ищем тотал Больше 0.5 // Минимальный курс minPrice := 1.5 var ( minPriceCheckPassed bool currentPrice float64 ) for _, over := range upd.Total.Over { if over.Param == "0.5" { if over.Price < minPrice { return fmt.Sprintf("коэф. %.2f < %.2f", over.Price, minPrice), nil, Waiting } currentPrice = over.Price minPriceCheckPassed = true break } } if !minPriceCheckPassed { return "тотал 0.5 не найден", nil, Waiting } // attacks := upd.HomeAttacks + upd.AwayAttacks + upd.HomeDangerousAttacks + upd.AwayDangerousAttacks shotsOffTarget := upd.HomeShotsOffTarget + upd.AwayShotsOffTarget shotsOnTarget := upd.HomeShotsOnTarget + upd.AwayShotsOnTarget if attacks < 135 { return fmt.Sprintf("%d атак < 135", attacks), nil, Waiting } if shotsOffTarget < 6 { return fmt.Sprintf("%d shotsOffTarget < 6", shotsOffTarget), nil, Waiting } if shotsOnTarget < 4 { return fmt.Sprintf("%d shotsOnTarget < 4", shotsOnTarget), nil, Waiting } return "", &BetDetails{ Market: MarketTotal, Side: SideOver, Param: "0.5", Price: currentPrice, }, Bet } func (s FootballOver05) GetResult(tip model.Tip, stats onexbet.TeamMatchResult) (_ *TipResult) { //buf, _ := json.MarshalIndent(stats, "", " ") //fmt.Printf("%s\n", buf) if stats.Status != onexbet.StatusMatchCompleted { return } res := new(TipResult) goals := stats.HomePoints + stats.AwayPoints if goals > 0 { res.Status = model.Won } else { res.Status = model.Lost } res.Result = fmt.Sprintf("счет: %d-%d", stats.HomePoints, stats.AwayPoints) return res } ////////// P1 OVER 0.5 type FootballP1Over05 struct { id int64 notifyInTelegram bool isChampAccepted func(string) bool } func NewFootballP1Over05(opt StrategyOptions) FootballP1Over05 { if opt.ID == 0 { panic("StrategyID not defined") } s := FootballP1Over05{} s.id = opt.ID s.notifyInTelegram = opt.NotifyInTelegram s.isChampAccepted = opt.IsChampAccepted return s } func (s FootballP1Over05) ID() int64 { return s.id } func (s FootballP1Over05) ShortName() string { return fmt.Sprintf("#%d H1 Over 0.5 M", s.id) } func (s FootballP1Over05) IsNotifyInTelegram() bool { return s.notifyInTelegram } func (s FootballP1Over05) GetTelegramTipPattern() string { return `Сигнал # %d. Будет ГОЛ! Алгоритм %d Первый тайм, Тотал Больше 0.5 Футбол. %s. %s %s - %s Коэф. %.3f` } func (s FootballP1Over05) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool { if liveMatch.CurrentPeriod == 1 { return true } return false } type isInterestingNotesFootballP1Over05 struct { PriceOver25 float64 `json:"Over 2.5 price"` MatchesAnalysed int `json:"matchesAnalysed"` HasGoalInP1Matches int `json:"hasGoalInP1Matches"` P1GoalsPercent float64 `json:"p1GoalsPercent"` HasGoalInP1H2HMatches int `json:"hasGoalInP1H2HMatches"` P1H2HGoalsPercent float64 `json:"p1H2HGoalsPercent"` } func (s FootballP1Over05) IsInteresting(report *TeamMatch) (notes interface{}, _ bool) { var ( priceOver25 float64 foundPriceOver25 bool ) for _, total := range report.Odds.FullTimeTotal { if total.Total != "2.5" { continue } // 2.5 for _, offer := range total.Offers { if offer.Bookmaker == flashscore.Bookmaker1xBet { priceOver25 = offer.Over foundPriceOver25 = true break } } if foundPriceOver25 { break } else { // 1xBet не нашли // Берем первый коэф. if len(total.Offers) > 0 { priceOver25 = total.Offers[0].Over foundPriceOver25 = true } break } } if priceOver25 > 1.58 { return } var ( hasStatsMatchCount int hasGoalsInP1MatchCount int hasH2HStatsMatchCount int hasGoalsInP1H2HMatchCount int p1H2HGoalsPercent float64 ) for _, match := range report.HomeTeamMatches { if match.HasScoreByPeriods { hasStatsMatchCount++ if match.P1HomeGoals > 0 || match.P1AwayGoals > 0 { hasGoalsInP1MatchCount++ } } } for _, match := range report.AwayTeamMatches { if match.HasScoreByPeriods { hasStatsMatchCount++ if match.P1HomeGoals > 0 || match.P1AwayGoals > 0 { hasGoalsInP1MatchCount++ } } } for _, match := range report.H2H { if match.HasScoreByPeriods { hasH2HStatsMatchCount++ if match.P1HomeGoals > 0 || match.P1AwayGoals > 0 { hasGoalsInP1H2HMatchCount++ } } } if hasStatsMatchCount == 0 { return } // hasStatsMatchCount - 100% // hasGoalsInP1MatchCount - x% p1GoalsPercent := float64(hasGoalsInP1MatchCount*100) / float64(hasStatsMatchCount) if p1GoalsPercent < 75 { return } if hasH2HStatsMatchCount > 0 { // hasH2HStatsMatchCount - 100% // hasGoalsInP1H2HMatchCount - x% p1H2HGoalsPercent = float64(hasGoalsInP1H2HMatchCount*100) / float64(hasH2HStatsMatchCount) if p1H2HGoalsPercent < 80 { return } } obj := isInterestingNotesFootballP1Over05{ PriceOver25: priceOver25, MatchesAnalysed: hasStatsMatchCount, HasGoalInP1Matches: hasGoalsInP1MatchCount, P1GoalsPercent: p1GoalsPercent, HasGoalInP1H2HMatches: hasGoalsInP1H2HMatchCount, P1H2HGoalsPercent: p1H2HGoalsPercent, } //buf, _ := json.MarshalIndent(obj, "", " ") return obj, true } func (s FootballP1Over05) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (_ string, _ *BetDetails, _ TipCode) { if upd.CurrentTime == 0 { // ВАЖНО! // Матч еще не начался. Избегаем деления на 0 при вычислении attacksRatio return "матч не начался", nil, Waiting } // Прогнозы даем до 20 минуты включительно. maxTime := 20 * 60 if upd.CurrentTime > maxTime { return "20 минут уже отыграли", nil, Unwatch } if upd.HomeGoals > 0 || upd.AwayGoals > 0 { // Если гол уже забили - выходим return "гол уже забили", nil, Unwatch } // Ищем тотал Больше 0.5 // Минимальный курс minPrice := 1.5 var ( minPriceCheckPassed bool currentPrice float64 ) for _, over := range upd.H1.Total.Over { if over.Param == "0.5" { if over.Price < minPrice { return fmt.Sprintf("коэф. %.2f < %.2f", over.Price, minPrice), nil, Waiting } currentPrice = over.Price minPriceCheckPassed = true break } } if !minPriceCheckPassed { return "тотал 0.5 не найден", nil, Waiting } // соотношение атак по времени >= 2.1 (за 10 минут от 21 атаки) attacks := upd.HomeAttacks + upd.AwayAttacks shotsOffTarget := upd.HomeShotsOffTarget + upd.AwayShotsOffTarget attacksRatio := float64(attacks*60) / float64(upd.CurrentTime) if attacksRatio < 2.1 { return fmt.Sprintf("отношение атак ко времени %.2f < 2.1", attacksRatio), nil, Waiting } if shotsOffTarget < 3 { return fmt.Sprintf("%d shotsOffTarget < 6", shotsOffTarget), nil, Waiting } return "", &BetDetails{ Market: MarketTotalH1, Side: SideOver, Param: "0.5", Price: currentPrice, }, Bet } func (s FootballP1Over05) GetResult(tip model.Tip, stats onexbet.TeamMatchResult) (_ *TipResult) { //buf, _ := json.MarshalIndent(stats, "", " ") //fmt.Printf("%s\n", buf) h1, ok := stats.ScoreByPeriods[1] if !ok { return } _, ok = stats.ScoreByPeriods[2] if !ok { return } res := new(TipResult) h1TotalGoals := h1.HomePoints + h1.AwayPoints if h1TotalGoals > 0 { res.Status = model.Won } else { res.Status = model.Lost } res.Result = fmt.Sprintf("1й тайм: %d-%d", h1.HomePoints, h1.AwayPoints) return res } //////////////// OVER 1.5 type FootballOver15 struct { id int64 notifyInTelegram bool isChampAccepted func(string) bool } func NewFootballOver15(opt StrategyOptions) FootballOver15 { if opt.ID == 0 { panic("StrategyID not defined") } s := FootballOver15{} s.id = opt.ID s.notifyInTelegram = opt.NotifyInTelegram s.isChampAccepted = opt.IsChampAccepted return s } func (s FootballOver15) ID() int64 { return s.id } func (s FootballOver15) ShortName() string { return fmt.Sprintf("#%d FT Over 1.5 M", s.id) } func (s FootballOver15) IsNotifyInTelegram() bool { return s.notifyInTelegram } func (s FootballOver15) GetTelegramTipPattern() string { return `Сигнал # %d. Будет 2й ГОЛ! Алгоритм %d Тотал Больше 1.5 Футбол. %s. %s %s - %s Коэф. %.3f` } func (s FootballOver15) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool { return true } type isInterestingNotesFootballOver15 struct { PriceOver25 float64 `json:"Over 2.5 price"` MatchesAnalysed int `json:"matchesAnalysed"` HasGoalInMatches int `json:"hasGoalInMatches"` GoalsPercent float64 `json:"goalsPercent"` FavoritePrice float64 `json:"favoritePrice"` Favorite string `json:"favorite"` } func (s FootballOver15) IsInteresting(report *TeamMatch) (notes interface{}, _ bool) { var favoritePrice float64 = 2 var favorite string // Проходим по всем ценам, каждый раз обновляя минимальную for _, offer := range report.Odds.Winner { if offer.Home < favoritePrice && offer.Home > 1.01 { favoritePrice = offer.Home favorite = "home" } if offer.Away < favoritePrice && offer.Away > 1.01 { favoritePrice = offer.Away favorite = "away" } } if favoritePrice > 1.4 { return } var ( priceOver25 float64 foundPriceOver25 bool ) for _, total := range report.Odds.FullTimeTotal { if total.Total != "2.5" { continue } // 2.5 for _, offer := range total.Offers { if offer.Bookmaker == flashscore.Bookmaker1xBet { priceOver25 = offer.Over foundPriceOver25 = true break } } if foundPriceOver25 { break } else { // 1xBet не нашли // Берем первый коэф. if len(total.Offers) > 0 { priceOver25 = total.Offers[0].Over foundPriceOver25 = true } break } } if priceOver25 > 1.7 { return } var ( matchCount int hasGoalsInMatchCount int ) for _, match := range report.HomeTeamMatches { if match.HomeGoals > 0 || match.AwayGoals > 0 { hasGoalsInMatchCount++ } matchCount++ } for _, match := range report.AwayTeamMatches { if match.HomeGoals > 0 || match.AwayGoals > 0 { hasGoalsInMatchCount++ } matchCount++ } for _, match := range report.H2H { if match.HomeGoals > 0 || match.AwayGoals > 0 { hasGoalsInMatchCount++ } matchCount++ } if matchCount == 0 { return } // matchCount - 100% // goalsInMatchCount - x% goalsPercent := float64(hasGoalsInMatchCount*100) / float64(matchCount) if goalsPercent < 75 { return } obj := isInterestingNotesFootballOver15{ PriceOver25: priceOver25, MatchesAnalysed: matchCount, HasGoalInMatches: hasGoalsInMatchCount, GoalsPercent: goalsPercent, FavoritePrice: favoritePrice, Favorite: favorite, } report.Favorite = favorite //buf, _ := json.MarshalIndent(obj, "", " ") return obj, true } func (s FootballOver15) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (_ string, _ *BetDetails, _ TipCode) { if upd.CurrentTime == 0 { // ВАЖНО! // Матч еще не начался. Избегаем деления на 0 при вычислении attacksRatio return "матч не начался", nil, Waiting } // Прогнозы даем до 70 минуты включительно. maxTime := 70 * 60 if upd.CurrentTime > maxTime { return "70 минут уже отыграли", nil, Unwatch } goals := upd.HomeGoals + upd.AwayGoals if goals > 1 { // Если 2 гола уже забили - выходим return "забили более 1 гола", nil, Unwatch } if goals == 0 { return "счет 0-0", nil, Waiting } if (upd.HomeGoals == 1 && match.Favorite == "away") || (upd.AwayGoals == 1 && match.Favorite == "home") { // все ок, фаворит уступает - анализируем дальше } else { // забил фаворит - далее неинтересно return "забил фаворит", nil, Unwatch } // Ищем тотал Больше 1.5 // Минимальный курс minPrice := 1.5 var ( minPriceCheckPassed bool currentPrice float64 ) for _, over := range upd.Total.Over { if over.Param == "1.5" { if over.Price < minPrice { comment := fmt.Sprintf("коэф. %.2f < %.2f", over.Price, minPrice) return comment, nil, Waiting } currentPrice = over.Price minPriceCheckPassed = true break } } if !minPriceCheckPassed { return "тотал 1.5 не найден", nil, Waiting } // attacks := upd.HomeAttacks + upd.AwayAttacks + upd.HomeDangerousAttacks + upd.AwayDangerousAttacks shotsOffTarget := upd.HomeShotsOffTarget + upd.AwayShotsOffTarget shotsOnTarget := upd.HomeShotsOnTarget + upd.AwayShotsOnTarget if attacks < 125 { return fmt.Sprintf("%d атак < 125", attacks), nil, Waiting } if shotsOffTarget < 6 { return fmt.Sprintf("%d shotsOffTarget < 6", shotsOffTarget), nil, Waiting } if shotsOnTarget < 4 { return fmt.Sprintf("%d shotsOnTarget < 4", shotsOnTarget), nil, Waiting } return "", &BetDetails{ Market: MarketTotal, Side: SideOver, Param: "1.5", Price: currentPrice, }, Bet } func (s FootballOver15) GetResult(tip model.Tip, stats onexbet.TeamMatchResult) (_ *TipResult) { //buf, _ := json.MarshalIndent(stats, "", " ") //fmt.Printf("%s\n", buf) if stats.Status != onexbet.StatusMatchCompleted { return } res := new(TipResult) goals := stats.HomePoints + stats.AwayPoints if goals > 1 { res.Status = model.Won } else { res.Status = model.Lost } res.Result = fmt.Sprintf("счет: %d-%d", stats.HomePoints, stats.AwayPoints) return res }