800 lines
18 KiB
Go
800 lines
18 KiB
Go
|
|
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
|
|||
|
|
}
|