1126 lines
28 KiB
Go
1126 lines
28 KiB
Go
|
|
package daemon
|
|||
|
|
|
|||
|
|
import (
|
|||
|
|
"fmt"
|
|||
|
|
"math"
|
|||
|
|
"sort"
|
|||
|
|
"strconv"
|
|||
|
|
"strings"
|
|||
|
|
|
|||
|
|
"gordenko.dev/dima/flashscore"
|
|||
|
|
"gordenko.dev/dima/onexbet"
|
|||
|
|
"gordenko.dev/dima/tipper/model"
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
/*
|
|||
|
|
type TennisMatch struct {
|
|||
|
|
flashscoreMatchID string
|
|||
|
|
totalBreakpoints int
|
|||
|
|
lastPrice float64
|
|||
|
|
last3rdSetTotal int
|
|||
|
|
closeCh chan struct{}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewTennisMatch() *TennisMatch {
|
|||
|
|
s := new(TennisMatch)
|
|||
|
|
s.closeCh = make(chan struct{})
|
|||
|
|
return s
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *TennisMatch) checker() {
|
|||
|
|
ticker := time.NewTicker(7 * time.Second)
|
|||
|
|
|
|||
|
|
for {
|
|||
|
|
select {
|
|||
|
|
case <-ticker.C:
|
|||
|
|
s.loadMatchStat()
|
|||
|
|
|
|||
|
|
case <-s.closeCh:
|
|||
|
|
ticker.Stop()
|
|||
|
|
// drain channel?
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *TennisMatch) loadMatchStat() {
|
|||
|
|
stat, err := flashscore.LoadTennisMatchStat(s.flashscoreMatchID)
|
|||
|
|
if err != nil {
|
|||
|
|
// log
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if stat.HomeBreakpoints != nil && stat.AwayBreakpoints != nil {
|
|||
|
|
s.totalBreakpoints = stat.HomeBreakpoints.Qty + stat.AwayBreakpoints.Qty
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s.tryGetTip()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *TennisMatch) tryGetTip() {
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s *TennisMatch) Close() {
|
|||
|
|
s.closeCh <- struct{}{}
|
|||
|
|
}
|
|||
|
|
*/
|
|||
|
|
|
|||
|
|
// test strategy
|
|||
|
|
// ТМ в 3 сете
|
|||
|
|
// 1) Женщины все покрытия
|
|||
|
|
// 2) Мужчины (кроме быстрых покрытий) (Грунт!!!)
|
|||
|
|
|
|||
|
|
// Условия:
|
|||
|
|
// 1) 1 и 2 сет ТБ 10.5 геймов (7:5; 6:7) сумарно не мение 6 и более (брейк поинтов) майскор
|
|||
|
|
// 2) 3 сет при счете 1:1 смотрим 10.5 меньше но не позднее 2:2
|
|||
|
|
type Tennis3rdSetTotalUnder struct {
|
|||
|
|
id int64
|
|||
|
|
notifyInTelegram bool
|
|||
|
|
isChampAccepted func(string) bool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewTennis3rdSetTotalUnder(opt StrategyOptions) Tennis3rdSetTotalUnder {
|
|||
|
|
if opt.ID == 0 {
|
|||
|
|
panic("StrategyID not defined")
|
|||
|
|
}
|
|||
|
|
s := Tennis3rdSetTotalUnder{}
|
|||
|
|
s.id = opt.ID
|
|||
|
|
s.notifyInTelegram = opt.NotifyInTelegram
|
|||
|
|
s.isChampAccepted = opt.IsChampAccepted
|
|||
|
|
return s
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis3rdSetTotalUnder) ID() int64 {
|
|||
|
|
return s.id
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis3rdSetTotalUnder) ShortName() string {
|
|||
|
|
return fmt.Sprintf("#%d S3 Under 10.5", s.id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis3rdSetTotalUnder) IsNotifyInTelegram() bool {
|
|||
|
|
return s.notifyInTelegram
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis3rdSetTotalUnder) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool {
|
|||
|
|
if liveMatch.Score.Home == 1 && liveMatch.Score.Away == 1 {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis3rdSetTotalUnder) IsInteresting(_ *TeamMatch) (interface{}, bool) {
|
|||
|
|
return nil, true
|
|||
|
|
}
|
|||
|
|
func (s Tennis3rdSetTotalUnder) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (string, *BetDetails, TipCode) {
|
|||
|
|
if upd.CurrentPeriod < 3 {
|
|||
|
|
return "не 3й сет", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if upd.CurrentPeriod > 3 {
|
|||
|
|
return "3й сет завершен", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s1score, ok := upd.ScoreByPeriods[1]
|
|||
|
|
if !ok {
|
|||
|
|
// нет счета 1 сета
|
|||
|
|
return "счет 1го сета не найден", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s1total := s1score.Home + s1score.Away
|
|||
|
|
|
|||
|
|
if float64(s1total) < 11.5 {
|
|||
|
|
return fmt.Sprintf("тотал 1го сета %d", s1total), nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s2score, ok := upd.ScoreByPeriods[2]
|
|||
|
|
if !ok {
|
|||
|
|
// нет счета 2 сета
|
|||
|
|
return "счет 2го сета не найден", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s2total := s2score.Home + s2score.Away
|
|||
|
|
|
|||
|
|
if float64(s2total) < 11.5 {
|
|||
|
|
return fmt.Sprintf("тотал 2го сета %d", s2total), nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s3score, ok := upd.ScoreByPeriods[3]
|
|||
|
|
if !ok {
|
|||
|
|
// нет счета 3 сета
|
|||
|
|
return "счет 3го сета не найден", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if s3score.Home > 2 || s3score.Away > 2 {
|
|||
|
|
return "счет в 3м сете превысил 2:2", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if s3score.Home == 0 || s3score.Away == 0 {
|
|||
|
|
return "ждем счет в 3м сете: 1:1, 1:2, 2:1 или 2:2", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
sex := flashscore.ParseTennisSexFromChampName(match.FullChampName)
|
|||
|
|
if sex == "" {
|
|||
|
|
return "не удалось определить мужчины или женщины играют", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if sex == flashscore.SexMen {
|
|||
|
|
surface := flashscore.ParseTennisSurfaceFromChampName(match.FullChampName)
|
|||
|
|
if surface != flashscore.SurfaceClay {
|
|||
|
|
return fmt.Sprintf("слишком быстрое покрытие для мужиков: %s", surface), nil, Unwatch
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if match.TwoSetsBreakpoints == 0 {
|
|||
|
|
|
|||
|
|
matchStat, err := flashscore.LoadTennisMatchStat(match.MatchID)
|
|||
|
|
if err != nil {
|
|||
|
|
return fmt.Sprintf("не удалось загрузить статистику по брейкпоинтам: %s", err),
|
|||
|
|
nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var breakpoints int
|
|||
|
|
|
|||
|
|
// Запросить стату по брейкпоинтам с сайта Flashscore
|
|||
|
|
if matchStat.HomeBreakpoints != nil {
|
|||
|
|
breakpoints += matchStat.HomeBreakpoints.Qty
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if matchStat.AwayBreakpoints != nil {
|
|||
|
|
breakpoints += matchStat.AwayBreakpoints.Qty
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
match.TwoSetsBreakpoints = breakpoints
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if match.TwoSetsBreakpoints < 6 {
|
|||
|
|
return fmt.Sprintf("к-во брейкпоинтов < 6: %d", match.TwoSetsBreakpoints), nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Поиски тотала с коэф. >= 1.5
|
|||
|
|
set3, ok := upd.Periods[3]
|
|||
|
|
if !ok {
|
|||
|
|
return "1xbet не дает тотал 3го сета", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
under := set3.Total.Under
|
|||
|
|
|
|||
|
|
sort.Slice(under, func(i, j int) bool {
|
|||
|
|
if under[i].ParamFloat > under[j].ParamFloat {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
for _, offer := range under {
|
|||
|
|
if offer.ParamFloat > 10.4 {
|
|||
|
|
if offer.Price >= 1.5 {
|
|||
|
|
|
|||
|
|
telegramMessage := fmt.Sprintf(`Сигнал # %%d.
|
|||
|
|
Алгоритм %d. Тотал меньше в 3м сете.
|
|||
|
|
%s
|
|||
|
|
%s - %s
|
|||
|
|
|
|||
|
|
Точка входа %.1f кф. %.3f
|
|||
|
|
|
|||
|
|
Рекомендация: тотал меньше %.1f и выше (+%.1f; +%.1f)`,
|
|||
|
|
s.id,
|
|||
|
|
match.Champ.Name,
|
|||
|
|
match.Home.CanonicalName, match.Away.CanonicalName,
|
|||
|
|
offer.ParamFloat, offer.Price,
|
|||
|
|
offer.ParamFloat, offer.ParamFloat+1, offer.ParamFloat+2)
|
|||
|
|
|
|||
|
|
return "", &BetDetails{
|
|||
|
|
Market: MarketTotal,
|
|||
|
|
Side: SideUnder,
|
|||
|
|
Param: offer.Param,
|
|||
|
|
Price: offer.Price,
|
|||
|
|
TelegramMessage: telegramMessage,
|
|||
|
|
}, Bet
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return "не найден тотал 10.5 и выше с коэф. 1.5 и выше", nil, Waiting
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
func (s Tennis3rdSetTotalUnder) GetResult(tip model.Tip, result onexbet.TeamMatchResult) (_ *TipResult) {
|
|||
|
|
score, ok := result.ScoreByPeriods[3]
|
|||
|
|
if !ok {
|
|||
|
|
// Сперва проверяем что счет есть.
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверяем что сет завершен
|
|||
|
|
if !isTennisSetCompleted(score) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
total := float64(score.HomePoints + score.AwayPoints)
|
|||
|
|
|
|||
|
|
param, err := strconv.ParseFloat(tip.Param, 64)
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Printf("Parse tip param: %s; param=%s", err, tip.Param)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
resultText := fmt.Sprintf(`3й сет: %d-%d`, score.HomePoints, score.AwayPoints)
|
|||
|
|
|
|||
|
|
if isFloatEqual(param, total, 0.1) {
|
|||
|
|
// Совпасть могут только целые форы +3, +4
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Void,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if total < param {
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Won,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Lost,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis3rdSetTotalUnder) GetTelegramTipPattern() string {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
//////////////////////////////////
|
|||
|
|
|
|||
|
|
// Плюсовая фора на Аутсайдера.
|
|||
|
|
|
|||
|
|
// 1) Определяем Фаворита до лайва (коэф. <= 1.5)
|
|||
|
|
|
|||
|
|
// В лайве наблюдаем и ждем выполнения 2 условий:
|
|||
|
|
// 2) Фаворит выигрывает 1й сет с тоталом < 8.5
|
|||
|
|
// 3) В первом сете на фаворита давали фору -2.5 по ровной линии (коэф. от 1.75 до 1.95)
|
|||
|
|
|
|||
|
|
// Прогноз:
|
|||
|
|
// Если на фаворита в 1м сете давали фору -2.5 в ровной линии,
|
|||
|
|
// то входим +3.5 на аутсайдера во 2м сете. Если давали -3.5 то входим +4.5;
|
|||
|
|
// -4.5 то входим +5.5
|
|||
|
|
|
|||
|
|
// Коэф. входа от 1.5 до 2.5, текущий счет во 2м сете не имеет значения.
|
|||
|
|
|
|||
|
|
type Tennis2ndSetPlusHandicap struct {
|
|||
|
|
id int64
|
|||
|
|
notifyInTelegram bool
|
|||
|
|
isChampAccepted func(string) bool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewTennis2ndSetPlusHandicap(opt StrategyOptions) Tennis2ndSetPlusHandicap {
|
|||
|
|
if opt.ID == 0 {
|
|||
|
|
panic("StrategyID not defined")
|
|||
|
|
}
|
|||
|
|
s := Tennis2ndSetPlusHandicap{}
|
|||
|
|
s.id = opt.ID
|
|||
|
|
s.notifyInTelegram = opt.NotifyInTelegram
|
|||
|
|
s.isChampAccepted = opt.IsChampAccepted
|
|||
|
|
return s
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicap) ID() int64 {
|
|||
|
|
return s.id
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicap) ShortName() string {
|
|||
|
|
return fmt.Sprintf("#%d S2 Outsider Handicap", s.id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicap) IsNotifyInTelegram() bool {
|
|||
|
|
return s.notifyInTelegram
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicap) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool {
|
|||
|
|
if liveMatch.CurrentPeriod == 1 || liveMatch.CurrentPeriod == 2 {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicap) IsInteresting(match *TeamMatch) (interface{}, bool) {
|
|||
|
|
// Поиск фаворита
|
|||
|
|
for _, offer := range match.Odds.Winner2Way {
|
|||
|
|
if offer.Home <= 1.5 {
|
|||
|
|
match.FavoriteSide = FavoriteHome
|
|||
|
|
|
|||
|
|
return fmt.Sprintf("фаворит %s, коэф. %.2f",
|
|||
|
|
match.Home.CanonicalName, offer.Home), true
|
|||
|
|
} else if offer.Away <= 1.5 {
|
|||
|
|
match.FavoriteSide = FavoriteAway
|
|||
|
|
|
|||
|
|
return fmt.Sprintf("фаворит %s, коэф. %.2f",
|
|||
|
|
match.Away.CanonicalName, offer.Away), true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return nil, false
|
|||
|
|
}
|
|||
|
|
func (s Tennis2ndSetPlusHandicap) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (string, *BetDetails, TipCode) {
|
|||
|
|
if upd.CurrentPeriod == 1 {
|
|||
|
|
// поиски форы на 1й сет по ровной линии
|
|||
|
|
p, ok := upd.Periods[1]
|
|||
|
|
if !ok {
|
|||
|
|
return "ставки на 1й сет не найдены", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var offers []onexbet.ParamOffer
|
|||
|
|
|
|||
|
|
switch match.FavoriteSide {
|
|||
|
|
case FavoriteHome:
|
|||
|
|
offers = p.Handicap.Home
|
|||
|
|
|
|||
|
|
case FavoriteAway:
|
|||
|
|
offers = p.Handicap.Away
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
return "условие 'фаворит в матче' не выполнено", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Масимальные минусовые форы первыми
|
|||
|
|
SortOffersByParamMinMax(offers)
|
|||
|
|
|
|||
|
|
for _, offer := range offers {
|
|||
|
|
switch offer.Param {
|
|||
|
|
case "-2.5", "-3.0", "-3.5", "-4.0", "-4.5", "-5.0":
|
|||
|
|
if offer.Price >= 1.75 && offer.Price <= 1.95 {
|
|||
|
|
local := match.MaxFavoritHandicapIn1stSet
|
|||
|
|
if local == nil {
|
|||
|
|
match.MaxFavoritHandicapIn1stSet = &onexbet.ParamOffer{
|
|||
|
|
Price: offer.Price,
|
|||
|
|
Param: offer.Param,
|
|||
|
|
ParamFloat: offer.ParamFloat,
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
// нужно сравнить параметры
|
|||
|
|
if offer.ParamFloat < local.ParamFloat {
|
|||
|
|
// Фора стала еще больше - обновляем, ибо размер форы влияет
|
|||
|
|
// на плюсовую фору в прогнозе
|
|||
|
|
local.Param = offer.Param
|
|||
|
|
local.ParamFloat = offer.ParamFloat
|
|||
|
|
local.Price = offer.Price
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case "-5.5":
|
|||
|
|
if offer.Price >= 1.75 && offer.Price <= 1.95 {
|
|||
|
|
return "1й сет, фора на фаворита -5.5", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if upd.CurrentPeriod < 2 {
|
|||
|
|
return "не 2й сет", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if upd.CurrentPeriod > 2 {
|
|||
|
|
return "2й сет завершен", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s1score, ok := upd.ScoreByPeriods[1]
|
|||
|
|
if !ok {
|
|||
|
|
// нет счета 1 сета
|
|||
|
|
return "счет 1го сета не найден", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s1total := s1score.Home + s1score.Away
|
|||
|
|
|
|||
|
|
if float64(s1total) > 8.5 {
|
|||
|
|
return fmt.Sprintf("тотал 1го сета %d", s1total), nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Поиски тотала с коэф. >= 1.5
|
|||
|
|
if match.MaxFavoritHandicapIn1stSet == nil {
|
|||
|
|
return "фора на фаворита в 1м сете от -2.5 и выше не была найдена", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p, ok := upd.Periods[2]
|
|||
|
|
if !ok {
|
|||
|
|
return "ставки на 2й сет не найдены", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Находим список фор на аутсайдера
|
|||
|
|
var (
|
|||
|
|
offers []onexbet.ParamOffer
|
|||
|
|
betSide int
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
switch match.FavoriteSide {
|
|||
|
|
case FavoriteHome:
|
|||
|
|
offers = p.Handicap.Away
|
|||
|
|
betSide = SideAway
|
|||
|
|
|
|||
|
|
if (s1score.Home - s1score.Away) <= 0 {
|
|||
|
|
return "фаворит 1й сет проиграл", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
case FavoriteAway:
|
|||
|
|
offers = p.Handicap.Home
|
|||
|
|
betSide = SideHome
|
|||
|
|
|
|||
|
|
if (s1score.Away - s1score.Home) <= 0 {
|
|||
|
|
return "фаворит 1й сет проиграл", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
return "условие 'фаворит в матче' не выполнено", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Масимальные плюсовые форы первыми
|
|||
|
|
SortOffersByParamMaxMin(offers)
|
|||
|
|
|
|||
|
|
// -2.5 превращаем в 3.4, -3 превращаем в 3.9
|
|||
|
|
minPlusHandicap := math.Abs(match.MaxFavoritHandicapIn1stSet.ParamFloat) - 0.1 + 1
|
|||
|
|
|
|||
|
|
for _, offer := range offers {
|
|||
|
|
if offer.Price < 1.5 || offer.Price > 2.5 {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if offer.ParamFloat > minPlusHandicap {
|
|||
|
|
|
|||
|
|
var sideName string
|
|||
|
|
|
|||
|
|
switch betSide {
|
|||
|
|
case SideHome:
|
|||
|
|
sideName = match.Home.CanonicalName
|
|||
|
|
|
|||
|
|
case SideAway:
|
|||
|
|
sideName = match.Away.CanonicalName
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var additionalHandicaps string
|
|||
|
|
|
|||
|
|
if offer.ParamFloat < 3.6 {
|
|||
|
|
// +3.5, +2.5
|
|||
|
|
additionalHandicaps = fmt.Sprintf("и выше (+%.1f; +%.1f)",
|
|||
|
|
offer.ParamFloat+1, offer.ParamFloat+2)
|
|||
|
|
} else if offer.ParamFloat < 4.6 {
|
|||
|
|
// // +4.5, +4.0
|
|||
|
|
additionalHandicaps = fmt.Sprintf("и выше (+%.1f)",
|
|||
|
|
offer.ParamFloat+1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
telegramMessage := fmt.Sprintf(`Сигнал # %%d.
|
|||
|
|
Алгоритм %d. Плюсовая фора на аутсайдера во 2м сете.
|
|||
|
|
%s
|
|||
|
|
%s - %s
|
|||
|
|
|
|||
|
|
Точка входа +%.1f кф. %.3f
|
|||
|
|
|
|||
|
|
Рекомендация: %s +%.1f %s`,
|
|||
|
|
s.id,
|
|||
|
|
match.Champ.Name,
|
|||
|
|
match.Home.CanonicalName, match.Away.CanonicalName,
|
|||
|
|
offer.ParamFloat, offer.Price,
|
|||
|
|
sideName, offer.ParamFloat, additionalHandicaps)
|
|||
|
|
|
|||
|
|
return "", &BetDetails{
|
|||
|
|
Market: MarketHandicap,
|
|||
|
|
Side: betSide,
|
|||
|
|
Param: offer.Param,
|
|||
|
|
Price: offer.Price,
|
|||
|
|
TelegramMessage: telegramMessage,
|
|||
|
|
}, Bet
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
msg := fmt.Sprintf("не найдена фора %.1f и выше с коэф. от 1.5 до 2.5",
|
|||
|
|
math.Abs(match.MaxFavoritHandicapIn1stSet.ParamFloat)+1)
|
|||
|
|
|
|||
|
|
return msg, nil, Waiting
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
func (s Tennis2ndSetPlusHandicap) GetResult(tip model.Tip, result onexbet.TeamMatchResult) (_ *TipResult) {
|
|||
|
|
score, ok := result.ScoreByPeriods[2]
|
|||
|
|
if !ok {
|
|||
|
|
// Сперва проверяем что счет есть.
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверяем что сет завершен
|
|||
|
|
if !isTennisSetCompleted(score) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var handicap float64
|
|||
|
|
|
|||
|
|
switch tip.Side {
|
|||
|
|
case SideHome:
|
|||
|
|
handicap = float64(score.AwayPoints - score.HomePoints)
|
|||
|
|
|
|||
|
|
case SideAway:
|
|||
|
|
handicap = float64(score.HomePoints - score.AwayPoints)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
param, err := strconv.ParseFloat(tip.Param, 64)
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Printf("Parse tip param: %s; param=%s", err, tip.Param)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
resultText := fmt.Sprintf(`2й сет: %d-%d`, score.HomePoints, score.AwayPoints)
|
|||
|
|
|
|||
|
|
if isFloatEqual(param, handicap, 0.1) {
|
|||
|
|
// Совпасть могут только целые форы +3, +4
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Void,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если форы не равны, то одна из них больше на 0.5 минимум, поэтому можно
|
|||
|
|
// просто сравнить
|
|||
|
|
|
|||
|
|
if handicap < 0.1 {
|
|||
|
|
// если меньше нуля (наш теннисит выиграл 2й сет)
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Won,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if handicap < param {
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Won,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Lost,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicap) GetTelegramTipPattern() string {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
////////////////////////////////////////
|
|||
|
|
|
|||
|
|
// Алгоритм 3 теннис
|
|||
|
|
|
|||
|
|
// Плюсовая фора 2 сет
|
|||
|
|
// Равные соперники
|
|||
|
|
|
|||
|
|
// Это ровная линия в кофициентах
|
|||
|
|
// 1.9 - 1.8
|
|||
|
|
// 1.87 - 1.87
|
|||
|
|
// 1.95 - 1.85
|
|||
|
|
|
|||
|
|
// Все покрытия: но в идеале Хард и грунт
|
|||
|
|
|
|||
|
|
// Только мужчины
|
|||
|
|
|
|||
|
|
// Условия:
|
|||
|
|
// Один спортсмен должен выиграть сет с большим преимуществом
|
|||
|
|
// Идеал:
|
|||
|
|
// 6:0
|
|||
|
|
// 6:1
|
|||
|
|
// Можно и 6:2
|
|||
|
|
|
|||
|
|
// Победитель первого сета должен получить КФ на победу 1.5
|
|||
|
|
|
|||
|
|
// Во втором сете мы ставим на плюсовую фору второго спортсмена
|
|||
|
|
// +2.5
|
|||
|
|
// +3.5
|
|||
|
|
// С КФ не ниже 1.5
|
|||
|
|
// Диапазон 1.5 - 1.7
|
|||
|
|
|
|||
|
|
type Tennis2ndSetPlusHandicapAny struct {
|
|||
|
|
id int64
|
|||
|
|
notifyInTelegram bool
|
|||
|
|
isChampAccepted func(string) bool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewTennis2ndSetPlusHandicapAny(opt StrategyOptions) Tennis2ndSetPlusHandicapAny {
|
|||
|
|
if opt.ID == 0 {
|
|||
|
|
panic("StrategyID not defined")
|
|||
|
|
}
|
|||
|
|
s := Tennis2ndSetPlusHandicapAny{}
|
|||
|
|
s.id = opt.ID
|
|||
|
|
s.notifyInTelegram = opt.NotifyInTelegram
|
|||
|
|
s.isChampAccepted = opt.IsChampAccepted
|
|||
|
|
return s
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicapAny) ID() int64 {
|
|||
|
|
return s.id
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicapAny) ShortName() string {
|
|||
|
|
return fmt.Sprintf("#%d S2 Equal Handicap", s.id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicapAny) IsNotifyInTelegram() bool {
|
|||
|
|
return s.notifyInTelegram
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicapAny) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool {
|
|||
|
|
if liveMatch.CurrentPeriod == 1 || liveMatch.CurrentPeriod == 2 {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicapAny) IsInteresting(match *TeamMatch) (interface{}, bool) {
|
|||
|
|
// Поиск фаворита
|
|||
|
|
for _, offer := range match.Odds.Winner2Way {
|
|||
|
|
if offer.Home >= 1.75 && offer.Home <= 1.97 {
|
|||
|
|
return nil, true
|
|||
|
|
} else if offer.Away >= 1.75 && offer.Away <= 1.97 {
|
|||
|
|
return nil, true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return nil, false
|
|||
|
|
}
|
|||
|
|
func (s Tennis2ndSetPlusHandicapAny) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (string, *BetDetails, TipCode) {
|
|||
|
|
if upd.CurrentPeriod < 2 {
|
|||
|
|
return "не 2й сет", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if upd.CurrentPeriod > 2 {
|
|||
|
|
return "2й сет завершен", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s1score, ok := upd.ScoreByPeriods[1]
|
|||
|
|
if !ok {
|
|||
|
|
// нет счета 1 сета
|
|||
|
|
return "счет 1го сета не найден", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s1total := s1score.Home + s1score.Away
|
|||
|
|
|
|||
|
|
if float64(s1total) > 8.5 {
|
|||
|
|
return fmt.Sprintf("тотал 1го сета %d", s1total), nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var (
|
|||
|
|
favorite FavoriteSide
|
|||
|
|
favoriteWinnerPrice float64
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
if upd.Winner2Way == nil {
|
|||
|
|
return "коэф. на победу в матче не найден", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if s1score.Home > s1score.Away {
|
|||
|
|
favorite = FavoriteHome
|
|||
|
|
favoriteWinnerPrice = upd.Winner2Way.Home
|
|||
|
|
} else {
|
|||
|
|
favorite = FavoriteAway
|
|||
|
|
favoriteWinnerPrice = upd.Winner2Way.Away
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if favoriteWinnerPrice > 1.5 {
|
|||
|
|
return fmt.Sprintf("коэф. на победу в матче %.3f", favoriteWinnerPrice), nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
sex := flashscore.ParseTennisSexFromChampName(match.FullChampName)
|
|||
|
|
if sex == "" {
|
|||
|
|
return "не удалось определить мужчины или женщины играют", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if sex == flashscore.SexWomen {
|
|||
|
|
champName := strings.ToLower(match.FullChampName)
|
|||
|
|
if !strings.Contains(champName, "wta") {
|
|||
|
|
return "женский матч должен играться на турнире серии WTA", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
p, ok := upd.Periods[2]
|
|||
|
|
if !ok {
|
|||
|
|
return "ставки на 2й сет не найдены", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Находим список фор на аутсайдера
|
|||
|
|
var (
|
|||
|
|
offers []onexbet.ParamOffer
|
|||
|
|
betSide int
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
switch favorite {
|
|||
|
|
case FavoriteHome:
|
|||
|
|
offers = p.Handicap.Away
|
|||
|
|
betSide = SideAway
|
|||
|
|
|
|||
|
|
case FavoriteAway:
|
|||
|
|
offers = p.Handicap.Home
|
|||
|
|
betSide = SideHome
|
|||
|
|
|
|||
|
|
default:
|
|||
|
|
return "условие 'фаворит в матче' не выполнено", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Масимальные плюсовые форы первыми
|
|||
|
|
SortOffersByParamMaxMin(offers)
|
|||
|
|
|
|||
|
|
// +2.5
|
|||
|
|
minPlusHandicap := 2.4
|
|||
|
|
|
|||
|
|
for _, offer := range offers {
|
|||
|
|
if offer.Price < 1.5 || offer.Price > 1.7 {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if offer.ParamFloat > minPlusHandicap {
|
|||
|
|
|
|||
|
|
var sideName string
|
|||
|
|
|
|||
|
|
switch betSide {
|
|||
|
|
case SideHome:
|
|||
|
|
sideName = match.Home.CanonicalName
|
|||
|
|
|
|||
|
|
case SideAway:
|
|||
|
|
sideName = match.Away.CanonicalName
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var additionalHandicaps string
|
|||
|
|
|
|||
|
|
if offer.ParamFloat < 3.6 {
|
|||
|
|
// +3.5, +2.5
|
|||
|
|
additionalHandicaps = fmt.Sprintf("и выше (+%.1f; +%.1f)",
|
|||
|
|
offer.ParamFloat+1, offer.ParamFloat+2)
|
|||
|
|
} else if offer.ParamFloat < 4.6 {
|
|||
|
|
// // +4.5, +4.0
|
|||
|
|
additionalHandicaps = fmt.Sprintf("и выше (+%.1f)",
|
|||
|
|
offer.ParamFloat+1)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
telegramMessage := fmt.Sprintf(`Сигнал # %%d.
|
|||
|
|
Алгоритм %d. Плюсовая фора во 2м сете.
|
|||
|
|
%s
|
|||
|
|
%s - %s
|
|||
|
|
|
|||
|
|
Точка входа +%.1f кф. %.3f
|
|||
|
|
|
|||
|
|
Рекомендация: %s +%.1f %s`,
|
|||
|
|
s.id,
|
|||
|
|
match.Champ.Name,
|
|||
|
|
match.Home.CanonicalName, match.Away.CanonicalName,
|
|||
|
|
offer.ParamFloat, offer.Price,
|
|||
|
|
sideName, offer.ParamFloat, additionalHandicaps)
|
|||
|
|
|
|||
|
|
return "", &BetDetails{
|
|||
|
|
Market: MarketHandicap,
|
|||
|
|
Side: betSide,
|
|||
|
|
Param: offer.Param,
|
|||
|
|
Price: offer.Price,
|
|||
|
|
TelegramMessage: telegramMessage,
|
|||
|
|
}, Bet
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
msg := fmt.Sprintf("не найдена фора +%.1f и выше с коэф. от 1.5 до 1.7", 2.5)
|
|||
|
|
|
|||
|
|
return msg, nil, Waiting
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
func (s Tennis2ndSetPlusHandicapAny) GetResult(tip model.Tip, result onexbet.TeamMatchResult) (_ *TipResult) {
|
|||
|
|
score, ok := result.ScoreByPeriods[2]
|
|||
|
|
if !ok {
|
|||
|
|
// Сперва проверяем что счет есть.
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверяем что сет завершен
|
|||
|
|
if !isTennisSetCompleted(score) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var handicap float64
|
|||
|
|
|
|||
|
|
switch tip.Side {
|
|||
|
|
case SideHome:
|
|||
|
|
handicap = float64(score.AwayPoints - score.HomePoints)
|
|||
|
|
|
|||
|
|
case SideAway:
|
|||
|
|
handicap = float64(score.HomePoints - score.AwayPoints)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
param, err := strconv.ParseFloat(tip.Param, 64)
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Printf("Parse tip param: %s; param=%s", err, tip.Param)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
resultText := fmt.Sprintf(`2й сет: %d-%d`, score.HomePoints, score.AwayPoints)
|
|||
|
|
|
|||
|
|
if isFloatEqual(param, handicap, 0.1) {
|
|||
|
|
// Совпасть могут только целые форы +3, +4
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Void,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если форы не равны, то одна из них больше на 0.5 минимум, поэтому можно
|
|||
|
|
// просто сравнить
|
|||
|
|
|
|||
|
|
if handicap < 0.1 {
|
|||
|
|
// если меньше нуля (наш теннисит выиграл 2й сет)
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Won,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if handicap < param {
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Won,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Lost,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetPlusHandicapAny) GetTelegramTipPattern() string {
|
|||
|
|
return ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
////////////////////////////////////////
|
|||
|
|
|
|||
|
|
// 4 Алгоритм
|
|||
|
|
// плюсовая фора на Фаворита
|
|||
|
|
|
|||
|
|
// Фаорит кф 1,6 и ниже проигрывает первый сет с большим преимуществом
|
|||
|
|
// 0:6
|
|||
|
|
// 1:6
|
|||
|
|
// 2:6
|
|||
|
|
|
|||
|
|
// Во втором сете мы ставим на плюсовую фору фаворита
|
|||
|
|
// +2.5
|
|||
|
|
// +3.5
|
|||
|
|
// Коэф. в диапазоне 1.5 - 2.5
|
|||
|
|
|
|||
|
|
type Tennis2ndSetFavoriteHandicap struct {
|
|||
|
|
id int64
|
|||
|
|
notifyInTelegram bool
|
|||
|
|
isChampAccepted func(string) bool
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func NewTennis2ndSetFavoriteHandicap(opt StrategyOptions) Tennis2ndSetFavoriteHandicap {
|
|||
|
|
if opt.ID == 0 {
|
|||
|
|
panic("StrategyID not defined")
|
|||
|
|
}
|
|||
|
|
s := Tennis2ndSetFavoriteHandicap{}
|
|||
|
|
s.id = opt.ID
|
|||
|
|
s.notifyInTelegram = opt.NotifyInTelegram
|
|||
|
|
s.isChampAccepted = opt.IsChampAccepted
|
|||
|
|
return s
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetFavoriteHandicap) ID() int64 {
|
|||
|
|
return s.id
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetFavoriteHandicap) ShortName() string {
|
|||
|
|
return fmt.Sprintf("#%d S2 Favorite Handicap", s.id)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetFavoriteHandicap) IsNotifyInTelegram() bool {
|
|||
|
|
return s.notifyInTelegram
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetFavoriteHandicap) IsNeedToWatch(liveMatch onexbet.LiveMatch) bool {
|
|||
|
|
if liveMatch.CurrentPeriod == 1 || liveMatch.CurrentPeriod == 2 {
|
|||
|
|
return true
|
|||
|
|
}
|
|||
|
|
return false
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetFavoriteHandicap) IsInteresting(match *TeamMatch) (interface{}, bool) {
|
|||
|
|
// Поиск фаворита
|
|||
|
|
for _, offer := range match.Odds.Winner2Way {
|
|||
|
|
if offer.Home <= 1.6 {
|
|||
|
|
match.Lte16FavoriteSide = FavoriteHome
|
|||
|
|
|
|||
|
|
return fmt.Sprintf("фаворит %s, коэф. %.2f",
|
|||
|
|
match.Home.CanonicalName, offer.Home), true
|
|||
|
|
} else if offer.Away <= 1.6 {
|
|||
|
|
match.Lte16FavoriteSide = FavoriteAway
|
|||
|
|
|
|||
|
|
return fmt.Sprintf("фаворит %s, коэф. %.2f",
|
|||
|
|
match.Away.CanonicalName, offer.Away), true
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
return nil, false
|
|||
|
|
}
|
|||
|
|
func (s Tennis2ndSetFavoriteHandicap) GetTip(match *TeamMatch, upd onexbet.TeamLiveMatchData) (string, *BetDetails, TipCode) {
|
|||
|
|
if upd.CurrentPeriod < 2 {
|
|||
|
|
return "не 2й сет", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if upd.CurrentPeriod > 2 {
|
|||
|
|
return "2й сет завершен", nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s1score, ok := upd.ScoreByPeriods[1]
|
|||
|
|
if !ok {
|
|||
|
|
// нет счета 1 сета
|
|||
|
|
return "счет 1го сета не найден", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var dif int
|
|||
|
|
|
|||
|
|
switch match.Lte16FavoriteSide {
|
|||
|
|
case FavoriteHome:
|
|||
|
|
dif = s1score.Away - s1score.Home
|
|||
|
|
|
|||
|
|
case FavoriteAway:
|
|||
|
|
dif = s1score.Home - s1score.Away
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// фаворит должен проиграть со счетом 6-0, 6-1 или 6-2
|
|||
|
|
if dif < 4 {
|
|||
|
|
return fmt.Sprintf("фора первого сета %d", dif), nil, Unwatch
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
s2, ok := upd.Periods[2]
|
|||
|
|
if !ok {
|
|||
|
|
return "ставки на 2й сет не найдены", nil, Waiting
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Находим список фор на аутсайдера
|
|||
|
|
var (
|
|||
|
|
offers []onexbet.ParamOffer
|
|||
|
|
betSide int
|
|||
|
|
sideName string
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
switch match.Lte16FavoriteSide {
|
|||
|
|
case FavoriteHome:
|
|||
|
|
offers = s2.Handicap.Home
|
|||
|
|
betSide = SideHome
|
|||
|
|
sideName = match.Home.CanonicalName
|
|||
|
|
|
|||
|
|
case FavoriteAway:
|
|||
|
|
offers = s2.Handicap.Away
|
|||
|
|
betSide = SideAway
|
|||
|
|
sideName = match.Away.CanonicalName
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Масимальные плюсовые форы первыми
|
|||
|
|
SortOffersByParamMaxMin(offers)
|
|||
|
|
|
|||
|
|
for _, offer := range offers {
|
|||
|
|
if offer.Price < 1.5 || offer.Price > 2.5 {
|
|||
|
|
continue
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
switch offer.Param {
|
|||
|
|
case "2.5", "3.5":
|
|||
|
|
additionalHandicaps := fmt.Sprintf("и выше (+%.1f; +%.1f)",
|
|||
|
|
offer.ParamFloat+1, offer.ParamFloat+2)
|
|||
|
|
|
|||
|
|
telegramMessage := fmt.Sprintf(`Сигнал # %%d.
|
|||
|
|
Алгоритм %d. Плюсовая фора во 2м сете.
|
|||
|
|
%s
|
|||
|
|
%s - %s
|
|||
|
|
|
|||
|
|
Точка входа +%.1f кф. %.3f
|
|||
|
|
|
|||
|
|
Рекомендация: %s +%.1f %s`,
|
|||
|
|
s.id,
|
|||
|
|
match.Champ.Name,
|
|||
|
|
match.Home.CanonicalName, match.Away.CanonicalName,
|
|||
|
|
offer.ParamFloat, offer.Price,
|
|||
|
|
sideName, offer.ParamFloat, additionalHandicaps)
|
|||
|
|
|
|||
|
|
return "", &BetDetails{
|
|||
|
|
Market: MarketHandicap,
|
|||
|
|
Side: betSide,
|
|||
|
|
Param: offer.Param,
|
|||
|
|
Price: offer.Price,
|
|||
|
|
TelegramMessage: telegramMessage,
|
|||
|
|
}, Bet
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
msg := fmt.Sprintf("не найдена фора +2.5 / +3.5 с коэф. от 1.5 до 2.5")
|
|||
|
|
|
|||
|
|
return msg, nil, Waiting
|
|||
|
|
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Общий код всех фор - можно выделить в отдельную функцию
|
|||
|
|
func (s Tennis2ndSetFavoriteHandicap) GetResult(tip model.Tip, result onexbet.TeamMatchResult) (_ *TipResult) {
|
|||
|
|
score, ok := result.ScoreByPeriods[2]
|
|||
|
|
if !ok {
|
|||
|
|
// Сперва проверяем что счет есть.
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Проверяем что сет завершен
|
|||
|
|
if !isTennisSetCompleted(score) {
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
var handicap float64
|
|||
|
|
|
|||
|
|
switch tip.Side {
|
|||
|
|
case SideHome:
|
|||
|
|
handicap = float64(score.AwayPoints - score.HomePoints)
|
|||
|
|
|
|||
|
|
case SideAway:
|
|||
|
|
handicap = float64(score.HomePoints - score.AwayPoints)
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
param, err := strconv.ParseFloat(tip.Param, 64)
|
|||
|
|
if err != nil {
|
|||
|
|
fmt.Printf("Parse tip param: %s; param=%s", err, tip.Param)
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
resultText := fmt.Sprintf(`2й сет: %d-%d`, score.HomePoints, score.AwayPoints)
|
|||
|
|
|
|||
|
|
if isFloatEqual(param, handicap, 0.1) {
|
|||
|
|
// Совпасть могут только целые форы +3, +4
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Void,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// Если форы не равны, то одна из них больше на 0.5 минимум, поэтому можно
|
|||
|
|
// просто сравнить
|
|||
|
|
|
|||
|
|
if handicap < 0.1 {
|
|||
|
|
// если меньше нуля (наш теннисит выиграл 2й сет)
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Won,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
if handicap < param {
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Won,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
} else {
|
|||
|
|
return &TipResult{
|
|||
|
|
Status: model.Lost,
|
|||
|
|
Result: resultText,
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
func (s Tennis2ndSetFavoriteHandicap) GetTelegramTipPattern() string {
|
|||
|
|
return ""
|
|||
|
|
}
|