mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-21 22:43:52 +00:00
add back legacy implementation
This commit is contained in:
parent
cdba7924b4
commit
eb70410f80
3
.github/workflows/go.yml
vendored
3
.github/workflows/go.yml
vendored
|
@ -44,3 +44,6 @@ jobs:
|
||||||
|
|
||||||
- name: Test
|
- name: Test
|
||||||
run: go test -v ./pkg/...
|
run: go test -v ./pkg/...
|
||||||
|
|
||||||
|
- name: TestDnum
|
||||||
|
run: go test -tag dnum -v ./pkg/...
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build !dnum
|
||||||
|
|
||||||
package fixedpoint
|
package fixedpoint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -6,7 +8,6 @@ import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"math"
|
"math"
|
||||||
"math/big"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
)
|
)
|
||||||
|
@ -18,6 +19,37 @@ const DefaultPow = 1e8
|
||||||
|
|
||||||
type Value int64
|
type Value int64
|
||||||
|
|
||||||
|
const Zero = Value(0)
|
||||||
|
const One = Value(1e8)
|
||||||
|
const NegOne = Value(-1e8)
|
||||||
|
|
||||||
|
type RoundingMode int
|
||||||
|
|
||||||
|
const (
|
||||||
|
Up RoundingMode = iota
|
||||||
|
Down
|
||||||
|
HalfUp
|
||||||
|
)
|
||||||
|
|
||||||
|
// Trunc returns the integer portion (truncating any fractional part)
|
||||||
|
func (v Value) Trunc() Value {
|
||||||
|
return NewFromFloat(math.Floor(v.Float64()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) Round(r int, mode RoundingMode) Value {
|
||||||
|
pow := math.Pow10(r)
|
||||||
|
result := v.Float64() * pow
|
||||||
|
switch mode {
|
||||||
|
case Up:
|
||||||
|
return NewFromFloat(math.Ceil(result) / pow)
|
||||||
|
case HalfUp:
|
||||||
|
return NewFromFloat(math.Floor(result+0.5) / pow)
|
||||||
|
case Down:
|
||||||
|
return NewFromFloat(math.Floor(result) / pow)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
func (v Value) Value() (driver.Value, error) {
|
func (v Value) Value() (driver.Value, error) {
|
||||||
return v.Float64(), nil
|
return v.Float64(), nil
|
||||||
}
|
}
|
||||||
|
@ -62,8 +94,22 @@ func (v Value) String() string {
|
||||||
return strconv.FormatFloat(float64(v)/DefaultPow, 'f', -1, 64)
|
return strconv.FormatFloat(float64(v)/DefaultPow, 'f', -1, 64)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (v Value) FormatString(prec int) string {
|
||||||
|
return strconv.FormatFloat(float64(v)/DefaultPow, 'f', prec, 64)
|
||||||
|
}
|
||||||
|
|
||||||
func (v Value) Percentage() string {
|
func (v Value) Percentage() string {
|
||||||
return fmt.Sprintf("%.2f%%", v.Float64()*100.0)
|
if v == 0 {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
return strconv.FormatFloat(float64(v)/DefaultPow*100., 'f', -1, 64) + "%"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) FormatPercentage(prec int) string {
|
||||||
|
if v == 0 {
|
||||||
|
return "0"
|
||||||
|
}
|
||||||
|
return strconv.FormatFloat(float64(v)/DefaultPow*100., 'f', prec, 64) + "%"
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) SignedPercentage() string {
|
func (v Value) SignedPercentage() string {
|
||||||
|
@ -78,35 +124,48 @@ func (v Value) Int64() int64 {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Int() int {
|
func (v Value) Int() int {
|
||||||
return int(v.Float64())
|
n := v.Int64()
|
||||||
|
if int64(int(n)) != n {
|
||||||
|
panic("unable to convert Value to int32")
|
||||||
|
}
|
||||||
|
return int(n)
|
||||||
}
|
}
|
||||||
|
|
||||||
// BigMul is the math/big version multiplication
|
func (v Value) Neg() Value {
|
||||||
func (v Value) BigMul(v2 Value) Value {
|
return -v
|
||||||
x := new(big.Int).Mul(big.NewInt(int64(v)), big.NewInt(int64(v2)))
|
}
|
||||||
return Value(x.Int64() / DefaultPow)
|
|
||||||
|
// TODO inf
|
||||||
|
func (v Value) Sign() int {
|
||||||
|
if v > 0 {
|
||||||
|
return 1
|
||||||
|
} else if v == 0 {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (v Value) IsZero() bool {
|
||||||
|
return v == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func Mul(x, y Value) Value {
|
||||||
|
return NewFromFloat(x.Float64() * y.Float64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Mul(v2 Value) Value {
|
func (v Value) Mul(v2 Value) Value {
|
||||||
return NewFromFloat(v.Float64() * v2.Float64())
|
return NewFromFloat(v.Float64() * v2.Float64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) MulInt(v2 int) Value {
|
func Div(x, y Value) Value {
|
||||||
return NewFromFloat(v.Float64() * float64(v2))
|
return NewFromFloat(x.Float64() / y.Float64())
|
||||||
}
|
|
||||||
|
|
||||||
func (v Value) MulFloat64(v2 float64) Value {
|
|
||||||
return NewFromFloat(v.Float64() * v2)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) Div(v2 Value) Value {
|
func (v Value) Div(v2 Value) Value {
|
||||||
return NewFromFloat(v.Float64() / v2.Float64())
|
return NewFromFloat(v.Float64() / v2.Float64())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v Value) DivFloat64(v2 float64) Value {
|
|
||||||
return NewFromFloat(v.Float64() / v2)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (v Value) Floor() Value {
|
func (v Value) Floor() Value {
|
||||||
return NewFromFloat(math.Floor(v.Float64()))
|
return NewFromFloat(math.Floor(v.Float64()))
|
||||||
}
|
}
|
||||||
|
@ -141,7 +200,7 @@ func (v *Value) UnmarshalYAML(unmarshal func(a interface{}) error) (err error) {
|
||||||
|
|
||||||
var i int64
|
var i int64
|
||||||
if err = unmarshal(&i); err == nil {
|
if err = unmarshal(&i); err == nil {
|
||||||
*v = NewFromInt64(i)
|
*v = NewFromInt(i)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,15 +232,13 @@ func (v *Value) UnmarshalJSON(data []byte) error {
|
||||||
switch d := a.(type) {
|
switch d := a.(type) {
|
||||||
case float64:
|
case float64:
|
||||||
*v = NewFromFloat(d)
|
*v = NewFromFloat(d)
|
||||||
|
|
||||||
case float32:
|
case float32:
|
||||||
*v = NewFromFloat32(d)
|
*v = NewFromFloat(float64(d))
|
||||||
|
|
||||||
case int:
|
|
||||||
*v = NewFromInt(d)
|
|
||||||
|
|
||||||
case int64:
|
case int64:
|
||||||
*v = NewFromInt64(d)
|
*v = NewFromInt(d)
|
||||||
|
case int:
|
||||||
|
*v = NewFromInt(int64(d))
|
||||||
|
|
||||||
case string:
|
case string:
|
||||||
v2, err := NewFromString(d)
|
v2, err := NewFromString(d)
|
||||||
|
@ -199,14 +256,6 @@ func (v *Value) UnmarshalJSON(data []byte) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Must(v Value, err error) Value {
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
|
|
||||||
var ErrPrecisionLoss = errors.New("precision loss")
|
var ErrPrecisionLoss = errors.New("precision loss")
|
||||||
|
|
||||||
func Parse(input string) (num int64, numDecimalPoints int, err error) {
|
func Parse(input string) (num int64, numDecimalPoints int, err error) {
|
||||||
|
@ -271,20 +320,6 @@ func Parse(input string) (num int64, numDecimalPoints int, err error) {
|
||||||
return num, numDecimalPoints, nil
|
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)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFromString(input string) (Value, error) {
|
func NewFromString(input string) (Value, error) {
|
||||||
length := len(input)
|
length := len(input)
|
||||||
|
|
||||||
|
@ -317,23 +352,48 @@ func MustNewFromString(input string) Value {
|
||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func NewFromBytes(input []byte) (Value, error) {
|
||||||
|
return NewFromString(string(input))
|
||||||
|
}
|
||||||
|
|
||||||
|
func MustNewFromBytes(input []byte) (v Value) {
|
||||||
|
var err error
|
||||||
|
if v, err = NewFromString(string(input)); err != nil {
|
||||||
|
return Zero
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
|
func Must(v Value, err error) Value {
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
|
||||||
func NewFromFloat(val float64) Value {
|
func NewFromFloat(val float64) Value {
|
||||||
return Value(int64(math.Round(val * DefaultPow)))
|
return Value(int64(math.Round(val * DefaultPow)))
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewFromFloat32(val float32) Value {
|
func NewFromInt(val int64) Value {
|
||||||
return Value(int64(math.Round(float64(val) * DefaultPow)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFromInt(val int) Value {
|
|
||||||
return Value(int64(val * DefaultPow))
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewFromInt64(val int64) Value {
|
|
||||||
return Value(val * DefaultPow)
|
return Value(val * DefaultPow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NumFractionalDigits(a Value) int {
|
func (a Value) MulExp(exp int) Value {
|
||||||
|
return Value(int64(float64(a) * math.Pow(10, float64(exp))))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Value) NumIntDigits() int {
|
||||||
|
digits := 0
|
||||||
|
target := int64(a)
|
||||||
|
for pow := int64(DefaultPow); pow <= target; pow *= 10 {
|
||||||
|
digits++
|
||||||
|
}
|
||||||
|
return digits
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: speedup
|
||||||
|
func (a Value) NumFractionalDigits() int {
|
||||||
numPow := 0
|
numPow := 0
|
||||||
for pow := int64(DefaultPow); pow%10 != 1; pow /= 10 {
|
for pow := int64(DefaultPow); pow%10 != 1; pow /= 10 {
|
||||||
numPow++
|
numPow++
|
||||||
|
@ -345,6 +405,26 @@ func NumFractionalDigits(a Value) int {
|
||||||
return numPow - numZeros
|
return numPow - numZeros
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Compare(x, y Value) int {
|
||||||
|
if x > y {
|
||||||
|
return 1
|
||||||
|
} else if x == y {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Value) Compare(y Value) int {
|
||||||
|
if x > y {
|
||||||
|
return 1
|
||||||
|
} else if x == y {
|
||||||
|
return 0
|
||||||
|
} else {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func Min(a, b Value) Value {
|
func Min(a, b Value) Value {
|
||||||
if a < b {
|
if a < b {
|
||||||
return a
|
return a
|
||||||
|
@ -361,6 +441,14 @@ func Max(a, b Value) Value {
|
||||||
return b
|
return b
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Equal(x, y Value) bool {
|
||||||
|
return x == y
|
||||||
|
}
|
||||||
|
|
||||||
|
func (x Value) Eq(y Value) bool {
|
||||||
|
return x == y
|
||||||
|
}
|
||||||
|
|
||||||
func Abs(a Value) Value {
|
func Abs(a Value) Value {
|
||||||
if a < 0 {
|
if a < 0 {
|
||||||
return -a
|
return -a
|
|
@ -1,3 +1,5 @@
|
||||||
|
//go:build dnum
|
||||||
|
|
||||||
package fixedpoint
|
package fixedpoint
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
@ -1026,8 +1028,7 @@ func Must(v Value, err error) Value {
|
||||||
|
|
||||||
// v * 10^(exp)
|
// v * 10^(exp)
|
||||||
func (v Value) MulExp(exp int) Value {
|
func (v Value) MulExp(exp int) Value {
|
||||||
v.exp += exp
|
return Value{v.coef, v.sign, v.exp + exp}
|
||||||
return v
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sub returns the difference of two Value's
|
// Sub returns the difference of two Value's
|
||||||
|
|
14
pkg/fixedpoint/dec_dnum_test.go
Normal file
14
pkg/fixedpoint/dec_dnum_test.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
//go:build dnum
|
||||||
|
|
||||||
|
package fixedpoint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestDelta(t *testing.T) {
|
||||||
|
f1 := MustNewFromString("0.0009763593380614657")
|
||||||
|
f2 := NewFromInt(42300)
|
||||||
|
assert.InDelta(t, f1.Mul(f2).Float64(), 41.3, 1e-14)
|
||||||
|
}
|
28
pkg/fixedpoint/dec_legacy_test.go
Normal file
28
pkg/fixedpoint/dec_legacy_test.go
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
//go:build !dnum
|
||||||
|
|
||||||
|
package fixedpoint
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestNumFractionalDigitsLegacy(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
v Value
|
||||||
|
want int
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "over the default precision",
|
||||||
|
v: MustNewFromString("0.123456789"),
|
||||||
|
want: 8,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
if got := tt.v.NumFractionalDigits(); got != tt.want {
|
||||||
|
t.Errorf("NumFractionalDigitsLegacy() = %v, want %v", got, tt.want)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,97 +86,17 @@ func TestFromString(t *testing.T) {
|
||||||
assert.Equal(t, "0.004075", f.String())
|
assert.Equal(t, "0.004075", f.String())
|
||||||
f = MustNewFromString("0.03")
|
f = MustNewFromString("0.03")
|
||||||
assert.Equal(t, "0.03", f.String())
|
assert.Equal(t, "0.03", f.String())
|
||||||
}
|
|
||||||
|
|
||||||
func TestDelta(t *testing.T) {
|
f = MustNewFromString("0.75%")
|
||||||
f1 := MustNewFromString("0.0009763593380614657")
|
assert.Equal(t, "0.0075", f.String())
|
||||||
f2 := NewFromInt(42300)
|
|
||||||
assert.InDelta(t, f1.Mul(f2).Float64(), 41.3, 1e-14)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not used
|
|
||||||
/*func TestParse(t *testing.T) {
|
|
||||||
type args struct {
|
|
||||||
input string
|
|
||||||
}
|
|
||||||
tests := []struct {
|
|
||||||
name string
|
|
||||||
args args
|
|
||||||
wantNum int64
|
|
||||||
wantNumDecimalPoints int
|
|
||||||
wantErr bool
|
|
||||||
}{
|
|
||||||
{
|
|
||||||
args: args{input: "-99.9"},
|
|
||||||
wantNum: -999,
|
|
||||||
wantNumDecimalPoints: 1,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: args{input: "0.75%"},
|
|
||||||
wantNum: 75,
|
|
||||||
wantNumDecimalPoints: 4,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: args{input: "0.12345678"},
|
|
||||||
wantNum: 12345678,
|
|
||||||
wantNumDecimalPoints: 8,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: args{input: "a"},
|
|
||||||
wantNum: 0,
|
|
||||||
wantNumDecimalPoints: 0,
|
|
||||||
wantErr: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: args{input: "0.1"},
|
|
||||||
wantNum: 1,
|
|
||||||
wantNumDecimalPoints: 1,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: args{input: "100"},
|
|
||||||
wantNum: 100,
|
|
||||||
wantNumDecimalPoints: 0,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
args: args{input: "100.9999"},
|
|
||||||
wantNum: 1009999,
|
|
||||||
wantNumDecimalPoints: 4,
|
|
||||||
wantErr: false,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
for _, tt := range tests {
|
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
|
||||||
gotNum, gotNumDecimalPoints, err := Parse(tt.args.input)
|
|
||||||
if (err != nil) != tt.wantErr {
|
|
||||||
t.Errorf("Parse() error = %v, wantErr %v", err, tt.wantErr)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if gotNum != tt.wantNum {
|
|
||||||
t.Errorf("Parse() gotNum = %v, want %v", gotNum, tt.wantNum)
|
|
||||||
}
|
|
||||||
if gotNumDecimalPoints != tt.wantNumDecimalPoints {
|
|
||||||
t.Errorf("Parse() gotNumDecimalPoints = %v, want %v", gotNumDecimalPoints, tt.wantNumDecimalPoints)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
func TestNumFractionalDigits(t *testing.T) {
|
func TestNumFractionalDigits(t *testing.T) {
|
||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
v Value
|
v Value
|
||||||
want int
|
want int
|
||||||
}{
|
}{
|
||||||
{
|
|
||||||
name: "over the default precision",
|
|
||||||
v: MustNewFromString("0.123456789"),
|
|
||||||
want: 9,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: "ignore the integer part",
|
name: "ignore the integer part",
|
||||||
v: MustNewFromString("123.4567"),
|
v: MustNewFromString("123.4567"),
|
||||||
|
|
|
@ -1,3 +1,6 @@
|
||||||
|
//go:build dnum
|
||||||
|
// +build dnum
|
||||||
|
|
||||||
// Copyright Suneido Software Corp. All rights reserved.
|
// Copyright Suneido Software Corp. All rights reserved.
|
||||||
// Governed by the MIT license found in the LICENSE file.
|
// Governed by the MIT license found in the LICENSE file.
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user