]> git.immae.eu Git - perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front.git/blob - markets/poloniex.go
Poloniex connection.
[perso/Immae/Projets/Cryptomonnaies/Cryptoportfolio/Front.git] / markets / poloniex.go
1 package markets
2
3 import (
4 "fmt"
5 "strings"
6
7 "github.com/jloup/poloniex"
8 "github.com/jloup/utils"
9 "github.com/shopspring/decimal"
10 )
11
12 var (
13 ErrorFlagCounter utils.Counter = 0
14 CurrencyPairNotInTicker = utils.InitFlag(&ErrorFlagCounter, "CurrencyPairNotInTicker")
15 InvalidCredentials = utils.InitFlag(&ErrorFlagCounter, "InvalidCredentials")
16 )
17
18 func poloniexInvalidCredentialsError(err error) bool {
19 if err == nil {
20 return false
21 }
22 return strings.Contains(err.Error(), "Invalid API key/secret pair")
23 }
24
25 type CurrencyPair struct {
26 Name string
27 Rate decimal.Decimal
28 }
29
30 type Poloniex struct {
31 TickerCache map[string]CurrencyPair
32
33 publicClient *poloniex.Poloniex
34 updateTickerChan chan CurrencyPair
35 }
36
37 func NewPoloniex() *Poloniex {
38 client, _ := poloniex.NewClient("", "")
39
40 return &Poloniex{
41 TickerCache: make(map[string]CurrencyPair),
42 updateTickerChan: nil,
43 publicClient: client,
44 }
45 }
46
47 func (p *Poloniex) GetBalance(apiKey, apiSecret string) (map[string]decimal.Decimal, error) {
48 client, _ := poloniex.NewClient(apiKey, apiSecret)
49
50 accounts, err := client.TradeReturnAvailableAccountBalances()
51 if poloniexInvalidCredentialsError(err) {
52 return nil, utils.Error{InvalidCredentials, "invalid poloniex credentials"}
53 }
54
55 if err != nil {
56 return nil, err
57 }
58
59 balances := make(map[string]decimal.Decimal)
60 for currency, balance := range accounts.Margin {
61 balances[currency] = balances[currency].Add(balance)
62 }
63
64 for currency, balance := range accounts.Exchange {
65 balances[currency] = balances[currency].Add(balance)
66 }
67
68 return balances, nil
69 }
70
71 func (p *Poloniex) ComputeAccountBalanceValue(account map[string]decimal.Decimal, baseCurrency string) (decimal.Decimal, error) {
72 var total decimal.Decimal
73
74 for currency, amount := range account {
75 pair, err := p.GetCurrencyPair(baseCurrency, currency)
76 if err != nil {
77 return decimal.Zero, err
78 }
79
80 total = total.Add(amount.Mul(pair.Rate))
81 }
82
83 return total, nil
84 }
85
86 func (p *Poloniex) GetCurrencyPair(curr1, curr2 string) (CurrencyPair, error) {
87 pairName := fmt.Sprintf("%s_%s", curr1, curr2)
88 var err error
89
90 if curr1 == curr2 {
91 return CurrencyPair{pairName, decimal.NewFromFloat(1.0)}, nil
92 }
93
94 pair, ok := p.TickerCache[pairName]
95 if !ok {
96 pair, err = p.fetchTicker(curr1, curr2)
97
98 if utils.ErrIs(err, CurrencyPairNotInTicker) {
99 // try to invert an existing ticker.
100 pair, err = p.fetchTicker(curr2, curr1)
101 if err != nil {
102 return CurrencyPair{}, err
103 }
104
105 return CurrencyPair{pairName, decimal.NewFromFloat(1.0).Div(pair.Rate)}, nil
106 }
107
108 if err != nil {
109 return CurrencyPair{}, err
110 }
111 }
112
113 return pair, nil
114 }
115
116 func (p *Poloniex) fetchTicker(curr1, curr2 string) (CurrencyPair, error) {
117 tickers, err := p.publicClient.PubReturnTickers()
118 if err != nil {
119 return CurrencyPair{}, err
120 }
121
122 pairName := fmt.Sprintf("%s_%s", curr1, curr2)
123
124 if ticker, ok := tickers[pairName]; ok {
125 pair := CurrencyPair{Name: pairName, Rate: ticker.Last}
126
127 if p.updateTickerChan != nil {
128 p.updateTickerChan <- pair
129 }
130
131 return pair, nil
132 }
133
134 return CurrencyPair{}, utils.Error{CurrencyPairNotInTicker, fmt.Sprintf("%s_%s not in ticker", curr1, curr2)}
135 }
136
137 func (p *Poloniex) StartTicker() error {
138 stream, err := poloniex.NewWSClient()
139 if err != nil {
140 return err
141 }
142
143 err = stream.SubscribeTicker()
144 if err != nil {
145 return err
146 }
147
148 p.updateTickerChan = make(chan CurrencyPair)
149
150 for {
151 quit := false
152 select {
153 case data, ok := <-stream.Subs["ticker"]:
154 if !ok {
155 quit = true
156 } else {
157 ticker := data.(poloniex.WSTicker)
158 if ticker.CurrencyPair == "USDT_BTC" || true {
159 }
160 p.TickerCache[ticker.CurrencyPair] = CurrencyPair{Name: ticker.CurrencyPair, Rate: decimal.NewFromFloat(ticker.Last)}
161 }
162
163 case pair, ok := <-p.updateTickerChan:
164 if !ok {
165 quit = true
166 } else {
167 p.TickerCache[pair.Name] = pair
168 }
169 }
170 if quit {
171 p.updateTickerChan = nil
172 break
173 }
174 }
175
176 return nil
177 }