]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front.git/blame - markets/poloniex.go
Set poloniex client timeout to 20 seconds.
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front.git] / markets / poloniex.go
CommitLineData
2f91f20a 1package markets
2
3import (
4 "fmt"
5 "strings"
6
7 "github.com/jloup/poloniex"
8 "github.com/jloup/utils"
9 "github.com/shopspring/decimal"
10)
11
12var (
13 ErrorFlagCounter utils.Counter = 0
14 CurrencyPairNotInTicker = utils.InitFlag(&ErrorFlagCounter, "CurrencyPairNotInTicker")
15 InvalidCredentials = utils.InitFlag(&ErrorFlagCounter, "InvalidCredentials")
908ee2dd 16 IPRestricted = utils.InitFlag(&ErrorFlagCounter, "IPRestricted")
2f91f20a 17)
18
c086df91 19const defaultTimeout = 10
20
2f91f20a 21func poloniexInvalidCredentialsError(err error) bool {
22 if err == nil {
23 return false
24 }
299b6b6d 25 return strings.Contains(err.Error(), "Invalid API key/secret pair") || strings.Contains(err.Error(), "Set the API KEY and API SECRET")
2f91f20a 26}
27
908ee2dd 28func poloniexRestrictedIPError(err error) bool {
29 if err == nil {
30 return false
31 }
32 return strings.Contains(err.Error(), "Permission denied")
33}
34
2f91f20a 35type CurrencyPair struct {
36 Name string
37 Rate decimal.Decimal
38}
39
40type Poloniex struct {
41 TickerCache map[string]CurrencyPair
42
43 publicClient *poloniex.Poloniex
44 updateTickerChan chan CurrencyPair
45}
46
47func NewPoloniex() *Poloniex {
c086df91 48 client, _ := poloniex.NewClient("", "", defaultTimeout)
2f91f20a 49
50 return &Poloniex{
51 TickerCache: make(map[string]CurrencyPair),
52 updateTickerChan: nil,
53 publicClient: client,
54 }
55}
56
c086df91 57func (p *Poloniex) TestCredentials(apiKey, apiSecret string, timeout int32) error {
58 client, _ := poloniex.NewClient(apiKey, apiSecret, timeout)
24e47979 59
60 _, err := client.TradeReturnDepositAdresses()
61
62 if poloniexInvalidCredentialsError(err) {
63 return utils.Error{InvalidCredentials, "invalid poloniex credentials"}
64 }
65
66 if poloniexRestrictedIPError(err) {
67 return utils.Error{IPRestricted, "IP restricted api key"}
68 }
69
299b6b6d 70 return err
24e47979 71}
72
c086df91 73func (p *Poloniex) GetBalance(apiKey, apiSecret string, timeout int32) (Summary, error) {
74 client, _ := poloniex.NewClient(apiKey, apiSecret, timeout)
50c6eea6 75 var summary Summary
2f91f20a 76
77 accounts, err := client.TradeReturnAvailableAccountBalances()
78 if poloniexInvalidCredentialsError(err) {
50c6eea6 79 return Summary{}, utils.Error{InvalidCredentials, "invalid poloniex credentials"}
2f91f20a 80 }
81
50c6eea6 82 if poloniexRestrictedIPError(err) {
83 return Summary{}, utils.Error{IPRestricted, "IP restricted api key"}
84 }
85
86 if err != nil {
87 return Summary{}, err
88 }
89
90 positions, err := client.TradeGetMarginPosition()
2f91f20a 91 if err != nil {
50c6eea6 92 return Summary{}, err
2f91f20a 93 }
94
50c6eea6 95 marginAccount, err := client.TradeReturnMarginAccountSummary()
96 if err != nil {
97 return Summary{}, err
2f91f20a 98 }
99
50c6eea6 100 summary.Balances = make(map[string]Balance)
101 for currency, amount := range accounts.Exchange {
102 balance := summary.Balances[currency]
103 balance.Amount = balance.Amount.Add(amount)
104
105 summary.Balances[currency] = balance
2f91f20a 106 }
107
50c6eea6 108 summary.BTCValue, err = p.ComputeAccountBalanceValue(summary.Balances)
109 if err != nil {
110 return Summary{}, err
111 }
112
113 for currencyPair, position := range positions {
114 if position.Type == "none" {
115 continue
116 }
117 currency := currencyPair[4:]
118 balance := summary.Balances[currency]
119 balance.Amount = balance.Amount.Add(position.Amount)
120 balance.BTCValue = balance.BTCValue.Add(position.Total.Add(position.PlusValue))
121
122 summary.Balances[currency] = balance
123 }
124
125 summary.BTCValue = summary.BTCValue.Add(marginAccount.NetValue)
126
127 return summary, nil
2f91f20a 128}
129
50c6eea6 130func (p *Poloniex) ComputeAccountBalanceValue(account map[string]Balance) (decimal.Decimal, error) {
2f91f20a 131 var total decimal.Decimal
132
50c6eea6 133 for currency, balance := range account {
134 pair, err := p.GetCurrencyPair("BTC", currency)
2f91f20a 135 if err != nil {
136 return decimal.Zero, err
137 }
138
50c6eea6 139 total = total.Add(balance.Amount.Mul(pair.Rate))
140 balance.BTCValue = balance.BTCValue.Add(balance.Amount.Mul(pair.Rate))
141 account[currency] = balance
2f91f20a 142 }
143
144 return total, nil
145}
146
147func (p *Poloniex) GetCurrencyPair(curr1, curr2 string) (CurrencyPair, error) {
148 pairName := fmt.Sprintf("%s_%s", curr1, curr2)
149 var err error
150
151 if curr1 == curr2 {
152 return CurrencyPair{pairName, decimal.NewFromFloat(1.0)}, nil
153 }
154
155 pair, ok := p.TickerCache[pairName]
156 if !ok {
157 pair, err = p.fetchTicker(curr1, curr2)
158
159 if utils.ErrIs(err, CurrencyPairNotInTicker) {
160 // try to invert an existing ticker.
161 pair, err = p.fetchTicker(curr2, curr1)
162 if err != nil {
163 return CurrencyPair{}, err
164 }
165
166 return CurrencyPair{pairName, decimal.NewFromFloat(1.0).Div(pair.Rate)}, nil
167 }
168
169 if err != nil {
170 return CurrencyPair{}, err
171 }
172 }
173
174 return pair, nil
175}
176
177func (p *Poloniex) fetchTicker(curr1, curr2 string) (CurrencyPair, error) {
178 tickers, err := p.publicClient.PubReturnTickers()
179 if err != nil {
180 return CurrencyPair{}, err
181 }
182
183 pairName := fmt.Sprintf("%s_%s", curr1, curr2)
184
185 if ticker, ok := tickers[pairName]; ok {
186 pair := CurrencyPair{Name: pairName, Rate: ticker.Last}
187
188 if p.updateTickerChan != nil {
189 p.updateTickerChan <- pair
190 }
191
192 return pair, nil
193 }
194
195 return CurrencyPair{}, utils.Error{CurrencyPairNotInTicker, fmt.Sprintf("%s_%s not in ticker", curr1, curr2)}
196}
197
198func (p *Poloniex) StartTicker() error {
199 stream, err := poloniex.NewWSClient()
200 if err != nil {
201 return err
202 }
203
204 err = stream.SubscribeTicker()
205 if err != nil {
206 return err
207 }
208
209 p.updateTickerChan = make(chan CurrencyPair)
210
211 for {
212 quit := false
213 select {
214 case data, ok := <-stream.Subs["ticker"]:
215 if !ok {
216 quit = true
217 } else {
218 ticker := data.(poloniex.WSTicker)
219 if ticker.CurrencyPair == "USDT_BTC" || true {
220 }
221 p.TickerCache[ticker.CurrencyPair] = CurrencyPair{Name: ticker.CurrencyPair, Rate: decimal.NewFromFloat(ticker.Last)}
222 }
223
224 case pair, ok := <-p.updateTickerChan:
225 if !ok {
226 quit = true
227 } else {
228 p.TickerCache[pair.Name] = pair
229 }
230 }
231 if quit {
232 p.updateTickerChan = nil
233 break
234 }
235 }
236
237 return nil
238}