mirror of
https://github.com/d0zingcat/gocryptotrader.git
synced 2026-05-18 07:26:50 +00:00
* init * updates config * wrapper configuration * updates exchange readme * adds SendAuthHTTPRequest and SendHTTPRequest * adds ratelimit file * adds test case and minor fixes * improve error handling * update testcases and improve GetSymbols API * adds SPOT API's * minor fix * WIP * WIP * adds test case * adds check in test case * fixes in Auth. HTTP * improvements * adds trade, kline support and testcases * adds SPOT API and testcases for same * adds SPOT API and testcases * adds SPOT API and testcase * WIP * adds API's * adds API's * adds test cases * adds comment to exported data types * adds API and test cases * adds API * adds API * rearrange functions * WIP: adds API * adds API for Post Order SPOT * adds API and few fixes * fixes * WIP * WIP * add PostBulkOrder API and its test case * fix issues * adds cancel order APIs and test cases for same * add minor test fixes * add API * adds API * fixes * add API * adds API and test cases * fix test * adds API * adds test * fix test * adds API and test * adds deposit API and test cases * WIP * adds API and test cases * WIP * WIP * add public future API and test cases * WIP * remove v2 API and replace them with v1 * update test cases * adds future order API and test cases * adds futures order API * adds API * add API and test cases * adds API and test cases * adds API and test cases * adds API and test cases * Adding wrapper functions * Fix on wrapper function * Adding websocket support * Complete addressing WS push datas * Adding spot push data unit tests * adding futures websocket push data handlers * Adding futures websocket push data handlers * Added unit tests * Updating unit tests * Updating wrapper and unit test functions * Adding missing wrapper functions and code cleaning up * Resolved linter issues * Fixing websocket issues * Fixing websocket issues * Slight fix on config_example file * Minor update * Basic nits updates * Fix minor linter issues * Minor update * Minor unit test update * Minor unit test update * Code update and linter issues fix * Removed unnecessary type conversion codes * Monor update based on review comment * Fix based on review comments * Adding rate-limiter * Websocket update and overall minor fixes * Removed IsAssetTypeEnabled method implementation * Fix connection and formatting issues * Updating orderbook issues * Very minor label fix * Minor error returning fix * code cleaning up and minor spelling fix * Updates on unit test * Update on unit tests and slight code structure * unit test update * orderbook update and minor fix * fix on race * Mini linter fix * fix minor parameter and unit test issues * handler funcs and models update * Fixing websocket and unit test issues * order side string for active orders * Fix on websocket and unit tests * Minor type changes * Minor Orderbook fix and unit test update * Small fix on orderbook * Updating orderbook functionality * FIx on websocket orderbook handlers * Small update on kucoin websocket * fix missed review comments * fix based on review comments * Updating websocket orderbook and fixing unit tests * Minor fixes * unit test update * Updating unit test according to enabled asset type * toggle canManipulateRealOrders const * Unit test update * Fix minor issues * minor fix * documentation fix * wrapper coverage and unused params fix * testing and minor changes * documentation, websocket and unit test update * minor linter fix * Websocket spot/margin subscription update * minor ticker update fix * minor fixes on endpoints * timestamp and number convert method and unit tests * timestamp convert minor update * minor type and conversion fix * create a common timestamp convert and fix minor issues * linter and ticker fix * Updating unit tests and order placing endpoint methods * Added a pairs check * Fix config test error * rm unused error variable * Fix source of linter issue * code update: convert, wrapper and websocket fix * minor code update * Websocket code and unit tests update * Websocket ticker ask/bid type change and small error msg fix * docs update * fix: websocket orderbook handling * change orderbook channel to marketOrderbookLevel2Channels and fix websocket orderbook update * Minor func rename and reciever change * Minor orderbook unit test issue fix * comment: about why we used a random delimiter '-' for futures * update config files and FetchTradablePair func for futures pairs * futures config pairs update * remove ConnextionMonitorDelay from websocket setup * fix on types and futures pair conversion * updating config pairs * change NewPairFromString to DeriveFrom * unit tests update * unit tests update * Added TickerBatching * added GetStandardConfig to GetDefaultConfig --------- Co-authored-by: Jaydeep Rajpurohit <jaydeeppurohit1996@gmail.com>
299 lines
7.8 KiB
Go
299 lines
7.8 KiB
Go
package convert
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"math"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/shopspring/decimal"
|
|
)
|
|
|
|
const jsonStringIdent = `"` // immutable byte sequence
|
|
|
|
var errUnhandledType = errors.New("unhandled type")
|
|
|
|
// FloatFromString format
|
|
func FloatFromString(raw interface{}) (float64, error) {
|
|
str, ok := raw.(string)
|
|
if !ok {
|
|
return 0, fmt.Errorf("unable to parse, value not string: %T", raw)
|
|
}
|
|
flt, err := strconv.ParseFloat(str, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("could not convert value: %s Error: %s", str, err)
|
|
}
|
|
return flt, nil
|
|
}
|
|
|
|
// IntFromString format
|
|
func IntFromString(raw interface{}) (int, error) {
|
|
str, ok := raw.(string)
|
|
if !ok {
|
|
return 0, fmt.Errorf("unable to parse, value not string: %T", raw)
|
|
}
|
|
n, err := strconv.Atoi(str)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("unable to parse as int: %T", raw)
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// Int64FromString format
|
|
func Int64FromString(raw interface{}) (int64, error) {
|
|
str, ok := raw.(string)
|
|
if !ok {
|
|
return 0, fmt.Errorf("unable to parse, value not string: %T", raw)
|
|
}
|
|
n, err := strconv.ParseInt(str, 10, 64)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("unable to parse as int64: %T", raw)
|
|
}
|
|
return n, nil
|
|
}
|
|
|
|
// TimeFromUnixTimestampFloat format
|
|
func TimeFromUnixTimestampFloat(raw interface{}) (time.Time, error) {
|
|
ts, ok := raw.(float64)
|
|
if !ok {
|
|
return time.Time{}, fmt.Errorf("unable to parse, value not float64: %T", raw)
|
|
}
|
|
return time.UnixMilli(int64(ts)), nil
|
|
}
|
|
|
|
// TimeFromUnixTimestampDecimal converts a unix timestamp in decimal form to
|
|
// a time.Time
|
|
func TimeFromUnixTimestampDecimal(input float64) time.Time {
|
|
i, f := math.Modf(input)
|
|
return time.Unix(int64(i), int64(f*(1e9)))
|
|
}
|
|
|
|
// UnixTimestampToTime returns time.time
|
|
func UnixTimestampToTime(timeint64 int64) time.Time {
|
|
return time.Unix(timeint64, 0)
|
|
}
|
|
|
|
// UnixTimestampStrToTime returns a time.time and an error
|
|
func UnixTimestampStrToTime(timeStr string) (time.Time, error) {
|
|
i, err := strconv.ParseInt(timeStr, 10, 64)
|
|
if err != nil {
|
|
return time.Time{}, err
|
|
}
|
|
return time.Unix(i, 0), nil
|
|
}
|
|
|
|
// BoolPtr takes in boolean condition and returns pointer version of it
|
|
func BoolPtr(condition bool) *bool {
|
|
b := condition
|
|
return &b
|
|
}
|
|
|
|
// IntToHumanFriendlyString converts an int to a comma separated string at the thousand point
|
|
// eg 1000 becomes 1,000
|
|
func IntToHumanFriendlyString(number int64, thousandsSep string) string {
|
|
neg := false
|
|
if number < 0 {
|
|
number = -number
|
|
neg = true
|
|
}
|
|
str := fmt.Sprintf("%v", number)
|
|
return numberToHumanFriendlyString(str, 0, "", thousandsSep, neg)
|
|
}
|
|
|
|
// FloatToHumanFriendlyString converts a float to a comma separated string at the thousand point
|
|
// eg 1000 becomes 1,000
|
|
func FloatToHumanFriendlyString(number float64, decimals uint, decPoint, thousandsSep string) string {
|
|
neg := false
|
|
if number < 0 {
|
|
number = -number
|
|
neg = true
|
|
}
|
|
dec := int(decimals)
|
|
str := fmt.Sprintf("%."+strconv.Itoa(dec)+"F", number)
|
|
return numberToHumanFriendlyString(str, dec, decPoint, thousandsSep, neg)
|
|
}
|
|
|
|
// DecimalToHumanFriendlyString converts a decimal number to a comma separated string at the thousand point
|
|
// eg 1000 becomes 1,000
|
|
func DecimalToHumanFriendlyString(number decimal.Decimal, rounding int, decPoint, thousandsSep string) string {
|
|
neg := false
|
|
if number.LessThan(decimal.Zero) {
|
|
number = number.Abs()
|
|
neg = true
|
|
}
|
|
str := number.String()
|
|
if rnd := strings.Split(str, "."); len(rnd) == 1 {
|
|
rounding = 0
|
|
} else if len(rnd[1]) < rounding {
|
|
rounding = len(rnd[1])
|
|
}
|
|
return numberToHumanFriendlyString(number.StringFixed(int32(rounding)), rounding, decPoint, thousandsSep, neg)
|
|
}
|
|
|
|
func numberToHumanFriendlyString(str string, dec int, decPoint, thousandsSep string, neg bool) string {
|
|
var prefix, suffix string
|
|
if len(str)-(dec+1) < 0 {
|
|
dec = 0
|
|
}
|
|
if dec > 0 {
|
|
prefix = str[:len(str)-(dec+1)]
|
|
suffix = str[len(str)-dec:]
|
|
} else {
|
|
prefix = str
|
|
}
|
|
sep := []byte(thousandsSep)
|
|
n, l1, l2 := 0, len(prefix), len(sep)
|
|
// thousands sep num
|
|
c := (l1 - 1) / 3
|
|
tmp := make([]byte, l2*c+l1)
|
|
pos := len(tmp) - 1
|
|
for i := l1 - 1; i >= 0; i, n, pos = i-1, n+1, pos-1 {
|
|
if l2 > 0 && n > 0 && n%3 == 0 {
|
|
for j := range sep {
|
|
tmp[pos] = sep[l2-j-1]
|
|
pos--
|
|
}
|
|
}
|
|
tmp[pos] = prefix[i]
|
|
}
|
|
s := string(tmp)
|
|
if dec > 0 {
|
|
s += decPoint + suffix
|
|
}
|
|
if neg {
|
|
s = "-" + s
|
|
}
|
|
|
|
return s
|
|
}
|
|
|
|
// InterfaceToFloat64OrZeroValue returns the type assertion value or variable zero value
|
|
func InterfaceToFloat64OrZeroValue(r interface{}) float64 {
|
|
if v, ok := r.(float64); ok {
|
|
return v
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// InterfaceToIntOrZeroValue returns the type assertion value or variable zero value
|
|
func InterfaceToIntOrZeroValue(r interface{}) int {
|
|
if v, ok := r.(int); ok {
|
|
return v
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// InterfaceToStringOrZeroValue returns the type assertion value or variable zero value
|
|
func InterfaceToStringOrZeroValue(r interface{}) string {
|
|
if v, ok := r.(string); ok {
|
|
return v
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// StringToFloat64 is a float64 that unmarshals from a string. This is useful
|
|
// for APIs that return numbers as strings and return an empty string instead of
|
|
// 0.
|
|
type StringToFloat64 float64
|
|
|
|
// UnmarshalJSON implements the json.Unmarshaler interface.
|
|
// This implementation is slightly more performant than calling json.Unmarshal
|
|
// again.
|
|
func (f *StringToFloat64) UnmarshalJSON(data []byte) error {
|
|
if !bytes.HasPrefix(data, []byte(jsonStringIdent)) {
|
|
return fmt.Errorf("%w: %s", errUnhandledType, string(data))
|
|
}
|
|
|
|
data = data[1 : len(data)-1] // Remove quotes
|
|
if len(data) == 0 {
|
|
*f = StringToFloat64(0)
|
|
return nil
|
|
}
|
|
|
|
val, err := strconv.ParseFloat(string(data), 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
*f = StringToFloat64(val)
|
|
return nil
|
|
}
|
|
|
|
// MarshalJSON implements the json.Marshaler interface.
|
|
func (f StringToFloat64) MarshalJSON() ([]byte, error) {
|
|
if f == 0 {
|
|
return []byte(jsonStringIdent + jsonStringIdent), nil
|
|
}
|
|
val := strconv.FormatFloat(float64(f), 'f', -1, 64)
|
|
return []byte(jsonStringIdent + val + jsonStringIdent), nil
|
|
}
|
|
|
|
// Float64 returns the float64 value of the FloatString.
|
|
func (f StringToFloat64) Float64() float64 {
|
|
return float64(f)
|
|
}
|
|
|
|
// Decimal returns the decimal value of the FloatString
|
|
// Warning: this does not handle big numbers as the underlying
|
|
// is still a float
|
|
func (f StringToFloat64) Decimal() decimal.Decimal {
|
|
return decimal.NewFromFloat(float64(f))
|
|
}
|
|
|
|
// ExchangeTime provides timestamp to time conversion method.
|
|
type ExchangeTime time.Time
|
|
|
|
// UnmarshalJSON is custom type json unmarshaller for ExchangeTime
|
|
func (k *ExchangeTime) UnmarshalJSON(data []byte) error {
|
|
var timestamp interface{}
|
|
err := json.Unmarshal(data, ×tamp)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
var standard int64
|
|
switch value := timestamp.(type) {
|
|
case string:
|
|
if value == "" {
|
|
// Setting the time to zero value because some timestamp fields could return an empty string while there is no error
|
|
// So, in such cases, Time returns zero timestamp.
|
|
break
|
|
}
|
|
standard, err = strconv.ParseInt(value, 10, 64)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
case int64:
|
|
standard = value
|
|
case float64:
|
|
// Warning: converting float64 to int64 instance may create loss of precision in the timestamp information.
|
|
// be aware or consider customizing this section if found necessary.
|
|
standard = int64(value)
|
|
case nil:
|
|
// for some exchange timestamp fields, if the timestamp information is not specified,
|
|
// the data is 'nil' instead of zero value string or integer value.
|
|
default:
|
|
return fmt.Errorf("unsupported timestamp type %T", timestamp)
|
|
}
|
|
|
|
switch {
|
|
case standard == 0:
|
|
*k = ExchangeTime(time.Time{})
|
|
case standard >= 1e13:
|
|
*k = ExchangeTime(time.Unix(standard/1e9, standard%1e9))
|
|
case standard > 9999999999:
|
|
*k = ExchangeTime(time.UnixMilli(standard))
|
|
default:
|
|
*k = ExchangeTime(time.Unix(standard, 0))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// Time returns a time.Time instance from ExchangeTime instance object.
|
|
func (k ExchangeTime) Time() time.Time {
|
|
return time.Time(k)
|
|
}
|