/
base_interface.go
157 lines (129 loc) · 3.94 KB
/
base_interface.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
package base
import (
"errors"
"fmt"
"strings"
"sync"
"github.com/thrasher-corp/gocryptotrader/common"
"github.com/thrasher-corp/gocryptotrader/log"
)
// IFXProvider enforces standard functions for all foreign exchange providers
// supported in GoCryptoTrader
type IFXProvider interface {
Setup(config Settings) error
GetRates(baseCurrency, symbols string) (map[string]float64, error)
GetName() string
IsEnabled() bool
IsPrimaryProvider() bool
GetSupportedCurrencies() ([]string, error)
}
// FXHandler defines a full suite of FX data providers with failure backup with
// unsupported currency shunt procedure
type FXHandler struct {
Primary Provider
Support []Provider
mtx sync.Mutex
}
// Provider defines a singular foreign exchange provider with its supported
// currencies to cross reference request currencies and if not supported shunt
// request traffic to and from other providers so that we can maintain full
// currency list integration
type Provider struct {
Provider IFXProvider
SupportedCurrencies []string
}
// GetNewRate access rates by predetermined logic based on how a provider
// handles requests
func (p *Provider) GetNewRate(base string, currencies []string) (map[string]float64, error) {
if !p.Provider.IsEnabled() {
return nil, fmt.Errorf("provider %s is not enabled",
p.Provider.GetName())
}
switch p.Provider.GetName() {
case "ExchangeRates":
return p.Provider.GetRates(base, "") // Zero value to get all rates
default:
return p.Provider.GetRates(base, strings.Join(currencies, ","))
}
}
// CheckCurrencies cross references supplied currencies with exchange supported
// currencies, if there are any currencies not supported it returns a list
// to pass on to the next provider
func (p Provider) CheckCurrencies(currencies []string) []string {
var spillOver []string
for _, c := range currencies {
if !common.StringDataCompareInsensitive(p.SupportedCurrencies, c) {
spillOver = append(spillOver, c)
}
}
return spillOver
}
// GetCurrencyData returns currency data from enabled FX providers
func (f *FXHandler) GetCurrencyData(baseCurrency string, currencies []string) (map[string]float64, error) {
var fullRange = currencies
if !common.StringDataCompareInsensitive(currencies, baseCurrency) {
fullRange = append(fullRange, baseCurrency)
}
f.mtx.Lock()
defer f.mtx.Unlock()
if f.Primary.Provider == nil {
return nil, errors.New("primary foreign exchange provider details not set")
}
shunt := f.Primary.CheckCurrencies(fullRange)
rates, err := f.Primary.GetNewRate(baseCurrency, currencies)
if err != nil {
return f.backupGetRate(baseCurrency, currencies)
}
if len(shunt) != 0 {
return rates, nil
}
rateNew, err := f.backupGetRate(baseCurrency, shunt)
if err != nil {
log.Warnf(log.Global, "%s and subsequent providers, failed to update rate map for currencies %v %v",
f.Primary.Provider.GetName(),
shunt,
err)
}
for key, val := range rateNew {
rates[key] = val
}
return rates, nil
}
// backupGetRate uses the currencies that are supported and falls through, and
// errors when unsupported currency found
func (f *FXHandler) backupGetRate(base string, currencies []string) (map[string]float64, error) {
if f.Support == nil {
return nil, errors.New("no supporting foreign exchange providers set")
}
var shunt []string
rate := make(map[string]float64)
for i := range f.Support {
if len(shunt) != 0 {
shunt = f.Support[i].CheckCurrencies(shunt)
newRate, err := f.Support[i].GetNewRate(base, shunt)
if err != nil {
continue
}
for k, v := range newRate {
rate[k] = v
}
if len(shunt) != 0 {
continue
}
return rate, nil
}
shunt = f.Support[i].CheckCurrencies(currencies)
newRate, err := f.Support[i].GetNewRate(base, currencies)
if err != nil {
continue
}
for k, v := range newRate {
rate[k] = v
}
if len(shunt) != 0 {
continue
}
return rate, nil
}
return nil, fmt.Errorf("currencies %s not supported", shunt)
}