bbgo_origin/pkg/fixedpoint/convert.go

370 lines
6.2 KiB
Go
Raw Normal View History

2020-10-02 12:50:05 +00:00
package fixedpoint
import (
"database/sql/driver"
"encoding/json"
"errors"
2020-11-09 08:34:35 +00:00
"fmt"
2020-10-02 12:50:05 +00:00
"math"
"math/big"
2020-10-02 12:50:05 +00:00
"strconv"
"sync/atomic"
2020-10-02 12:50:05 +00:00
)
const MaxPrecision = 12
2020-10-02 12:50:05 +00:00
const DefaultPrecision = 8
const DefaultPow = 1e8
type Value int64
func (v Value) Value() (driver.Value, error) {
return v.Float64(), nil
}
func (v *Value) Scan(src interface{}) error {
switch d := src.(type) {
case int64:
*v = Value(d)
return nil
case float64:
*v = NewFromFloat(d)
return nil
case []byte:
vv, err := NewFromString(string(d))
if err != nil {
return err
}
*v = vv
return nil
default:
}
return fmt.Errorf("fixedpoint.Value scan error, type: %T is not supported, value; %+v", src, src)
}
2020-10-02 12:50:05 +00:00
func (v Value) Float64() float64 {
return float64(v) / DefaultPow
}
2021-10-17 14:26:04 +00:00
func (v Value) Abs() Value {
if v < 0 {
return -v
}
return v
}
2021-05-24 18:21:55 +00:00
func (v Value) String() string {
return strconv.FormatFloat(float64(v)/DefaultPow, 'f', -1, 64)
}
func (v Value) Percentage() string {
2021-10-17 14:26:04 +00:00
return fmt.Sprintf("%.2f%%", v.Float64()*100.0)
}
func (v Value) SignedPercentage() string {
if v > 0 {
return "+" + v.Percentage()
}
return v.Percentage()
}
2020-10-03 03:11:59 +00:00
func (v Value) Int64() int64 {
return int64(v.Float64())
}
func (v Value) Int() int {
return int(v.Float64())
2020-10-03 03:11:59 +00:00
}
// BigMul is the math/big version multiplication
func (v Value) BigMul(v2 Value) Value {
x := new(big.Int).Mul(big.NewInt(int64(v)), big.NewInt(int64(v2)))
return Value(x.Int64() / DefaultPow)
}
2020-10-02 12:50:05 +00:00
func (v Value) Mul(v2 Value) Value {
return NewFromFloat(v.Float64() * v2.Float64())
}
2021-05-14 07:35:11 +00:00
func (v Value) MulInt(v2 int) Value {
return NewFromFloat(v.Float64() * float64(v2))
}
2020-10-03 05:44:38 +00:00
func (v Value) MulFloat64(v2 float64) Value {
return NewFromFloat(v.Float64() * v2)
}
2020-10-02 12:50:05 +00:00
func (v Value) Div(v2 Value) Value {
return NewFromFloat(v.Float64() / v2.Float64())
}
func (v Value) DivFloat64(v2 float64) Value {
return NewFromFloat(v.Float64() / v2)
}
func (v Value) Floor() Value {
return NewFromFloat(math.Floor(v.Float64()))
}
func (v Value) Ceil() Value {
return NewFromFloat(math.Ceil(v.Float64()))
}
2020-10-02 12:50:05 +00:00
func (v Value) Sub(v2 Value) Value {
return Value(int64(v) - int64(v2))
}
func (v Value) Add(v2 Value) Value {
return Value(int64(v) + int64(v2))
}
func (v *Value) AtomicAdd(v2 Value) {
atomic.AddInt64((*int64)(v), int64(v2))
}
func (v *Value) AtomicLoad() Value {
i := atomic.LoadInt64((*int64)(v))
return Value(i)
}
2020-10-26 09:57:28 +00:00
func (v *Value) UnmarshalYAML(unmarshal func(a interface{}) error) (err error) {
var f float64
if err = unmarshal(&f); err == nil {
*v = NewFromFloat(f)
return
}
2020-11-10 06:19:22 +00:00
var i int64
if err = unmarshal(&i); err == nil {
*v = NewFromInt64(i)
return
}
2020-10-26 09:57:28 +00:00
var s string
if err = unmarshal(&s); err == nil {
nv, err2 := NewFromString(s)
if err2 == nil {
*v = nv
return
}
}
return err
}
2020-12-07 15:03:06 +00:00
func (v Value) MarshalJSON() ([]byte, error) {
f := float64(v) / DefaultPow
2021-01-25 07:32:17 +00:00
o := strconv.FormatFloat(f, 'f', 8, 64)
2020-12-07 15:03:06 +00:00
return []byte(o), nil
}
func (v *Value) UnmarshalJSON(data []byte) error {
var a interface{}
var err = json.Unmarshal(data, &a)
if err != nil {
return err
}
switch d := a.(type) {
case float64:
*v = NewFromFloat(d)
case float32:
*v = NewFromFloat32(d)
case int:
*v = NewFromInt(d)
2021-02-23 01:37:06 +00:00
case int64:
*v = NewFromInt64(d)
2021-02-23 01:37:06 +00:00
case string:
v2, err := NewFromString(d)
if err != nil {
return err
}
*v = v2
default:
2020-11-09 08:34:35 +00:00
return fmt.Errorf("unsupported type: %T %v", d, d)
}
return nil
}
2020-11-10 06:19:22 +00:00
func Must(v Value, err error) Value {
if err != nil {
panic(err)
}
return v
}
var ErrPrecisionLoss = errors.New("precision loss")
func Parse(input string) (num int64, numDecimalPoints int, err error) {
2021-05-16 07:16:04 +00:00
length := len(input)
isPercentage := input[length-1] == '%'
if isPercentage {
length -= 1
2021-05-24 17:50:33 +00:00
input = input[0:length]
2021-05-16 07:16:04 +00:00
}
var neg int64 = 1
var digit int64
2021-05-16 07:16:04 +00:00
for i := 0; i < length; i++ {
c := input[i]
if c == '-' {
neg = -1
} else if c >= '0' && c <= '9' {
digit, err = strconv.ParseInt(string(c), 10, 64)
if err != nil {
return
}
num = num*10 + digit
} else if c == '.' {
i++
if i > len(input)-1 {
err = fmt.Errorf("expect fraction numbers after dot")
return
}
for j := i; j < len(input); j++ {
fc := input[j]
if fc >= '0' && fc <= '9' {
digit, err = strconv.ParseInt(string(fc), 10, 64)
if err != nil {
return
}
numDecimalPoints++
num = num*10 + digit
if numDecimalPoints >= MaxPrecision {
return num, numDecimalPoints, ErrPrecisionLoss
}
} else {
err = fmt.Errorf("expect digit, got %c", fc)
return
}
}
break
} else {
err = fmt.Errorf("unexpected char %c", c)
return
}
}
num = num * neg
2021-05-16 07:16:04 +00:00
if isPercentage {
numDecimalPoints += 2
}
return num, numDecimalPoints, nil
}
func NewFromAny(any interface{}) (Value, error) {
switch v := any.(type) {
case string:
return NewFromString(v)
case float64:
return NewFromFloat(v), nil
case int64:
return NewFromInt64(v), nil
default:
return 0, fmt.Errorf("fixedpoint unsupported type %v", v)
}
}
2020-10-02 12:50:05 +00:00
func NewFromString(input string) (Value, error) {
2021-05-16 07:16:04 +00:00
length := len(input)
if length == 0 {
return 0, nil
}
2021-05-16 07:16:04 +00:00
isPercentage := input[length-1] == '%'
if isPercentage {
2021-05-24 17:35:38 +00:00
input = input[0 : length-1]
2021-05-16 07:16:04 +00:00
}
2020-10-02 12:50:05 +00:00
v, err := strconv.ParseFloat(input, 64)
if err != nil {
return 0, err
}
2021-05-16 07:16:04 +00:00
if isPercentage {
v = v * 0.01
}
2020-10-02 12:50:05 +00:00
return NewFromFloat(v), nil
}
func MustNewFromString(input string) Value {
v, err := NewFromString(input)
if err != nil {
panic(fmt.Errorf("can not parse %s into fixedpoint, error: %s", input, err.Error()))
}
return v
}
2020-10-02 12:50:05 +00:00
func NewFromFloat(val float64) Value {
return Value(int64(math.Round(val * DefaultPow)))
}
2020-10-03 03:11:59 +00:00
func NewFromFloat32(val float32) Value {
return Value(int64(math.Round(float64(val) * DefaultPow)))
}
2020-10-03 03:11:59 +00:00
func NewFromInt(val int) Value {
return Value(int64(val * DefaultPow))
}
func NewFromInt64(val int64) Value {
return Value(val * DefaultPow)
}
func NumFractionalDigits(a Value) int {
numPow := 0
for pow := int64(DefaultPow); pow%10 != 1; pow /= 10 {
numPow++
}
numZeros := 0
for v := int64(a); v%10 == 0; v /= 10 {
numZeros++
}
return numPow - numZeros
}
func Min(a, b Value) Value {
if a < b {
return a
}
return b
}
func Max(a, b Value) Value {
if a > b {
return a
}
return b
}
2021-05-14 03:53:07 +00:00
func Abs(a Value) Value {
if a < 0 {
return -a
}
return a
}