mirror of
https://github.com/c9s/bbgo.git
synced 2024-11-26 00:35:15 +00:00
Merge pull request #881 from zenixls2/feature/print_strategy_config
print strategy config
This commit is contained in:
commit
039fc21505
|
@ -142,12 +142,16 @@ func (m *SimplePriceMatching) PlaceOrder(o types.SubmitOrder) (*types.Order, *ty
|
||||||
|
|
||||||
case types.OrderTypeStopMarket:
|
case types.OrderTypeStopMarket:
|
||||||
// the actual price might be different.
|
// the actual price might be different.
|
||||||
|
o.StopPrice = m.Market.TruncatePrice(o.StopPrice)
|
||||||
price = o.StopPrice
|
price = o.StopPrice
|
||||||
|
|
||||||
case types.OrderTypeLimit, types.OrderTypeStopLimit, types.OrderTypeLimitMaker:
|
case types.OrderTypeLimit, types.OrderTypeStopLimit, types.OrderTypeLimitMaker:
|
||||||
|
o.Price = m.Market.TruncatePrice(o.Price)
|
||||||
price = o.Price
|
price = o.Price
|
||||||
}
|
}
|
||||||
|
|
||||||
|
o.Quantity = m.Market.TruncateQuantity(o.Quantity)
|
||||||
|
|
||||||
if o.Quantity.Compare(m.Market.MinQuantity) < 0 {
|
if o.Quantity.Compare(m.Market.MinQuantity) < 0 {
|
||||||
return nil, nil, fmt.Errorf("order quantity %s is less than minQuantity %s, order: %+v", o.Quantity.String(), m.Market.MinQuantity.String(), o)
|
return nil, nil, fmt.Errorf("order quantity %s is less than minQuantity %s, order: %+v", o.Quantity.String(), m.Market.MinQuantity.String(), o)
|
||||||
}
|
}
|
||||||
|
|
|
@ -38,6 +38,8 @@ func TestSimplePriceMatching_orderUpdate(t *testing.T) {
|
||||||
MinNotional: fixedpoint.MustNewFromString("0.001"),
|
MinNotional: fixedpoint.MustNewFromString("0.001"),
|
||||||
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
||||||
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
||||||
|
StepSize: fixedpoint.MustNewFromString("0.00001"),
|
||||||
|
TickSize: fixedpoint.MustNewFromString("0.01"),
|
||||||
}
|
}
|
||||||
|
|
||||||
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
t1 := time.Date(2021, 7, 1, 0, 0, 0, 0, time.UTC)
|
||||||
|
@ -192,6 +194,8 @@ func getTestMarket() types.Market {
|
||||||
MinNotional: fixedpoint.MustNewFromString("0.001"),
|
MinNotional: fixedpoint.MustNewFromString("0.001"),
|
||||||
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
MinAmount: fixedpoint.MustNewFromString("10.0"),
|
||||||
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
MinQuantity: fixedpoint.MustNewFromString("0.001"),
|
||||||
|
StepSize: fixedpoint.MustNewFromString("0.00001"),
|
||||||
|
TickSize: fixedpoint.MustNewFromString("0.01"),
|
||||||
}
|
}
|
||||||
return market
|
return market
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package bbgo
|
package bbgo
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
|
@ -35,6 +37,35 @@ type ExitMethod struct {
|
||||||
TrailingStop *TrailingStop2 `json:"trailingStop"`
|
TrailingStop *TrailingStop2 `json:"trailingStop"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (e ExitMethod) String() string {
|
||||||
|
var buf bytes.Buffer
|
||||||
|
if e.RoiStopLoss != nil {
|
||||||
|
b, _ := json.Marshal(e.RoiStopLoss)
|
||||||
|
buf.WriteString("roiStopLoss: " + string(b) + ", ")
|
||||||
|
}
|
||||||
|
if e.ProtectiveStopLoss != nil {
|
||||||
|
b, _ := json.Marshal(e.ProtectiveStopLoss)
|
||||||
|
buf.WriteString("protectiveStopLoss: " + string(b) + ", ")
|
||||||
|
}
|
||||||
|
if e.RoiTakeProfit != nil {
|
||||||
|
b, _ := json.Marshal(e.RoiTakeProfit)
|
||||||
|
buf.WriteString("rioTakeProft: " + string(b) + ", ")
|
||||||
|
}
|
||||||
|
if e.LowerShadowTakeProfit != nil {
|
||||||
|
b, _ := json.Marshal(e.LowerShadowTakeProfit)
|
||||||
|
buf.WriteString("lowerShadowTakeProft: " + string(b) + ", ")
|
||||||
|
}
|
||||||
|
if e.CumulatedVolumeTakeProfit != nil {
|
||||||
|
b, _ := json.Marshal(e.CumulatedVolumeTakeProfit)
|
||||||
|
buf.WriteString("cumulatedVolumeTakeProfit: " + string(b) + ", ")
|
||||||
|
}
|
||||||
|
if e.TrailingStop != nil {
|
||||||
|
b, _ := json.Marshal(e.TrailingStop)
|
||||||
|
buf.WriteString("trailingStop: " + string(b) + ", ")
|
||||||
|
}
|
||||||
|
return buf.String()
|
||||||
|
}
|
||||||
|
|
||||||
// Inherit is used for inheriting properties from the given strategy struct
|
// Inherit is used for inheriting properties from the given strategy struct
|
||||||
// for example, some exit method requires the default interval and symbol name from the strategy param object
|
// for example, some exit method requires the default interval and symbol name from the strategy param object
|
||||||
func (m *ExitMethod) Inherit(parent interface{}) {
|
func (m *ExitMethod) Inherit(parent interface{}) {
|
||||||
|
|
|
@ -526,7 +526,7 @@ func (it *CoreInteraction) Initialize() error {
|
||||||
// getStrategySignature returns strategy instance unique signature
|
// getStrategySignature returns strategy instance unique signature
|
||||||
func getStrategySignature(strategy SingleExchangeStrategy) (string, error) {
|
func getStrategySignature(strategy SingleExchangeStrategy) (string, error) {
|
||||||
// Returns instance ID
|
// Returns instance ID
|
||||||
var signature = callID(strategy)
|
var signature = CallID(strategy)
|
||||||
if signature != "" {
|
if signature != "" {
|
||||||
return signature, nil
|
return signature, nil
|
||||||
}
|
}
|
||||||
|
|
|
@ -82,7 +82,7 @@ func (p *Persistence) Save(val interface{}, subIDs ...string) error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Persistence) Sync(obj interface{}) error {
|
func (p *Persistence) Sync(obj interface{}) error {
|
||||||
id := callID(obj)
|
id := CallID(obj)
|
||||||
if len(id) == 0 {
|
if len(id) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ func (p *Persistence) Sync(obj interface{}) error {
|
||||||
|
|
||||||
// Sync syncs the object properties into the persistence layer
|
// Sync syncs the object properties into the persistence layer
|
||||||
func Sync(obj interface{}) {
|
func Sync(obj interface{}) {
|
||||||
id := callID(obj)
|
id := CallID(obj)
|
||||||
if len(id) == 0 {
|
if len(id) == 0 {
|
||||||
log.Warnf("InstanceID() is not provided, can not sync persistence")
|
log.Warnf("InstanceID() is not provided, can not sync persistence")
|
||||||
return
|
return
|
||||||
|
|
|
@ -21,7 +21,6 @@ func (s *TestStructWithoutInstanceID) ID() string {
|
||||||
return "test-struct-no-instance-id"
|
return "test-struct-no-instance-id"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
type TestStruct struct {
|
type TestStruct struct {
|
||||||
*Environment
|
*Environment
|
||||||
|
|
||||||
|
@ -56,15 +55,15 @@ func preparePersistentServices() []service.PersistenceService {
|
||||||
return pss
|
return pss
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_callID(t *testing.T) {
|
func Test_CallID(t *testing.T) {
|
||||||
t.Run("default", func(t *testing.T) {
|
t.Run("default", func(t *testing.T) {
|
||||||
id := callID(&TestStruct{})
|
id := CallID(&TestStruct{})
|
||||||
assert.NotEmpty(t, id)
|
assert.NotEmpty(t, id)
|
||||||
assert.Equal(t, "test-struct", id)
|
assert.Equal(t, "test-struct", id)
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("fallback", func(t *testing.T) {
|
t.Run("fallback", func(t *testing.T) {
|
||||||
id := callID(&TestStructWithoutInstanceID{Symbol: "BTCUSDT"})
|
id := CallID(&TestStructWithoutInstanceID{Symbol: "BTCUSDT"})
|
||||||
assert.Equal(t, "test-struct-no-instance-id:BTCUSDT", id)
|
assert.Equal(t, "test-struct-no-instance-id:BTCUSDT", id)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -122,7 +121,7 @@ func Test_storePersistenceFields(t *testing.T) {
|
||||||
for _, ps := range pss {
|
for _, ps := range pss {
|
||||||
psName := reflect.TypeOf(ps).Elem().String()
|
psName := reflect.TypeOf(ps).Elem().String()
|
||||||
t.Run("all/"+psName, func(t *testing.T) {
|
t.Run("all/"+psName, func(t *testing.T) {
|
||||||
id := callID(a)
|
id := CallID(a)
|
||||||
err := storePersistenceFields(a, id, ps)
|
err := storePersistenceFields(a, id, ps)
|
||||||
assert.NoError(t, err)
|
assert.NoError(t, err)
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ type InstanceIDProvider interface {
|
||||||
InstanceID() string
|
InstanceID() string
|
||||||
}
|
}
|
||||||
|
|
||||||
func callID(obj interface{}) string {
|
func CallID(obj interface{}) string {
|
||||||
sv := reflect.ValueOf(obj)
|
sv := reflect.ValueOf(obj)
|
||||||
st := reflect.TypeOf(obj)
|
st := reflect.TypeOf(obj)
|
||||||
if st.Implements(reflect.TypeOf((*InstanceIDProvider)(nil)).Elem()) {
|
if st.Implements(reflect.TypeOf((*InstanceIDProvider)(nil)).Elem()) {
|
||||||
|
@ -30,4 +30,3 @@ func callID(obj interface{}) string {
|
||||||
ret := m.Call(nil)
|
ret := m.Call(nil)
|
||||||
return ret[0].String() + ":"
|
return ret[0].String() + ":"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -384,7 +384,7 @@ func (trader *Trader) LoadState() error {
|
||||||
log.Infof("loading strategies states...")
|
log.Infof("loading strategies states...")
|
||||||
|
|
||||||
return trader.IterateStrategies(func(strategy StrategyID) error {
|
return trader.IterateStrategies(func(strategy StrategyID) error {
|
||||||
id := callID(strategy)
|
id := CallID(strategy)
|
||||||
return loadPersistenceFields(strategy, id, ps)
|
return loadPersistenceFields(strategy, id, ps)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -420,7 +420,7 @@ func (trader *Trader) SaveState() error {
|
||||||
|
|
||||||
log.Infof("saving strategies states...")
|
log.Infof("saving strategies states...")
|
||||||
return trader.IterateStrategies(func(strategy StrategyID) error {
|
return trader.IterateStrategies(func(strategy StrategyID) error {
|
||||||
id := callID(strategy)
|
id := CallID(strategy)
|
||||||
if len(id) == 0 {
|
if len(id) == 0 {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
14
pkg/dynamic/can.go
Normal file
14
pkg/dynamic/can.go
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
package dynamic
|
||||||
|
|
||||||
|
import "reflect"
|
||||||
|
|
||||||
|
// For backward compatibility of reflect.Value.CanInt in go1.17
|
||||||
|
func CanInt(v reflect.Value) bool {
|
||||||
|
k := v.Type().Kind()
|
||||||
|
switch k {
|
||||||
|
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
|
||||||
|
return true
|
||||||
|
default:
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -4,28 +4,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"strings"
|
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/util"
|
"github.com/c9s/bbgo/pkg/dynamic"
|
||||||
"github.com/fatih/color"
|
"github.com/c9s/bbgo/pkg/strategy"
|
||||||
"github.com/jedib0t/go-pretty/v6/table"
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
"github.com/jedib0t/go-pretty/v6/text"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
type jsonStruct struct {
|
|
||||||
key string
|
|
||||||
json string
|
|
||||||
tp string
|
|
||||||
value interface{}
|
|
||||||
}
|
|
||||||
type jsonArr []jsonStruct
|
|
||||||
|
|
||||||
func (a jsonArr) Len() int { return len(a) }
|
|
||||||
func (a jsonArr) Less(i, j int) bool { return a[i].key < a[j].key }
|
|
||||||
func (a jsonArr) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
|
||||||
|
|
||||||
func (s *Strategy) ParamDump(f io.Writer, seriesLength ...int) {
|
func (s *Strategy) ParamDump(f io.Writer, seriesLength ...int) {
|
||||||
length := 1
|
length := 1
|
||||||
if len(seriesLength) > 0 && seriesLength[0] > 0 {
|
if len(seriesLength) > 0 && seriesLength[0] > 0 {
|
||||||
|
@ -75,7 +60,7 @@ func (s *Strategy) ParamDump(f io.Writer, seriesLength ...int) {
|
||||||
}
|
}
|
||||||
} else if canString {
|
} else if canString {
|
||||||
fmt.Fprintf(f, "%s: %s\n", fieldName, stringFunc.Call(nil)[0].String())
|
fmt.Fprintf(f, "%s: %s\n", fieldName, stringFunc.Call(nil)[0].String())
|
||||||
} else if field.CanConvert(reflect.TypeOf(int(0))) {
|
} else if dynamic.CanInt(field) {
|
||||||
fmt.Fprintf(f, "%s: %d\n", fieldName, field.Int())
|
fmt.Fprintf(f, "%s: %d\n", fieldName, field.Int())
|
||||||
} else if field.CanConvert(reflect.TypeOf(float64(0))) {
|
} else if field.CanConvert(reflect.TypeOf(float64(0))) {
|
||||||
fmt.Fprintf(f, "%s: %.4f\n", fieldName, field.Float())
|
fmt.Fprintf(f, "%s: %.4f\n", fieldName, field.Float())
|
||||||
|
@ -107,104 +92,9 @@ func (s *Strategy) ParamDump(f io.Writer, seriesLength ...int) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *Strategy) Print(f io.Writer, pretty bool, withColor ...bool) {
|
func (s *Strategy) Print(f io.Writer, pretty bool, withColor ...bool) {
|
||||||
//b, _ := json.MarshalIndent(s.ExitMethods, " ", " ")
|
var style *table.Style
|
||||||
|
|
||||||
t := table.NewWriter()
|
|
||||||
style := table.Style{
|
|
||||||
Name: "StyleRounded",
|
|
||||||
Box: table.StyleBoxRounded,
|
|
||||||
Color: table.ColorOptionsDefault,
|
|
||||||
Format: table.FormatOptionsDefault,
|
|
||||||
HTML: table.DefaultHTMLOptions,
|
|
||||||
Options: table.OptionsDefault,
|
|
||||||
Title: table.TitleOptionsDefault,
|
|
||||||
}
|
|
||||||
var hiyellow func(io.Writer, string, ...interface{})
|
|
||||||
if len(withColor) > 0 && withColor[0] {
|
|
||||||
if pretty {
|
if pretty {
|
||||||
style.Color = table.ColorOptionsYellowWhiteOnBlack
|
style = strategy.DefaultStyle()
|
||||||
style.Color.Row = text.Colors{text.FgHiYellow, text.BgHiBlack}
|
|
||||||
style.Color.RowAlternate = text.Colors{text.FgYellow, text.BgBlack}
|
|
||||||
}
|
|
||||||
hiyellow = color.New(color.FgHiYellow).FprintfFunc()
|
|
||||||
} else {
|
|
||||||
hiyellow = func(a io.Writer, format string, args ...interface{}) {
|
|
||||||
fmt.Fprintf(a, format, args...)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pretty {
|
|
||||||
t.SetOutputMirror(f)
|
|
||||||
t.SetStyle(style)
|
|
||||||
t.AppendHeader(table.Row{"json", "struct field name", "type", "value"})
|
|
||||||
}
|
|
||||||
hiyellow(f, "------ %s Settings ------\n", s.InstanceID())
|
|
||||||
|
|
||||||
embeddedWhiteSet := map[string]struct{}{"Window": {}, "Interval": {}, "Symbol": {}}
|
|
||||||
redundantSet := map[string]struct{}{}
|
|
||||||
var rows []table.Row
|
|
||||||
val := reflect.ValueOf(*s)
|
|
||||||
var values jsonArr
|
|
||||||
for i := 0; i < val.Type().NumField(); i++ {
|
|
||||||
t := val.Type().Field(i)
|
|
||||||
if !t.IsExported() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fieldName := t.Name
|
|
||||||
switch jsonTag := t.Tag.Get("json"); jsonTag {
|
|
||||||
case "-":
|
|
||||||
case "":
|
|
||||||
if t.Anonymous {
|
|
||||||
var target reflect.Type
|
|
||||||
if t.Type.Kind() == util.Pointer {
|
|
||||||
target = t.Type.Elem()
|
|
||||||
} else {
|
|
||||||
target = t.Type
|
|
||||||
}
|
|
||||||
for j := 0; j < target.NumField(); j++ {
|
|
||||||
tt := target.Field(j)
|
|
||||||
if !tt.IsExported() {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
fieldName := tt.Name
|
|
||||||
if _, ok := embeddedWhiteSet[fieldName]; !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if jtag := tt.Tag.Get("json"); jtag != "" && jtag != "-" {
|
|
||||||
name := strings.Split(jtag, ",")[0]
|
|
||||||
if _, ok := redundantSet[name]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
redundantSet[name] = struct{}{}
|
|
||||||
var value interface{}
|
|
||||||
if t.Type.Kind() == util.Pointer {
|
|
||||||
value = val.Field(i).Elem().Field(j).Interface()
|
|
||||||
} else {
|
|
||||||
value = val.Field(i).Field(j).Interface()
|
|
||||||
}
|
|
||||||
values = append(values, jsonStruct{key: fieldName, json: name, tp: tt.Type.String(), value: value})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
name := strings.Split(jsonTag, ",")[0]
|
|
||||||
if _, ok := redundantSet[name]; ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
redundantSet[name] = struct{}{}
|
|
||||||
values = append(values, jsonStruct{key: fieldName, json: name, tp: t.Type.String(), value: val.Field(i).Interface()})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Sort(values)
|
|
||||||
for _, value := range values {
|
|
||||||
if pretty {
|
|
||||||
rows = append(rows, table.Row{value.json, value.key, value.tp, value.value})
|
|
||||||
} else {
|
|
||||||
hiyellow(f, "%s: %v\n", value.json, value.value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if pretty {
|
|
||||||
t.AppendRows(rows)
|
|
||||||
t.Render()
|
|
||||||
}
|
}
|
||||||
|
strategy.PrintConfig(s, f, style, len(withColor) > 0 && withColor[0], strategy.DefaultWhiteList()...)
|
||||||
}
|
}
|
||||||
|
|
151
pkg/strategy/output.go
Normal file
151
pkg/strategy/output.go
Normal file
|
@ -0,0 +1,151 @@
|
||||||
|
package strategy
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"reflect"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/c9s/bbgo/pkg/bbgo"
|
||||||
|
"github.com/c9s/bbgo/pkg/util"
|
||||||
|
"github.com/fatih/color"
|
||||||
|
"github.com/jedib0t/go-pretty/v6/table"
|
||||||
|
"github.com/jedib0t/go-pretty/v6/text"
|
||||||
|
)
|
||||||
|
|
||||||
|
type JsonStruct struct {
|
||||||
|
Key string
|
||||||
|
Json string
|
||||||
|
Type string
|
||||||
|
Value interface{}
|
||||||
|
}
|
||||||
|
|
||||||
|
type JsonArr []JsonStruct
|
||||||
|
|
||||||
|
func (a JsonArr) Len() int { return len(a) }
|
||||||
|
func (a JsonArr) Less(i, j int) bool { return a[i].Key < a[j].Key }
|
||||||
|
func (a JsonArr) Swap(i, j int) { a[i], a[j] = a[j], a[i] }
|
||||||
|
|
||||||
|
func DefaultStyle() *table.Style {
|
||||||
|
style := table.Style{
|
||||||
|
Name: "StyleRounded",
|
||||||
|
Box: table.StyleBoxRounded,
|
||||||
|
Format: table.FormatOptionsDefault,
|
||||||
|
HTML: table.DefaultHTMLOptions,
|
||||||
|
Options: table.OptionsDefault,
|
||||||
|
Title: table.TitleOptionsDefault,
|
||||||
|
Color: table.ColorOptionsYellowWhiteOnBlack,
|
||||||
|
}
|
||||||
|
style.Color.Row = text.Colors{text.FgHiYellow, text.BgHiBlack}
|
||||||
|
style.Color.RowAlternate = text.Colors{text.FgYellow, text.BgBlack}
|
||||||
|
return &style
|
||||||
|
}
|
||||||
|
|
||||||
|
func DefaultWhiteList() []string {
|
||||||
|
return []string{"Window", "Interval", "Symbol"}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @param s: strategy object
|
||||||
|
// @param f: io.Writer used for writing the config dump
|
||||||
|
// @param style: pretty print table style. Use DefaultStyle() to get default one.
|
||||||
|
// @param withColor: whether to print with color
|
||||||
|
// @param whiteLists: fields to be printed out from embedded struct (1st layer only)
|
||||||
|
func PrintConfig(s interface{}, f io.Writer, style *table.Style, withColor bool, whiteLists ...string) {
|
||||||
|
t := table.NewWriter()
|
||||||
|
var write func(io.Writer, string, ...interface{})
|
||||||
|
|
||||||
|
if withColor {
|
||||||
|
write = color.New(color.FgHiYellow).FprintfFunc()
|
||||||
|
} else {
|
||||||
|
write = func(a io.Writer, format string, args ...interface{}) {
|
||||||
|
fmt.Fprintf(a, format, args...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if style != nil {
|
||||||
|
t.SetOutputMirror(f)
|
||||||
|
t.SetStyle(*style)
|
||||||
|
t.SetColumnConfigs([]table.ColumnConfig{
|
||||||
|
{Number: 4, WidthMax: 50, WidthMaxEnforcer: text.WrapText},
|
||||||
|
})
|
||||||
|
t.AppendHeader(table.Row{"json", "struct field name", "type", "value"})
|
||||||
|
}
|
||||||
|
write(f, "---- %s Settings ---\n", bbgo.CallID(s))
|
||||||
|
|
||||||
|
embeddedWhiteSet := map[string]struct{}{}
|
||||||
|
for _, whiteList := range whiteLists {
|
||||||
|
embeddedWhiteSet[whiteList] = struct{}{}
|
||||||
|
}
|
||||||
|
|
||||||
|
redundantSet := map[string]struct{}{}
|
||||||
|
|
||||||
|
var rows []table.Row
|
||||||
|
|
||||||
|
val := reflect.ValueOf(s)
|
||||||
|
|
||||||
|
if val.Type().Kind() == util.Pointer {
|
||||||
|
val = val.Elem()
|
||||||
|
}
|
||||||
|
var values JsonArr
|
||||||
|
for i := 0; i < val.Type().NumField(); i++ {
|
||||||
|
t := val.Type().Field(i)
|
||||||
|
if !t.IsExported() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldName := t.Name
|
||||||
|
switch jsonTag := t.Tag.Get("json"); jsonTag {
|
||||||
|
case "-":
|
||||||
|
case "":
|
||||||
|
// we only fetch fields from the first layer of the embedded struct
|
||||||
|
if t.Anonymous {
|
||||||
|
var target reflect.Type
|
||||||
|
var field reflect.Value
|
||||||
|
if t.Type.Kind() == util.Pointer {
|
||||||
|
target = t.Type.Elem()
|
||||||
|
field = val.Field(i).Elem()
|
||||||
|
} else {
|
||||||
|
target = t.Type
|
||||||
|
field = val.Field(i)
|
||||||
|
}
|
||||||
|
for j := 0; j < target.NumField(); j++ {
|
||||||
|
tt := target.Field(j)
|
||||||
|
if !tt.IsExported() {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
fieldName := tt.Name
|
||||||
|
if _, ok := embeddedWhiteSet[fieldName]; !ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if jtag := tt.Tag.Get("json"); jtag != "" && jtag != "-" {
|
||||||
|
name := strings.Split(jtag, ",")[0]
|
||||||
|
if _, ok := redundantSet[name]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
redundantSet[name] = struct{}{}
|
||||||
|
value := field.Field(j).Interface()
|
||||||
|
values = append(values, JsonStruct{Key: fieldName, Json: name, Type: tt.Type.String(), Value: value})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
name := strings.Split(jsonTag, ",")[0]
|
||||||
|
if _, ok := redundantSet[name]; ok {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
redundantSet[name] = struct{}{}
|
||||||
|
values = append(values, JsonStruct{Key: fieldName, Json: name, Type: t.Type.String(), Value: val.Field(i).Interface()})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Sort(values)
|
||||||
|
for _, value := range values {
|
||||||
|
if style != nil {
|
||||||
|
rows = append(rows, table.Row{value.Json, value.Key, value.Type, value.Value})
|
||||||
|
} else {
|
||||||
|
write(f, "%s: %v\n", value.Json, value.Value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if style != nil {
|
||||||
|
t.AppendRows(rows)
|
||||||
|
t.Render()
|
||||||
|
}
|
||||||
|
}
|
|
@ -156,7 +156,7 @@ func (a *Account) UseLockedBalance(currency string, fund fixedpoint.Value) error
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return fmt.Errorf("trying to use more than locked: locked %v < want to use %v", balance.Locked, fund)
|
return fmt.Errorf("trying to use more than locked: locked %v < want to use %v diff %v", balance.Locked, fund, balance.Locked.Sub(fund))
|
||||||
}
|
}
|
||||||
|
|
||||||
var QuantityDelta = fixedpoint.MustNewFromString("0.00000000001")
|
var QuantityDelta = fixedpoint.MustNewFromString("0.00000000001")
|
||||||
|
|
|
@ -100,8 +100,11 @@ func (m Market) IsDustQuantity(quantity, price fixedpoint.Value) bool {
|
||||||
|
|
||||||
// TruncateQuantity uses the step size to truncate floating number, in order to avoid the rounding issue
|
// TruncateQuantity uses the step size to truncate floating number, in order to avoid the rounding issue
|
||||||
func (m Market) TruncateQuantity(quantity fixedpoint.Value) fixedpoint.Value {
|
func (m Market) TruncateQuantity(quantity fixedpoint.Value) fixedpoint.Value {
|
||||||
stepRound := math.Pow10(-int(math.Log10(m.StepSize.Float64())))
|
return fixedpoint.MustNewFromString(m.FormatQuantity(quantity))
|
||||||
return fixedpoint.NewFromFloat(math.Trunc(quantity.Float64()*stepRound) / stepRound)
|
}
|
||||||
|
|
||||||
|
func (m Market) TruncatePrice(price fixedpoint.Value) fixedpoint.Value {
|
||||||
|
return fixedpoint.MustNewFromString(m.FormatPrice(price))
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Market) BaseCurrencyFormatter() *accounting.Accounting {
|
func (m Market) BaseCurrencyFormatter() *accounting.Accounting {
|
||||||
|
|
Loading…
Reference in New Issue
Block a user