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