-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathfetch.go
162 lines (139 loc) · 6.83 KB
/
fetch.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
package bfldb
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
)
const (
defaultApiBase = "https://www.binance.com/bapi/futures"
)
var defaultHeaders = map[string]string{
"authority": "www.binance.com",
"accept": "*/*",
"accept-language": "en-US,en;q=0.8",
"cache-control": "no-cache",
"clienttype": "web",
"content-type": "application/json",
"lang": "en",
"origin": "https://www.binance.com",
"pragma": "no-cache",
"sec-fetch-dest": "empty",
"sec-fetch-mode": "cors",
"sec-fetch-site": "same-origin",
"sec-gpc": "1",
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
}
// LdbAPIRes represents a response from Binance's Futures LDB API.
type LdbAPIRes[T UserPositionData | UserBaseInfo | []NicknameDetails] struct {
Success bool `json:"success"` // Whether or not the request was successful
Code string `json:"code"` // Error code, "000000" means success
Message string `json:"message"` // Error message
Data T `json:"data"` // Data
MessageDetail interface{} `json:"messageDetail"` // ???
}
// ************************************************** /getOtherPosition **************************************************
// UserPositionData represents data about user's positions.
type UserPositionData struct {
OtherPositionRetList []rawPosition `json:"otherPositionRetList"` // List of positions
UpdateTimeStamp int64 `json:"updateTimeStamp"` // Timestamp
UpdateTime []int `json:"updateTime"` // Time array in the format of [YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, ... ]
}
// rawPosition represent details of an individual position returned.
type rawPosition struct {
Symbol string `json:"symbol"` // Position symbol
EntryPrice float64 `json:"entryPrice"` // Entry price
MarkPrice float64 `json:"markPrice"` // Mark Price
Pnl float64 `json:"pnl"` // PNL
Roe float64 `json:"roe"` // ROE
Amount float64 `json:"amount"` // Position size
UpdateTimeStamp int64 `json:"updateTimeStamp"` // Timestamp
UpdateTime []int `json:"updateTime"` // Time array in the format of [YEAR, MONTH, DAY, HOUR, MINUTE, SECOND, ... ]
Yellow bool `json:"yellow"` // ???
TradeBefore bool `json:"tradeBefore"` // ???
Leverage int `json:"leverage"` // leverage used
}
// GetOtherPosition gets all currently open positions for an user.
func GetOtherPosition(ctx context.Context, UUID string) (LdbAPIRes[UserPositionData], error) {
return NewUser(UUID).GetOtherPosition(ctx)
}
// GetOtherPosition gets all currently open positions for an user.
func (u *User) GetOtherPosition(ctx context.Context) (LdbAPIRes[UserPositionData], error) {
var res LdbAPIRes[UserPositionData]
return res, doPost(ctx, u.client, u.APIBase()+"/v1/public/future/leaderboard", "/getOtherPosition", u.Headers(), strings.NewReader(fmt.Sprintf("{\"encryptedUid\":\"%s\",\"tradeType\":\"PERPETUAL\"}", u.UID)), &res)
}
// ************************************************** /getOtherLeaderboardBaseInfo **************************************************
// UserBaseInfo represents user's data.
type UserBaseInfo struct {
NickName string `json:"nickName"` // Nickname
UserPhotoURL string `json:"userPhotoUrl"` // Photo URL
PositionShared bool `json:"positionShared"` // true if user is sharing their USD positions, false otherwise
DeliveryPositionShared bool `json:"deliveryPositionShared"` // true if user is sharing their COIN positions, false otherwise
FollowingCount int `json:"followingCount"` // How many people user follows
FollowerCount int `json:"followerCount"` // How many people follow user
TwitterURL string `json:"twitterUrl"` // Twitter URL
Introduction string `json:"introduction"` // Introduction (profile description)
TwShared bool `json:"twShared"` // Sharing their TraderWagon
IsTwTrader bool `json:"isTwTrader"` // Connected with TraderWagon
OpenID interface{} `json:"openId"` // ???
}
// GetOtherLeaderboardBaseInfo gets information for the uuid passed in.
func GetOtherLeaderboardBaseInfo(ctx context.Context, UUID string) (LdbAPIRes[UserBaseInfo], error) {
return NewUser(UUID).GetOtherLeaderboardBaseInfo(ctx)
}
// GetOtherLeaderboardBaseInfo gets information about an user.
func (u *User) GetOtherLeaderboardBaseInfo(ctx context.Context) (LdbAPIRes[UserBaseInfo], error) {
var res LdbAPIRes[UserBaseInfo]
return res, doPost(ctx, u.client, u.APIBase()+"/v2/public/future/leaderboard", "/getOtherLeaderboardBaseInfo", u.Headers(), strings.NewReader(fmt.Sprintf("{\"encryptedUid\":\"%s\"}", u.UID)), &res)
}
// ************************************************** /searchNickname **************************************************
type NicknameDetails struct {
EncryptedUID string `json:"encryptedUid"`
Nickname string `json:"nickname"`
FollowerCount int `json:"followerCount"`
UserPhotoURL string `json:"userPhotoUrl"`
}
// SearchNickname searches for a nickname.
func SearchNickname(ctx context.Context, nickname string) (LdbAPIRes[[]NicknameDetails], error) {
var res LdbAPIRes[[]NicknameDetails]
return res, doPost(ctx, http.DefaultClient, defaultApiBase+"/v1/public/future/leaderboard", "/searchNickname", defaultHeaders, strings.NewReader(fmt.Sprintf("{\"nickname\":\"%s\"}", nickname)), &res)
}
// ************************************************** Unexported **************************************************
// doPost POSTs the data passed in to the path on Binance's leaderboard API.
func doPost(ctx context.Context, c *http.Client, endpoint, path string, headers map[string]string, data io.Reader, resPtr any) error {
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
req, err := http.NewRequestWithContext(
ctx,
"POST",
endpoint+path,
data,
)
if err != nil {
return fmt.Errorf("failed to create request: %w", err)
}
for k, v := range headers {
req.Header.Add(k, v)
}
res, err := c.Do(req)
if err != nil {
return fmt.Errorf("failed to do request: %w", err)
}
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("failed to read request body: %w", err)
}
// all of the endpoints are expected to return 200
if res.StatusCode != http.StatusOK {
return BadStatusError{
Status: res.Status,
StatusCode: res.StatusCode,
Body: body,
}
}
return json.Unmarshal(body, resPtr)
}