Skip to content

Commit

Permalink
Fetch closed PnL
Browse files Browse the repository at this point in the history
  • Loading branch information
rluisr committed Sep 28, 2022
1 parent bb1f6f7 commit cd89b84
Show file tree
Hide file tree
Showing 6 changed files with 153 additions and 36 deletions.
15 changes: 15 additions & 0 deletions pkg/adapter/gateway/bybit_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,21 @@ func (r *BybitRepository) FetchOrder(req *domain.TV, orderID string) error {
return nil
}

func (r *BybitRepository) GetClosedOrderLast(symbol string) (*domain.BybitLinearClosedPnLResponse, error) {
params := map[string]interface{}{}
params["symbol"] = symbol
params["exec_type"] = "Trade"
params["limit"] = 1

var result domain.BybitLinearClosedPnLResponse
_, resp, err := r.Client.SignedRequest(http.MethodGet, "private/linear/trade/closed-pnl/list", params, &result)
if err != nil {
return nil, fmt.Errorf("SignedRequest err: %w, body: %s", err, string(resp))
}

return &result, nil
}

func (r *BybitRepository) GetActiveOrderCount(req *domain.TV, positions *[]rest.LinearPosition) int {
var activeOrder int

Expand Down
10 changes: 10 additions & 0 deletions pkg/adapter/gateway/tv_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,10 @@ type (
}
)

func (r *TVRepository) UpdateOrder(order *domain.TVOrder) error {
return r.RWDB.Save(&order).Error
}

