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