Skip to content

Commit

Permalink
bitfinex wallet service and balance
Browse files Browse the repository at this point in the history
  • Loading branch information
dasbd72 committed Mar 1, 2024
1 parent 0bc522d commit 8dd67be
Show file tree
Hide file tree
Showing 8 changed files with 339 additions and 13 deletions.
67 changes: 67 additions & 0 deletions bitfinex/definition.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package bitfinex

import (
"strconv"
"strings"
"time"
)

type (
JSONFloat64 float64
JSONInt64 int64
JSONTime time.Time
)

func (t *JSONFloat64) Float64() float64 { return float64(*t) }

func (t *JSONFloat64) UnmarshalJSON(s []byte) (err error) {
r := strings.Replace(string(s), `"`, ``, -1)
if r == "" {
return
}

q, err := strconv.ParseFloat(r, 64)
if err != nil {
return err
}
*(*float64)(t) = q
return
}

func (t *JSONInt64) Int64() int64 { return int64(*t) }

func (t *JSONInt64) UnmarshalJSON(s []byte) (err error) {
r := strings.Replace(string(s), `"`, ``, -1)
if r == "" {
return
}

q, err := strconv.ParseInt(r, 10, 64)
if err != nil {
return err
}
*(*int64)(t) = q
return
}

func (t *JSONTime) Time() time.Time { return time.Time(*t) }

func (t *JSONTime) UnmarshalJSON(s []byte) (err error) {
r := strings.Replace(string(s), `"`, ``, -1)
if r == "" {
return
}

q, err := strconv.ParseInt(r, 10, 64)
if err != nil {
return err
}
*(*time.Time)(t) = time.UnixMilli(q)
return
}

func (t *JSONTime) MarshalJSON() ([]byte, error) {
return []byte(strconv.FormatInt(time.Time(*t).UnixMilli(), 10)), nil
}

func (t *JSONTime) String() string { return (time.Time)(*t).String() }
183 changes: 183 additions & 0 deletions bitfinex/definition_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
package bitfinex

import (
"strconv"
"testing"
"time"

"github.com/google/go-cmp/cmp"
)

func TestJSONFloat64(t *testing.T) {
tests := []struct {
name string
in string
want float64
wantErr bool
}{
{
name: "zero",
in: "0",
want: 0,
},
{
name: "negative",
in: "-1.0",
want: -1.0,
},
{
name: "empty",
in: "",
want: 0,
},
{
name: "float",
in: "0.10203101249012",
want: 0.10203101249012,
},
{
name: "invalid",
in: "invalid",
wantErr: true,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var f JSONFloat64
err := f.UnmarshalJSON([]byte(test.in))
if test.wantErr {
if err == nil {
t.Fatal("expected error")
}
return
}
if err != nil {
t.Fatal(err)
}
if f.Float64() != test.want {
t.Errorf("expected %f, got %f", test.want, f.Float64())
}
})
}
}

func TestJSONInt64(t *testing.T) {
tests := []struct {
name string
in string
want int64
wantErr bool
}{
{
name: "zero",
in: "0",
want: 0,
},
{
name: "negative",
in: "-1",
want: -1,
},
{
name: "empty",
in: "",
want: 0,
},
{
name: "int",
in: "123",
want: 123,
},
{
name: "invalid",
in: "invalid",
wantErr: true,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var f JSONInt64
err := f.UnmarshalJSON([]byte(test.in))
if test.wantErr {
if err == nil {
t.Fatal("expected error")
}
return
}
if err != nil {
t.Fatal(err)
}
if f.Int64() != test.want {
t.Errorf("expected %d, got %d", test.want, f.Int64())
}
})
}
}

func TestJSONTime(t *testing.T) {
tests := []struct {
name string
in string
want time.Time
wantErr bool
}{
{
name: "zero",
in: "0",
want: time.UnixMilli(0),
},
{
name: "one",
in: "1",
want: time.UnixMilli(1),
},
{
name: "empty",
in: "",
want: time.Time{},
},
{
name: "time",
in: "1694061154503",
want: time.UnixMilli(1694061154503),
},
{
name: "invalid",
in: "invalid",
wantErr: true,
},
}

for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
var f JSONTime
err := f.UnmarshalJSON([]byte(test.in))
if test.wantErr {
if err == nil {
t.Fatal("expected error")
}
return
}
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(f.Time(), test.want); diff != "" {
t.Errorf("unexpected time (-got +want): %s", diff)
}
{
got, err := f.MarshalJSON()
if err != nil {
t.Fatal(err)
}
if diff := cmp.Diff(string(got), strconv.FormatInt(test.want.UnixMilli(), 10)); diff != "" {
t.Errorf("unexpected time (-got +want): %s", diff)
}
}
if diff := cmp.Diff(f.String(), test.want.String()); diff != "" {
t.Errorf("unexpected time (-got +want): %s", diff)
}
})
}
}
42 changes: 39 additions & 3 deletions bitfinex/wallet_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,46 @@ import (
)

