package model import ( "fmt" "math" "sort" "strings" "gordenko.dev/dima/qx" ) func New(qxdb *qx.Db) *Model { if qxdb == nil { panic("Param is required") } s := new(Model) s.qx = qxdb return s } type Model struct { qx *qx.Db } func (s *Model) AddTip(tip Tip) (tipID int64, err error) { /* if req.Name == "" { err = api.Err{ Code: EmptyValue, Field: "name", } return } */ tx, err := s.qx.Tx() if err != nil { return } defer tx.Close(&err) tipID, err = tx.Insert(tip) if err != nil { return } err = tx.Commit() return } func (s *Model) GetTip(tipID int64) (_ *Tip, err error) { tip := Tip{ TipID: tipID, } found, err := s.qx.One(&tip) if err != nil { return } if found { return &tip, nil } return } func (s *Model) HasTip(matchID string, strategyID int64) (has bool, err error) { has, err = s.qx.Has("tips", "WHERE matchID=? AND strategyID=?", matchID, strategyID) return } func (s *Model) ListSportTips(sportID int) (tips []Tip, err error) { err = s.qx.ListBy(&tips, "WHERE sportID=? ORDER BY tipTime DESC", sportID) return } func (s *Model) ListTips() (tips []Tip, err error) { err = s.qx.ListBy(&tips, "ORDER BY tipTime DESC") return } func (s *Model) ListWaitingTips() (tips []Tip, err error) { err = s.qx.ListBy(&tips, "WHERE status=? ORDER BY tipTime DESC", Waiting) return } func (s *Model) ListStrategyTips(strategyID int) (tips []Tip, err error) { err = s.qx.ListBy(&tips, "WHERE strategyID=? ORDER BY tipTime DESC", strategyID) return } // ListMatchTips - возвращает прогнозы с неизвестным результатом func (s *Model) ListMatchTips(matchID string) (tips []Tip, err error) { err = s.qx.ListBy(&tips, "WHERE matchID=? AND status=0", matchID) return } func (s *Model) SetTipResult(req SetTipResultReq) (err error) { switch req.Status { case Waiting, Won, Lost, Void: // pass default: err = fix.Field(WrongValue, "status") return } req.Result = strings.TrimSpace(req.Result) if req.Result == "" { err = fix.Field(EmptyValue, "result") return } err = s.qx.Exec(`UPDATE tips SET result=?, status=?, notes=? WHERE tipID=?`, req.Result, req.Status, req.Notes, req.TipID) return } type StrategyPoint struct { Date int64 `json:"date"` Profit float64 `json:"profit"` } func (s *Model) GetStrategyHistory(strategyID int64) (list []StrategyPoint, err error) { var ( tips []Tip profit float64 ) err = s.qx.ListBy(&tips, "WHERE strategyID=? AND status IN (1, 2) ORDER BY tipTime ASC", strategyID) if err != nil { return } for _, tip := range tips { if tip.Status == Won { profit += (tip.Price - 1) * 10 } else { profit -= 10 } list = append(list, StrategyPoint{ Date: tip.TipTime, Profit: profit, }) } return } func (s *Model) GetStrategyRatedHistory(strategyID int64) (list []StrategyPoint, err error) { var ( tips []Tip profit float64 ) err = s.qx.ListBy(&tips, "WHERE strategyID=? AND status IN (1, 2) AND isRated=true ORDER BY tipTime ASC", strategyID) if err != nil { return } for _, tip := range tips { if tip.Status == Won { profit += (tip.Price - 1) * 10 } else { profit -= 10 } list = append(list, StrategyPoint{ Date: tip.TipTime, Profit: profit, }) } return } func (s *Model) ListStrategiesReports(sportID int) (list []StrategyReport, err error) { var ( tips []Tip reports = make(map[int64]*StrategyReport) ) err = s.qx.ListBy(&tips, "WHERE sportID=? AND status IN (1, 2) ORDER BY tipTime ASC", sportID) if err != nil { return } for _, tip := range tips { report, ok := reports[tip.StrategyID] if !ok { report = &StrategyReport{ StrategyID: tip.StrategyID, } reports[tip.StrategyID] = report } report.Turnover += 10 if tip.Status == Won { report.Profit += (tip.Price - 1) * 10 report.WonTips++ } else { report.Profit -= 10 report.LostTips++ } if report.Profit < report.Drawdown { report.Drawdown = report.Profit } } var strategies []Strategy err = s.qx.ListQuery(&strategies, "SELECT strategyID, name FROM strategies WHERE sportID=?", sportID) if err != nil { return } for _, strategy := range strategies { report, ok := reports[strategy.StrategyID] if ok { report.Strategy = strategy.Name } } for _, report := range reports { // turnover = 100% // profit = x (ROI) report.ROI = (report.Profit * 100) / report.Turnover // won + lost = 100% // won = x (Accuracy) report.Accuracy = (float64(report.WonTips) * 100) / float64(report.WonTips+report.LostTips) list = append(list, *report) } sort.Slice(list, func(i, j int) bool { return list[i].StrategyID < list[j].StrategyID }) return } func (s *Model) GetSportRatedReport(sportID int64) (report SportReport, err error) { var tips []Tip report.SportID = sportID err = s.qx.ListBy(&tips, "WHERE sportID=? AND status IN (1, 2) AND isRated=true ORDER BY tipTime ASC", sportID) if err != nil { return } for _, tip := range tips { report.Turnover += 10 if tip.Status == Won { report.Profit += (tip.Price - 1) * 10 report.WonTips++ } else { report.Profit -= 10 report.LostTips++ } if report.Profit < report.Drawdown { report.Drawdown = report.Profit } } // turnover = 100% // profit = x (ROI) report.ROI = (report.Profit * 100) / report.Turnover // won + lost = 100% // won = x (Accuracy) report.Accuracy = (float64(report.WonTips) * 100) / float64(report.WonTips+report.LostTips) return } func getAccuracy(won, lost int) (accuracy float64) { // won + lost = 100% // won = x% // x = (won*100) / (won+lost) total := won + lost if total == 0 { return } return float64(won*100) / float64(total) } type StrategyChampStats struct { //StrategyID int64 `json:"strategyID"` ZoneID string `json:"zoneID" qx:"table=tips"` ZoneName string `json:"zoneName"` ChampID string `json:"champID"` ChampName string `json:"champName"` Won int `json:"won"` Lost int `json:"lost"` AvgPrice float64 `json:"avgPrice"` } // Статистика по лигам func (s *Model) ListStrategyChampStats(strategyID int64) (list []StrategyChampStats, err error) { err = s.qx.ListQuery(&list, ` SELECT z.zoneID, z.name, t.champID, c.name, COUNT(IF(t.status=1,1,NULL)) as won, COUNT(IF(t.status=2,1,NULL)) as lost, AVG(t.price) FROM tips t INNER JOIN teamChamps c USING(champID) INNER JOIN teamZones z USING(zoneID) WHERE t.strategyID=? AND t.status IN (1,2) GROUP BY t.champID ORDER BY c.name ASC`, strategyID) if err != nil { return } eps := 0.001 sort.Slice(list, func(i, j int) bool { // Сортируем по точности прогнозов a := getAccuracy(list[i].Won, list[i].Lost) b := getAccuracy(list[j].Won, list[j].Lost) dif := math.Abs(a - b) if dif < eps { if list[i].Won == list[j].Won { if list[i].ZoneName == list[j].ZoneName { return list[i].ChampName < list[j].ChampName } return list[i].ZoneName < list[j].ZoneName } return list[i].Won > list[j].Won } return a > b }) return } // Статистика по лигам func (s *Model) ListStrategyRatedChampStats(strategyID int64) (list []StrategyChampStats, err error) { err = s.qx.ListQuery(&list, ` SELECT z.zoneID, z.name, t.champID, c.name, COUNT(IF(t.status=1,1,NULL)) as won, COUNT(IF(t.status=2,1,NULL)) as lost, AVG(t.price) FROM tips t INNER JOIN teamChamps c USING(champID) INNER JOIN teamZones z USING(zoneID) WHERE t.strategyID=? AND t.status IN (1,2) AND t.isRated=true GROUP BY t.champID ORDER BY c.name ASC`, strategyID) if err != nil { return } eps := 0.001 sort.Slice(list, func(i, j int) bool { // Сортируем по точности прогнозов a := getAccuracy(list[i].Won, list[i].Lost) b := getAccuracy(list[j].Won, list[j].Lost) dif := math.Abs(a - b) if dif < eps { if list[i].Won == list[j].Won { if list[i].ZoneName == list[j].ZoneName { return list[i].ChampName < list[j].ChampName } return list[i].ZoneName < list[j].ZoneName } return list[i].Won > list[j].Won } return a > b }) return } type Strategy struct { StrategyID int64 `json:"strategyID" qx:"table=strategies; pk"` Name string `json:"name"` } // Статистика по лигам func (s *Model) ListStrategies() (list []Strategy, err error) { tmp := []struct { StrategyID int64 Name string SportID int64 SportName string }{} err = s.qx.ListQuery(&tmp, ` SELECT s.strategyID, s.name, x.sportID, x.name FROM strategies s INNER JOIN sports x USING(sportID) ORDER BY x.sportID ASC, s.strategyID ASC`) if err != nil { return } for _, x := range tmp { list = append(list, Strategy{ StrategyID: x.StrategyID, Name: fmt.Sprintf("#%d %s. %s", x.StrategyID, x.SportName, x.Name), }) } return /* return []Strategy{ { StrategyID: 1, Name: "#1 Алгоритм 1 (Футбол, гол в 1м тайме)", }, { StrategyID: 2, Name: "#2 Алгоритм 2 (Футбол, гол в матче)", }, { StrategyID: 3, Name: "#3 Алгоритм 3 (Футбол, 2й гол в матче)", }, { StrategyID: 4, Name: "#4 Алгоритм ТБ (-9) (Гандбол, ТБ в матче)", }, { StrategyID: 5, Name: "#5 Алгоритм ТМ (+8) (Гандбол, ТМ в матче)", }, { StrategyID: 6, Name: "#6 Алгоритм 1 модифицированный (Футбол, гол в 1м тайме)", }, { StrategyID: 7, Name: "#7 Алгоритм 2 модифицированный (Футбол, гол в матче)", }, { StrategyID: 8, Name: "#8 Алгоритм 3 модифицированный (Футбол, 2й гол в матче)", }, { StrategyID: 11, Name: "#11 Алгоритм ТБ (-6) (Гандбол, ТБ в 1м тайме)", }, { StrategyID: 12, Name: "#12 Алгоритм ТМ (+5) (Гандбол, ТМ в 1м тайме)", }, }, nil */ }