Skip to content

Commit

Permalink
getCurrentPrice return mark price
Browse files Browse the repository at this point in the history
  • Loading branch information
rluisr committed Aug 31, 2022
1 parent dbf0b49 commit 93fcbb0
Show file tree
Hide file tree
Showing 8 changed files with 156 additions and 66 deletions.
7 changes: 4 additions & 3 deletions pkg/adapter/controllers/tv_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,13 @@ type TVController struct {
Interactor usecase.TVInteractor
}

func NewTVController(rwDB, roDB *gorm.DB) *TVController {
func NewTVController(rwDB, roDB *gorm.DB, httpClient *http.Client) *TVController {
return &TVController{
Interactor: usecase.TVInteractor{
TVRepository: &gateway.TVRepository{
RWDB: rwDB,
RODB: roDB,
RWDB: rwDB,
RODB: roDB,
HTTPClient: httpClient,
},
},
}
Expand Down
140 changes: 84 additions & 56 deletions pkg/adapter/gateway/tv_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,22 +19,27 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
package gateway

import (
"encoding/json"
"errors"
"fmt"
"github.com/frankrap/bybit-api/rest"
"github.com/rluisr/tvbit-bot/pkg/domain"
"github.com/rluisr/tvbit-bot/pkg/external/bybit"
"github.com/rluisr/tvbit-bot/utils"
"gorm.io/gorm"
"io"
"math"
"net/http"
"strconv"
"strings"
"time"
)

type (
TVRepository struct {
RWDB *gorm.DB
RODB *gorm.DB
RWDB *gorm.DB
RODB *gorm.DB
HTTPClient *http.Client
}
)

Expand Down Expand Up @@ -69,52 +74,7 @@ func (r *TVRepository) SaveOrder(req domain.TV, order *rest.Order) error {
return r.RWDB.Save(&orderHistory).Error
}

// isOK: current time is between "start_time" and "stop_time"
func (r *TVRepository) isOK(req domain.TV) (bool, error) {
var setting domain.Setting
err := r.RODB.Where("api_key = ? and api_secret_key = ?", req.APIKey, req.APISecretKey).Take(&setting).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return true, nil
}
return false, err
}

if !setting.StartTime.Valid || !setting.StopTime.Valid {
return true, nil
}

utc := utils.UTC()
now := time.Now().In(utc)

hour := now.Hour()
minute := now.Minute()
n := fmt.Sprintf("%d%d", hour, minute)
currentTime, err := strconv.Atoi(n)
if err != nil {
return false, err
}

userStartTimeStr := strings.Replace(setting.StartTime.String, ":", "", 1)
userStopTimeStr := strings.Replace(setting.StopTime.String, ":", "", 1)

userStartTime, err := strconv.Atoi(userStartTimeStr)
if err != nil {
return false, err
}
userStopTime, err := strconv.Atoi(userStopTimeStr)
if err != nil {
return false, err
}

if userStartTime < currentTime && currentTime < userStopTime {
return true, nil
}

return false, nil
}