type (
GetWalletsResponse [][]interface{}
GetWalletsResponse struct {
Wallet []Wallet `json:"wallet"`
}
Wallet struct {
Type string `json:"type"`
Currency string `json:"currency"`
Balance JSONFloat64 `json:"balance"`
UnsettledInterest JSONFloat64 `json:"unsettled_interest"`
AvailableBalance JSONFloat64 `json:"available_balance"`
LastChange string `json:"last_change"`
LastChangeMetadata interface{} `json:"last_change_metadata"`
}
)

func (c *Client) GetWalletStatus(ctx context.Context, opts ...RequestOption) (*GetWalletsResponse, error) {
func (data *GetWalletsResponse) FromRaw(raw []byte) error {
container := [][]interface{}{}
err := json.Unmarshal(raw, &container)
if err != nil {
return err
}
for _, v := range container {
for i, vv := range v {
if vv == nil {
v[i] = ""
}
}
data.Wallet = append(data.Wallet, Wallet{
Type: v[0].(string),
Currency: v[1].(string),
Balance: JSONFloat64(v[2].(float64)),
UnsettledInterest: JSONFloat64(v[3].(float64)),
AvailableBalance: JSONFloat64(v[4].(float64)),
LastChange: v[5].(string),
LastChangeMetadata: v[6],
})
}
return nil
}

func (c *Client) GetWallets(ctx context.Context, opts ...RequestOption) (*GetWalletsResponse, error) {
res, err := c.CallAPI(ctx, Request_builder{
Method: http.MethodPost,
Endpoint: "/auth/r/wallets",
Expand All @@ -20,7 +56,7 @@ func (c *Client) GetWalletStatus(ctx context.Context, opts ...RequestOption) (*G
return nil, err
}
data := &GetWalletsResponse{}
err = json.Unmarshal(res, data)
err = data.FromRaw(res)
if err != nil {
return nil, err
}
Expand Down
16 changes: 12 additions & 4 deletions cmd/ccy-cli/balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"os"

"github.com/dasbd72/go-exchange-sdk/binance"
"github.com/dasbd72/go-exchange-sdk/bitfinex"
"github.com/dasbd72/go-exchange-sdk/manager"
"github.com/dasbd72/go-exchange-sdk/max"
"github.com/dasbd72/go-exchange-sdk/okx"
Expand All @@ -29,10 +30,17 @@ func Balance(cmd *cobra.Command, args []string) error {
return nil
}

c := manager.NewClient(
binance.NewClient(binanceApiKey, binanceApiSecret),
okx.NewClient(okxApiKey, okxApiSecret, okxPassphrase),
)
bitfinexApiKey := os.Getenv("BFX_API_KEY")
bitfinexApiSecret := os.Getenv("BFX_API_SECRET")
if bitfinexApiKey == "" || bitfinexApiSecret == "" {
return nil
}

c := manager.Client_builder{
BinanceClient: binance.NewClient(binanceApiKey, binanceApiSecret),
OkxClient: okx.NewClient(okxApiKey, okxApiSecret, okxPassphrase),
BitfinexClient: bitfinex.NewClient(bitfinexApiKey, bitfinexApiSecret),
}.Build()

balance, err := c.GetBalance(ctx)
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion cmd/ccy-cli/bitfinex.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func Bitfinex(cmd *cobra.Command, args []string) {
// SecType: bitfinex.SecTypePrivate,
// Params: map[string]interface{}{},
// }.Build())
data, err := c.GetWalletStatus(ctx)
data, err := c.GetWallets(ctx)
if err != nil {
log.Fatal(err)
}
Expand Down
20 changes: 20 additions & 0 deletions manager/balance.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,26 @@ func (c *Client) GetBalance(ctx context.Context) (*Balance, error) {
totalBalanceUsdt += sum
return nil
},
func() error {
if c.bitfinexClient == nil {
// Skip if bitfinex client is not set
return nil
}
sum := 0.0
// Get balance from wallet
res, err := c.bitfinexClient.GetWallets(ctx)
if err != nil {
return err
}
for _, w := range res.Wallet {
if w.Currency == "USD" || w.Currency == "UST" {
sum += w.Balance.Float64()
}
}

totalBalanceUsdt += sum
return nil
},
func() error {
usdtToTWD, err := max.GetUsdtToTWD()
if err != nil {
Expand Down
Loading

0 comments on commit 8dd67be

Please sign in to comment.