Skip to content

Commit

Permalink
Bitfinex: Implement leaderboard API (thrasher-corp#458)
Browse files Browse the repository at this point in the history
* Bitfinex: Implement leaderboard API

* Expand test coverage

* fix grammer
  • Loading branch information
thrasher- authored Mar 4, 2020
1 parent b686cf2 commit ba07c86
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 5 deletions.
73 changes: 68 additions & 5 deletions exchanges/bitfinex/bitfinex.go
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ const (
bitfinexMarginClose = "funding/close"
bitfinexLendbook = "lendbook/"
bitfinexLends = "lends/"
bitfinexLeaderboard = "rankings"

// Version 2 API endpoints
bitfinexAPIVersion2 = "/v2/"
Expand Down Expand Up @@ -506,11 +507,73 @@ func (b *Bitfinex) GetLiquidationFeed() error {
return common.ErrNotYetImplemented
}

// GetLeaderBoard returns leaderboard standings for unrealized
// profit (period delta), unrealized profit (inception), volume, and realized
// profit.
func (b *Bitfinex) GetLeaderBoard() error {
return common.ErrNotYetImplemented
// GetLeaderboard returns leaderboard standings for unrealized profit (period
// delta), unrealized profit (inception), volume, and realized profit.
// Allowed key values: "plu_diff" for unrealized profit (period delta), "plu"
// for unrealized profit (inception); "vol" for volume; "plr" for realized
// profit
// Allowed time frames are 3h, 1w and 1M
// Allowed symbols are trading pairs (e.g. tBTCUSD, tETHUSD and tGLOBAL:USD)
func (b *Bitfinex) GetLeaderboard(key, timeframe, symbol string, sort, limit int, start, end string) ([]LeaderboardEntry, error) {
validLeaderboardKey := func(input string) bool {
switch input {
case LeaderboardUnrealisedProfitPeriodDelta,
LeaderboardUnrealisedProfitInception,
LeaderboardVolume,
LeaderbookRealisedProfit:
return true
default:
return false
}
}

if !validLeaderboardKey(key) {
return nil, errors.New("invalid leaderboard key")
}

path := fmt.Sprintf("%s/%s:%s:%s/hist", b.API.Endpoints.URL+bitfinexAPIVersion2+bitfinexLeaderboard,
key,
timeframe,
symbol)
vals := url.Values{}
if sort != 0 {
vals.Set("sort", strconv.Itoa(sort))
}
if limit != 0 {
vals.Set("limit", strconv.Itoa(limit))
}
if start != "" {
vals.Set("start", start)
}
if end != "" {
vals.Set("end", end)
}
path = common.EncodeURLValues(path, vals)
var resp []interface{}
if err := b.SendHTTPRequest(path, &resp, leaderBoardReqRate); err != nil {
return nil, err
}

parseTwitterHandle := func(i interface{}) string {
r, ok := i.(string)
if !ok {
return ""
}
return r
}

var result []LeaderboardEntry
for x := range resp {
r := resp[x].([]interface{})
result = append(result, LeaderboardEntry{
Timestamp: time.Unix(0, int64(r[0].(float64))*int64(time.Millisecond)),
Username: r[2].(string),
Ranking: int(r[3].(float64)),
Value: r[6].(float64),
TwitterHandle: parseTwitterHandle(r[9]),
})
}
return result, nil
}

// GetMarketAveragePrice calculates the average execution price for Trading or
Expand Down
35 changes: 35 additions & 0 deletions exchanges/bitfinex/bitfinex_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,41 @@ func TestGetCandles(t *testing.T) {
}
}

func TestGetLeaderboard(t *testing.T) {
t.Parallel()
// Test invalid key
_, err := b.GetLeaderboard("", "", "", 0, 0, "", "")
if err == nil {
t.Error("an error should have been thrown for an invalid key")
}
// Test default
_, err = b.GetLeaderboard(LeaderboardUnrealisedProfitInception,
"1M",
"tGLOBAL:USD",
0,
0,
"",
"")
if err != nil {
t.Fatal(err)
}
// Test params
var result []LeaderboardEntry
result, err = b.GetLeaderboard(LeaderboardUnrealisedProfitInception,
"1M",
"tGLOBAL:USD",
-1,
1000,
"1582695181661",
"1583299981661")
if err != nil {
t.Fatal(err)
}
if len(result) == 0 {
t.Error("should have retrieved leaderboard data")
}
}

func TestGetAccountFees(t *testing.T) {
if !areTestAPIKeysSet() {
t.SkipNow()
Expand Down
19 changes: 19 additions & 0 deletions exchanges/bitfinex/bitfinex_types.go
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package bitfinex

import "time"

// AcceptedOrderType defines the accepted market types, exchange strings denote
// non-contract order types.
var AcceptedOrderType = []string{"market", "limit", "stop", "trailing-stop",
Expand Down Expand Up @@ -394,6 +396,23 @@ type Candle struct {
Volume float64
}

// Leaderboard keys
const (
LeaderboardUnrealisedProfitPeriodDelta = "plu_diff"
LeaderboardUnrealisedProfitInception = "plu"
LeaderboardVolume = "vol"
LeaderbookRealisedProfit = "plr"
)

// LeaderboardEntry holds leaderboard data
type LeaderboardEntry struct {
Timestamp time.Time
Username string
Ranking int
Value float64
TwitterHandle string
}

// WebsocketTicker holds ticker information
type WebsocketTicker struct {
Bid float64
Expand Down

0 comments on commit ba07c86

Please sign in to comment.