func (r *TVRepository) CalculateTPSL(req domain.TV, bybitClient *rest.ByBit, value interface{}, isType string) (float64, error) {
func (r *TVRepository) CalculateTPSL(req domain.TV, value interface{}, isType string) (float64, error) {
var err error

str, isOK := value.(string)
Expand All @@ -126,7 +86,7 @@ func (r *TVRepository) CalculateTPSL(req domain.TV, bybitClient *rest.ByBit, val
return 0, nil
}

currentPrice, err := r.getCurrentPrice(req.Order.Symbol, bybitClient)
currentPrice, err := r.getCurrentPrice(req.Order.Symbol)
if err != nil {
return 0, err
}
Expand Down Expand Up @@ -169,6 +129,9 @@ func (r *TVRepository) CalculateTPSL(req domain.TV, bybitClient *rest.ByBit, val
if err != nil {
return 0, fmt.Errorf("convert string to int err %w", err)
}
if req.Order.Side == "Sell" {
return currentPrice - float64(inputPrice), nil
}
return currentPrice + float64(inputPrice), nil
}
if strings.Contains(str, "-") {
Expand All @@ -177,6 +140,9 @@ func (r *TVRepository) CalculateTPSL(req domain.TV, bybitClient *rest.ByBit, val
if err != nil {
return 0, fmt.Errorf("convert string to int err %w", err)
}
if req.Order.Side == "Sell" {
return currentPrice + float64(inputPrice), nil
}
return currentPrice - float64(inputPrice), nil
}

Expand All @@ -189,16 +155,78 @@ func (r *TVRepository) CalculateTPSL(req domain.TV, bybitClient *rest.ByBit, val

}

// getCurrentPrice returns close price one minute ago
func (r *TVRepository) getCurrentPrice(symbol string, bybitClient *rest.ByBit) (float64, error) {
_, _, resp, err := bybitClient.LinearGetKLine(symbol, "1", time.Now().Unix()-60, 1)
// isOK: current time is between "start_time" and "stop_time"
func (r *TVRepository) isOK(req domain.TV) (bool, error) {
var setting domain.Setting
err := r.RODB.Where("api_key = ? and api_secret_key = ?", req.APIKey, req.APISecretKey).Take(&setting).Error
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return true, nil
}
return false, err
}

if !setting.StartTime.Valid || !setting.StopTime.Valid {
return true, nil
}

utc := utils.UTC()
now := time.Now().In(utc)

hour := now.Hour()
minute := now.Minute()
n := fmt.Sprintf("%d%d", hour, minute)
currentTime, err := strconv.Atoi(n)
if err != nil {
return false, err
}

userStartTimeStr := strings.Replace(setting.StartTime.String, ":", "", 1)
userStopTimeStr := strings.Replace(setting.StopTime.String, ":", "", 1)

userStartTime, err := strconv.Atoi(userStartTimeStr)
if err != nil {
return false, err
}
userStopTime, err := strconv.Atoi(userStopTimeStr)
if err != nil {
return false, err
}

if userStartTime < currentTime && currentTime < userStopTime {
return true, nil
}

return false, nil
}

// getCurrentPrice returns mark price
func (r *TVRepository) getCurrentPrice(symbol string) (float64, error) {
tickersURL := fmt.Sprintf("%sderivatives/v3/public/tickers?category=linear&symbol=%s", bybit.BaseURL, symbol)

req, _ := http.NewRequest(http.MethodGet, tickersURL, http.NoBody)
resp, err := r.HTTPClient.Do(req)
if err != nil {
return 0, fmt.Errorf("failed to get current price err: %w", err)
return 0, err
}
defer resp.Body.Close()

b, err := io.ReadAll(resp.Body)
if err != nil {
return 0, err
}

if len(resp) == 0 {
return 0, fmt.Errorf("failed to get current price. invalid query")
var ticker domain.Ticker
err = json.Unmarshal(b, &ticker)
if err != nil {
return 0, err
}

markPriceStr := ticker.Result.List[0].MarkPrice
markPrice, err := strconv.ParseFloat(markPriceStr, 64)
if err != nil {
return 0, err
}

return resp[0].Close, nil
return markPrice, nil
}
34 changes: 34 additions & 0 deletions pkg/domain/bybit.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package domain

type Ticker struct {
RetCode int `json:"retCode"`
RetMsg string `json:"retMsg"`
Result struct {
Category string `json:"category"`
List []struct {
Symbol string `json:"symbol"`
BidPrice string `json:"bidPrice"`
AskPrice string `json:"askPrice"`
LastPrice string `json:"lastPrice"`
LastTickDirection string `json:"lastTickDirection"`
PrevPrice24H string `json:"prevPrice24h"`
Price24HPcnt string `json:"price24hPcnt"`
HighPrice24H string `json:"highPrice24h"`
LowPrice24H string `json:"lowPrice24h"`
PrevPrice1H string `json:"prevPrice1h"`
MarkPrice string `json:"markPrice"`
IndexPrice string `json:"indexPrice"`
OpenInterest string `json:"openInterest"`
Turnover24H string `json:"turnover24h"`
Volume24H string `json:"volume24h"`
FundingRate string `json:"fundingRate"`
NextFundingTime string `json:"nextFundingTime"`
PredictedDeliveryPrice string `json:"predictedDeliveryPrice"`
BasisRate string `json:"basisRate"`
DeliveryFeeRate string `json:"deliveryFeeRate"`
DeliveryTime string `json:"deliveryTime"`
} `json:"list"`
} `json:"result"`
RetExtInfo interface{} `json:"retExtInfo"`
Time int64 `json:"time"`
}
7 changes: 4 additions & 3 deletions pkg/external/bybit/bybit.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,12 @@ import (
"github.com/rluisr/tvbit-bot/pkg/domain"
)

var BaseURL = "https://api.bybit.com/"

func Init(req domain.TV) *rest.ByBit {
baseURL := "https://api.bybit.com/"
if req.IsTestNet {
baseURL = "https://api-testnet.bybit.com/"
BaseURL = "https://api-testnet.bybit.com/"
}

return rest.New(nil, baseURL, req.APIKey, req.APISecretKey, false)
return rest.New(nil, BaseURL, req.APIKey, req.APISecretKey, false)
}
25 changes: 25 additions & 0 deletions pkg/external/http.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package external

import (
"net"
"net/http"
"time"
)

func NewHTTPClient() *http.Client {
return &http.Client{
Transport: &http.Transport{
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
}).DialContext,
MaxIdleConns: 128,
MaxIdleConnsPerHost: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ResponseHeaderTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
Timeout: 60 * time.Second,
}
}
3 changes: 2 additions & 1 deletion pkg/external/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,8 @@ func init() {
Router = gin.Default()
Router.ForwardedByClientIP = true

tvController := controllers.NewTVController(rwDB, roDB)
httpClient := NewHTTPClient()
tvController := controllers.NewTVController(rwDB, roDB, httpClient)
settingController := controllers.NewSettingController(rwDB, roDB)

Router.GET("/", func(c *gin.Context) {
Expand Down
2 changes: 1 addition & 1 deletion pkg/usecase/interfaces/repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ import (
type TVRepository interface {
CreateOrder(domain.TV, *rest.ByBit) (*rest.Order, error)
SaveOrder(domain.TV, *rest.Order) error
CalculateTPSL(domain.TV, *rest.ByBit, interface{}, string) (float64, error)
CalculateTPSL(domain.TV, interface{}, string) (float64, error)
}

type SettingRepository interface {
Expand Down
4 changes: 2 additions & 2 deletions pkg/usecase/tv_interactor.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ type TVInteractor struct {
func (i *TVInteractor) CreateOrder(req domain.TV, bybitClient *rest.ByBit) (domain.TVOrderResponse, error) {
var err error

req.Order.TP, err = i.TVRepository.CalculateTPSL(req, bybitClient, req.Order.TP, "TP")
req.Order.TP, err = i.TVRepository.CalculateTPSL(req, req.Order.TP, "TP")
if err != nil {
return domain.TVOrderResponse{
Success: false,
Expand All @@ -41,7 +41,7 @@ func (i *TVInteractor) CreateOrder(req domain.TV, bybitClient *rest.ByBit) (doma
}, err
}

req.Order.SL, err = i.TVRepository.CalculateTPSL(req, bybitClient, req.Order.SL, "SL")
req.Order.SL, err = i.TVRepository.CalculateTPSL(req, req.Order.SL, "SL")
if err != nil {
return domain.TVOrderResponse{
Success: false,
Expand Down

0 comments on commit 93fcbb0

Please sign in to comment.