func (r *TVRepository) SaveOrder(req domain.TV, order *domain.TVOrder) error {
var setting domain.Setting
err := r.RODB.Where("api_key = ? AND api_secret_key = ?", req.APIKey, req.APISecretKey).First(&setting).Error
Expand Down Expand Up @@ -71,3 +75,9 @@ func (r *TVRepository) GetSettings() ([]domain.Setting, error) {
func (r *TVRepository) SaveWalletHistories(histories []domain.WalletHistory) error {
return r.RWDB.Create(&histories).Error
}

func (r *TVRepository) GetPLNullOrders(settingID uint64) (*[]domain.TVOrder, error) {
var orders []domain.TVOrder
err := r.RODB.Where("setting_id = ? AND pl IS NULL", settingID).Order("id desc").Find(&orders).Error
return &orders, err
}
36 changes: 36 additions & 0 deletions pkg/domain/bybit.go
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,42 @@ type BybitUSDCPositions struct {
RetMsg string `json:"retMsg"`
}

type BybitLinearClosedPnLResponse struct {
RetCode int `json:"ret_code"`
RetMsg string `json:"ret_msg"`
Result struct {
Data []BybitLinearClosedPnL `json:"data"`
CurrentPage int `json:"current_page"`
} `json:"result"`
ExtCode string `json:"ext_code"`
ExtInfo string `json:"ext_info"`
TimeNow string `json:"time_now"`
RateLimitStatus int `json:"rate_limit_status"`
RateLimitResetMs int64 `json:"rate_limit_reset_ms"`
RateLimit int `json:"rate_limit"`
}

type BybitLinearClosedPnL struct {
ID int `json:"id"`
UserID int `json:"user_id"`
Symbol string `json:"symbol"`
OrderID string `json:"order_id"`
Side string `json:"side"`
Qty float64 `json:"qty"`
OrderPrice float64 `json:"order_price"`
OrderType string `json:"order_type"`
ExecType string `json:"exec_type"`
ClosedSize float64 `json:"closed_size"`
CumEntryValue float64 `json:"cum_entry_value"`
AvgEntryPrice float64 `json:"avg_entry_price"`
CumExitValue float64 `json:"cum_exit_value"`
AvgExitPrice float64 `json:"avg_exit_price"`
ClosedPnl float64 `json:"closed_pnl"`
FillCount int `json:"fill_count"`
Leverage int `json:"leverage"`
CreatedAt int64 `json:"created_at"`
}

type BybitWallet struct {
Result struct {
WalletBalance string `json:"walletBalance"`
Expand Down
1 change: 1 addition & 0 deletions pkg/domain/tv.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ type TVOrder struct {
QTY float64 `gorm:"type:float" json:"qty" binding:"required"`
TP interface{} `gorm:"type:float" json:"tp"`
SL interface{} `gorm:"type:float" json:"sl"`
PL decimal.Decimal `gorm:"type:decimal(10,4)" json:"-"`
}

type Tabler interface {
Expand Down
124 changes: 88 additions & 36 deletions pkg/external/cron.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ package external

import (
"context"
"os"

"github.com/rluisr/tvbit-bot/pkg/domain"

Expand All @@ -35,14 +36,18 @@ func cron() {
Verbose: true,
})

task.Task("0 * * * *", func(ctx context.Context) (int, error) {
/*
Set PnL to an order history.
Update a record if createdAt of saved order history on DB is lower than last closed order(GetClosedOrderLast)'s createdAt.
Why this implementation?
An order_id of open and close is difference, so we can't fetch a closed PnL with open order_id.
*/
task.Task("* * * * *", func(ctx context.Context) (int, error) {
settings, err := tvController.Interactor.TVRepository.GetSettings()
if err != nil {
return 0, err
}

var walletHistories []domain.WalletHistory

for _, setting := range settings {
switch setting.CEX {
case "bybit":
Expand All @@ -52,48 +57,95 @@ func cron() {
APISecretKey: setting.APISecretKey,
})

// USDC
bybitUSDCWallet, err := tvController.Interactor.BybitRepository.GetWalletInfoUSDC()
if err != nil {
return 1, err
}

balance, err := decimal.NewFromString(bybitUSDCWallet.Result.WalletBalance)
if err != nil {
return 1, err
}
totalRPL, err := decimal.NewFromString(bybitUSDCWallet.Result.TotalRPL)
savedPLNullOrders, err := tvController.Interactor.TVRepository.GetPLNullOrders(setting.ID)
if err != nil {
return 1, err
return 0, err
}

walletHistories = append(walletHistories, domain.WalletHistory{
SettingID: setting.ID,
Type: "usdc",
Balance: balance,
TotalRPL: totalRPL,
})
for _, savedOrder := range *savedPLNullOrders {
closedOrder, err := tvController.Interactor.BybitRepository.GetClosedOrderLast(savedOrder.Symbol)
if err != nil {
return 0, err
}

// Deriv USDT
bybitDerivWallet, err := tvController.Interactor.BybitRepository.GetWalletInfoDeriv()
if err != nil {
return 1, err
if savedOrder.CreatedAt.Unix() < closedOrder.Result.Data[0].CreatedAt {
savedOrder.PL = decimal.NewFromFloat(closedOrder.Result.Data[0].ClosedPnl)
err = tvController.Interactor.TVRepository.UpdateOrder(&savedOrder)
if err != nil {
return 0, err
}
break
}
}
walletHistories = append(walletHistories, domain.WalletHistory{
SettingID: setting.ID,
Type: "usdt",
Balance: decimal.NewFromFloat(bybitDerivWallet.Equity),
TotalRPL: decimal.NewFromFloat(bybitDerivWallet.CumRealisedPnl),
})
}
}

err = tvController.Interactor.TVRepository.SaveWalletHistories(walletHistories)
if err != nil {
return 1, err
}
return 0, nil
})

if os.Getenv("SERVER_ENV") != "local" {

/*
Save wallet balance
*/
task.Task("0 * * * *", func(ctx context.Context) (int, error) {
settings, err := tvController.Interactor.TVRepository.GetSettings()
if err != nil {
return 0, err
}

var walletHistories []domain.WalletHistory

for _, setting := range settings {
switch setting.CEX {
case "bybit":
tvController.Bybit(domain.TV{
IsTestNet: setting.IsTestnet,
APIKey: setting.APIKey,
APISecretKey: setting.APISecretKey,
})

// USDC
bybitUSDCWallet, err := tvController.Interactor.BybitRepository.GetWalletInfoUSDC()
if err != nil {
return 1, err
}

balance, err := decimal.NewFromString(bybitUSDCWallet.Result.WalletBalance)
if err != nil {
return 1, err
}
totalRPL, err := decimal.NewFromString(bybitUSDCWallet.Result.TotalRPL)
if err != nil {
return 1, err
}

walletHistories = append(walletHistories, domain.WalletHistory{
SettingID: setting.ID,
Type: "usdc",
Balance: balance,
TotalRPL: totalRPL,
})

// Deriv USDT
bybitDerivWallet, err := tvController.Interactor.BybitRepository.GetWalletInfoDeriv()
if err != nil {
return 1, err
}
walletHistories = append(walletHistories, domain.WalletHistory{
SettingID: setting.ID,
Type: "usdt",
Balance: decimal.NewFromFloat(bybitDerivWallet.Equity),
TotalRPL: decimal.NewFromFloat(bybitDerivWallet.CumRealisedPnl),
})
}
}

err = tvController.Interactor.TVRepository.SaveWalletHistories(walletHistories)
if err != nil {
return 1, err
}
return 0, nil
})
}
task.Run()
}
3 changes: 3 additions & 0 deletions pkg/usecase/interfaces/repositories.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,11 @@ import (

type TVRepository interface {
SaveOrder(domain.TV, *domain.TVOrder) error
UpdateOrder(order *domain.TVOrder) error
GetSetting(apiKey, apiSecretKey string) (*domain.Setting, error)
GetSettings() ([]domain.Setting, error)
SaveWalletHistories([]domain.WalletHistory) error
GetPLNullOrders(settingID uint64) (*[]domain.TVOrder, error)
}

type BybitRepository interface {
Expand All @@ -39,6 +41,7 @@ type BybitRepository interface {
GetWalletInfoDeriv() (*rest.Balance, error)
GetActiveOrderCount(req *domain.TV, positions *[]rest.LinearPosition) int
GetPositions(symbol string) (*[]rest.LinearPosition, error)
GetClosedOrderLast(symbol string) (*domain.BybitLinearClosedPnLResponse, error)
}

type SettingRepository interface {
Expand Down

0 comments on commit cd89b84

Please sign in to comment.