mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-30 07:26:46 +00:00
* poloniex: websocket update with debug output and currency tracking system * linter: fix issues * nits: Addr * poloniex: govet fix * nits: addr * Bittrex: Fix fee test
236 lines
5.9 KiB
Go
236 lines
5.9 KiB
Go
package poloniex
|
|
|
|
import (
|
|
"errors"
|
|
"strconv"
|
|
"sync"
|
|
|
|
"github.com/thrasher-corp/gocryptotrader/currency"
|
|
)
|
|
|
|
// CurrencyDetails stores a map of currencies associated with their ID
|
|
type CurrencyDetails struct {
|
|
pairs map[float64]PairSummaryInfo
|
|
codes map[float64]CodeSummaryInfo
|
|
// Mutex used for future when we will periodically update this table every
|
|
// week or so in production
|
|
m sync.RWMutex
|
|
}
|
|
|
|
// PairSummaryInfo defines currency pair information
|
|
type PairSummaryInfo struct {
|
|
Pair currency.Pair
|
|
IsFrozen bool
|
|
PostOnly bool
|
|
}
|
|
|
|
// CodeSummaryInfo defines currency information
|
|
type CodeSummaryInfo struct {
|
|
Currency currency.Code
|
|
WithdrawalTXFee float64
|
|
MinimumConfirmations int64
|
|
DepositAddress string
|
|
WithdrawalDepositDisabled bool
|
|
Frozen bool
|
|
}
|
|
|
|
var (
|
|
errCannotLoadNoData = errors.New("cannot load websocket currency data as data is nil")
|
|
errNoDepositAddress = errors.New("no public deposit address for currency")
|
|
errPairMapIsNil = errors.New("cannot get currency pair, map is nil")
|
|
errCodeMapIsNil = errors.New("cannot get currency code, map is nil")
|
|
errCurrencyNotFoundInMap = errors.New("currency not found")
|
|
)
|
|
|
|
// loadPairs loads currency pair associations with unique identifiers from
|
|
// ticker data map
|
|
func (w *CurrencyDetails) loadPairs(data map[string]Ticker) error {
|
|
if data == nil {
|
|
return errCannotLoadNoData
|
|
}
|
|
w.m.Lock()
|
|
defer w.m.Unlock()
|
|
for k, v := range data {
|
|
pair, err := currency.NewPairFromString(k)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if w.pairs == nil {
|
|
w.pairs = make(map[float64]PairSummaryInfo)
|
|
}
|
|
w.pairs[v.ID] = PairSummaryInfo{
|
|
Pair: pair,
|
|
IsFrozen: v.IsFrozen == 1,
|
|
PostOnly: v.PostOnly == 1,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// loadCodes loads currency codes from currency map
|
|
func (w *CurrencyDetails) loadCodes(data map[string]Currencies) error {
|
|
if data == nil {
|
|
return errCannotLoadNoData
|
|
}
|
|
w.m.Lock()
|
|
defer w.m.Unlock()
|
|
for k, v := range data {
|
|
if v.Delisted == 1 {
|
|
continue
|
|
}
|
|
|
|
if w.codes == nil {
|
|
w.codes = make(map[float64]CodeSummaryInfo)
|
|
}
|
|
|
|
w.codes[v.ID] = CodeSummaryInfo{
|
|
Currency: currency.NewCode(k),
|
|
WithdrawalTXFee: v.TxFee,
|
|
MinimumConfirmations: v.MinConfirmations,
|
|
DepositAddress: v.DepositAddress,
|
|
WithdrawalDepositDisabled: v.WithdrawalDepositDisabled == 1,
|
|
Frozen: v.Frozen == 1,
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetPair returns a currency pair by its ID
|
|
func (w *CurrencyDetails) GetPair(id float64) (currency.Pair, error) {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
if w.pairs == nil {
|
|
return currency.Pair{}, errPairMapIsNil
|
|
}
|
|
|
|
p, ok := w.pairs[id]
|
|
if ok {
|
|
return p.Pair, nil
|
|
}
|
|
|
|
// This is here so we can still log an order with the ID as the currency
|
|
// pair which you can then cross reference later with the exchange ID list,
|
|
// rather than error out.
|
|
op, err := currency.NewPairFromString(strconv.FormatFloat(id, 'f', -1, 64))
|
|
if err != nil {
|
|
return op, err
|
|
}
|
|
return op, errIDNotFoundInPairMap
|
|
}
|
|
|
|
// GetCode returns a currency code by its ID
|
|
func (w *CurrencyDetails) GetCode(id float64) (currency.Code, error) {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
if w.codes == nil {
|
|
return currency.Code{}, errCodeMapIsNil
|
|
}
|
|
c, ok := w.codes[id]
|
|
if ok {
|
|
return c.Currency, nil
|
|
}
|
|
return currency.Code{}, errIDNotFoundInCodeMap
|
|
}
|
|
|
|
// GetWithdrawalTXFee returns withdrawal transaction fee for the currency
|
|
func (w *CurrencyDetails) GetWithdrawalTXFee(c currency.Code) (float64, error) {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
if w.codes == nil {
|
|
return 0, errCodeMapIsNil
|
|
}
|
|
for _, v := range w.codes {
|
|
if v.Currency == c {
|
|
return v.WithdrawalTXFee, nil
|
|
}
|
|
}
|
|
return 0, errCurrencyNotFoundInMap
|
|
}
|
|
|
|
// GetDepositAddress returns the public deposit address details for the currency
|
|
func (w *CurrencyDetails) GetDepositAddress(c currency.Code) (string, error) {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
if w.codes == nil {
|
|
return "", errCodeMapIsNil
|
|
}
|
|
for _, v := range w.codes {
|
|
if v.Currency == c {
|
|
if v.DepositAddress == "" {
|
|
return "", errNoDepositAddress
|
|
}
|
|
return v.DepositAddress, nil
|
|
}
|
|
}
|
|
return "", errCurrencyNotFoundInMap
|
|
}
|
|
|
|
// IsWithdrawAndDepositsEnabled returns if withdrawals or deposits are enabled
|
|
func (w *CurrencyDetails) IsWithdrawAndDepositsEnabled(c currency.Code) (bool, error) {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
if w.codes == nil {
|
|
return false, errCodeMapIsNil
|
|
}
|
|
for _, v := range w.codes {
|
|
if v.Currency == c {
|
|
return !v.WithdrawalDepositDisabled, nil
|
|
}
|
|
}
|
|
return false, errCurrencyNotFoundInMap
|
|
}
|
|
|
|
// IsTradingEnabledForCurrency returns if the currency is allowed to be traded
|
|
func (w *CurrencyDetails) IsTradingEnabledForCurrency(c currency.Code) (bool, error) {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
if w.codes == nil {
|
|
return false, errCodeMapIsNil
|
|
}
|
|
for _, v := range w.codes {
|
|
if v.Currency == c {
|
|
return !v.Frozen, nil
|
|
}
|
|
}
|
|
return false, errCurrencyNotFoundInMap
|
|
}
|
|
|
|
// IsTradingEnabledForPair returns if the currency pair is allowed to be traded
|
|
func (w *CurrencyDetails) IsTradingEnabledForPair(pair currency.Pair) (bool, error) {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
if w.codes == nil {
|
|
return false, errCodeMapIsNil
|
|
}
|
|
for _, v := range w.pairs {
|
|
if v.Pair.Equal(pair) {
|
|
return !v.IsFrozen, nil
|
|
}
|
|
}
|
|
return false, errCurrencyNotFoundInMap
|
|
}
|
|
|
|
// IsPostOnlyForPair returns if an order is allowed to take liquidity from the
|
|
// books or reduce positions
|
|
func (w *CurrencyDetails) IsPostOnlyForPair(pair currency.Pair) (bool, error) {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
if w.codes == nil {
|
|
return false, errCodeMapIsNil
|
|
}
|
|
for _, v := range w.pairs {
|
|
if v.Pair.Equal(pair) {
|
|
return v.PostOnly, nil
|
|
}
|
|
}
|
|
return false, errCurrencyNotFoundInMap
|
|
}
|
|
|
|
// isInitial checks state of maps to determine if they have been loaded or not
|
|
func (w *CurrencyDetails) isInitial() bool {
|
|
w.m.RLock()
|
|
defer w.m.RUnlock()
|
|
return w.codes == nil || w.pairs == nil
|
|
}
|