mirror of
https://github.com/c9s/bbgo.git
synced 2024-09-20 08:11:08 +00:00
fix: use ZeroAssetError, refactor
This commit is contained in:
parent
ac2f7decdf
commit
fdbcaef2ca
|
@ -219,7 +219,23 @@ type OpenPositionOptions struct {
|
||||||
Tags []string `json:"-" yaml:"-"`
|
Tags []string `json:"-" yaml:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var Delta fixedpoint.Value = fixedpoint.NewFromFloat(0.005)
|
// Delta used to modify the order to submit, especially for the market order
|
||||||
|
var QuantityReduceDelta fixedpoint.Value = fixedpoint.NewFromFloat(0.005)
|
||||||
|
|
||||||
|
func (e *GeneralOrderExecutor) reduceQuantityAndSubmitOrder(ctx context.Context, price fixedpoint.Value, submitOrder types.SubmitOrder) (types.OrderSlice, error) {
|
||||||
|
for {
|
||||||
|
createdOrder, err2 := e.SubmitOrders(ctx, submitOrder)
|
||||||
|
if err2 != nil {
|
||||||
|
submitOrder.Quantity = submitOrder.Quantity.Mul(fixedpoint.One.Sub(QuantityReduceDelta))
|
||||||
|
if e.position.Market.IsDustQuantity(submitOrder.Quantity, price) {
|
||||||
|
return nil, err2
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
log.Infof("created order: %+v", createdOrder)
|
||||||
|
return createdOrder, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPositionOptions) (types.OrderSlice, error) {
|
func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPositionOptions) (types.OrderSlice, error) {
|
||||||
price := options.Price
|
price := options.Price
|
||||||
|
@ -252,24 +268,16 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
|
||||||
|
|
||||||
quantity := options.Quantity
|
quantity := options.Quantity
|
||||||
|
|
||||||
market, ok := e.session.Market(e.symbol)
|
|
||||||
if !ok {
|
|
||||||
return nil, errors.New("cannot find market with symbol " + e.symbol)
|
|
||||||
}
|
|
||||||
|
|
||||||
if options.Long {
|
if options.Long {
|
||||||
if quantity.IsZero() {
|
if quantity.IsZero() {
|
||||||
quoteQuantity, err := CalculateQuoteQuantity(ctx, e.session, e.position.QuoteCurrency, options.Leverage)
|
quoteQuantity, err := CalculateQuoteQuantity(ctx, e.session, e.position.QuoteCurrency, options.Leverage)
|
||||||
if quoteQuantity.IsZero() {
|
|
||||||
log.Warnf("dust quantity: %v", quantity)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
quantity = quoteQuantity.Div(price)
|
quantity = quoteQuantity.Div(price)
|
||||||
}
|
}
|
||||||
if market.IsDustQuantity(quantity, price) {
|
if e.position.Market.IsDustQuantity(quantity, price) {
|
||||||
log.Warnf("dust quantity: %v", quantity)
|
log.Warnf("dust quantity: %v", quantity)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -284,31 +292,16 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
|
||||||
submitOrder.Quantity = quantity
|
submitOrder.Quantity = quantity
|
||||||
|
|
||||||
Notify("Opening %s long position with quantity %v at price %v", e.position.Symbol, quantity, price)
|
Notify("Opening %s long position with quantity %v at price %v", e.position.Symbol, quantity, price)
|
||||||
for {
|
return e.reduceQuantityAndSubmitOrder(ctx, price, submitOrder)
|
||||||
createdOrder, err2 := e.SubmitOrders(ctx, submitOrder)
|
|
||||||
if err2 != nil {
|
|
||||||
submitOrder.Quantity = submitOrder.Quantity.Mul(fixedpoint.One.Sub(Delta))
|
|
||||||
if market.IsDustQuantity(submitOrder.Quantity, price) {
|
|
||||||
return nil, err2
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Infof("created order: %+v", createdOrder)
|
|
||||||
return createdOrder, nil
|
|
||||||
}
|
|
||||||
} else if options.Short {
|
} else if options.Short {
|
||||||
if quantity.IsZero() {
|
if quantity.IsZero() {
|
||||||
var err error
|
var err error
|
||||||
quantity, err = CalculateBaseQuantity(e.session, e.position.Market, price, quantity, options.Leverage)
|
quantity, err = CalculateBaseQuantity(e.session, e.position.Market, price, quantity, options.Leverage)
|
||||||
if quantity.IsZero() {
|
|
||||||
log.Warnf("dust quantity: %v", quantity)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if market.IsDustQuantity(quantity, price) {
|
if e.position.Market.IsDustQuantity(quantity, price) {
|
||||||
log.Warnf("dust quantity: %v", quantity)
|
log.Warnf("dust quantity: %v", quantity)
|
||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
@ -323,18 +316,7 @@ func (e *GeneralOrderExecutor) OpenPosition(ctx context.Context, options OpenPos
|
||||||
submitOrder.Quantity = quantity
|
submitOrder.Quantity = quantity
|
||||||
|
|
||||||
Notify("Opening %s short position with quantity %v at price %v", e.position.Symbol, quantity, price)
|
Notify("Opening %s short position with quantity %v at price %v", e.position.Symbol, quantity, price)
|
||||||
for {
|
return e.reduceQuantityAndSubmitOrder(ctx, price, submitOrder)
|
||||||
createdOrder, err2 := e.SubmitOrders(ctx, submitOrder)
|
|
||||||
if err2 != nil {
|
|
||||||
submitOrder.Quantity = submitOrder.Quantity.Mul(fixedpoint.One.Sub(Delta))
|
|
||||||
if market.IsDustQuantity(submitOrder.Quantity, price) {
|
|
||||||
return nil, err2
|
|
||||||
}
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
log.Infof("created order: %+v", createdOrder)
|
|
||||||
return createdOrder, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.New("options Long or Short must be set")
|
return nil, errors.New("options Long or Short must be set")
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
|
|
||||||
"github.com/c9s/bbgo/pkg/fixedpoint"
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
||||||
|
@ -243,7 +244,8 @@ func CalculateBaseQuantity(session *ExchangeSession, market types.Market, price,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return quantity, fmt.Errorf("quantity is zero, can not submit sell order, please check your quantity settings, your account balances: %+v", balances)
|
return quantity, types.NewZeroAssetError(
|
||||||
|
fmt.Errorf("quantity is zero, can not submit sell order, please check your quantity settings, your account balances: %+v", balances))
|
||||||
}
|
}
|
||||||
|
|
||||||
usdBalances, restBalances := usdFiatBalances(balances)
|
usdBalances, restBalances := usdFiatBalances(balances)
|
||||||
|
@ -329,7 +331,8 @@ func CalculateBaseQuantity(session *ExchangeSession, market types.Market, price,
|
||||||
return maxPositionQuantity, nil
|
return maxPositionQuantity, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return quantity, fmt.Errorf("quantity is zero, can not submit sell order, please check your settings")
|
return quantity, types.NewZeroAssetError(
|
||||||
|
errors.New("quantity is zero, can not submit sell order, please check your settings"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func CalculateQuoteQuantity(ctx context.Context, session *ExchangeSession, quoteCurrency string, leverage fixedpoint.Value) (fixedpoint.Value, error) {
|
func CalculateQuoteQuantity(ctx context.Context, session *ExchangeSession, quoteCurrency string, leverage fixedpoint.Value) (fixedpoint.Value, error) {
|
||||||
|
|
|
@ -36,7 +36,8 @@ func GetModifiableFields(val reflect.Value, callback func(tagName, name string))
|
||||||
if !val.IsValid() {
|
if !val.IsValid() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
for i := 0; i < val.Type().NumField(); i++ {
|
num := val.Type().NumField()
|
||||||
|
for i := 0; i < num; i++ {
|
||||||
t := val.Type().Field(i)
|
t := val.Type().Field(i)
|
||||||
if !t.IsExported() {
|
if !t.IsExported() {
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -2,7 +2,6 @@ package dynamic
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
@ -35,7 +34,6 @@ func TestGetModifiableFields(t *testing.T) {
|
||||||
assert.NotEqual(t, name, "Field2")
|
assert.NotEqual(t, name, "Field2")
|
||||||
assert.NotEqual(t, tagName, "field3")
|
assert.NotEqual(t, tagName, "field3")
|
||||||
assert.NotEqual(t, name, "Field3")
|
assert.NotEqual(t, name, "Field3")
|
||||||
fmt.Println(tagName, name)
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -384,7 +384,7 @@ func (s *Strategy) initTickerFunctions(ctx context.Context) {
|
||||||
s.trailingCheck(pricef, "long"))
|
s.trailingCheck(pricef, "long"))
|
||||||
if exitShortCondition || exitLongCondition {
|
if exitShortCondition || exitLongCondition {
|
||||||
if s.ClosePosition(ctx, fixedpoint.One) {
|
if s.ClosePosition(ctx, fixedpoint.One) {
|
||||||
log.Infof("Close position by orderbook changes")
|
log.Infof("close position by orderbook changes")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
s.positionLock.Unlock()
|
s.positionLock.Unlock()
|
||||||
|
@ -752,11 +752,14 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
opt.Price = source
|
opt.Price = source
|
||||||
opt.Tags = []string{"long"}
|
opt.Tags = []string{"long"}
|
||||||
createdOrders, err := s.GeneralOrderExecutor.OpenPosition(ctx, opt)
|
createdOrders, err := s.GeneralOrderExecutor.OpenPosition(ctx, opt)
|
||||||
log.Infof("orders %v", createdOrders)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if _, ok := err.(types.ZeroAssetError); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
log.WithError(err).Errorf("cannot place buy order")
|
log.WithError(err).Errorf("cannot place buy order")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Infof("orders %v", createdOrders)
|
||||||
if createdOrders != nil {
|
if createdOrders != nil {
|
||||||
s.orderPendingCounter[createdOrders[0].OrderID] = s.minutesCounter
|
s.orderPendingCounter[createdOrders[0].OrderID] = s.minutesCounter
|
||||||
}
|
}
|
||||||
|
@ -786,11 +789,14 @@ func (s *Strategy) klineHandler(ctx context.Context, kline types.KLine) {
|
||||||
opt.Price = source
|
opt.Price = source
|
||||||
opt.Tags = []string{"long"}
|
opt.Tags = []string{"long"}
|
||||||
createdOrders, err := s.GeneralOrderExecutor.OpenPosition(ctx, opt)
|
createdOrders, err := s.GeneralOrderExecutor.OpenPosition(ctx, opt)
|
||||||
log.Infof("orders %v", createdOrders)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
if _, ok := err.(types.ZeroAssetError); ok {
|
||||||
|
return
|
||||||
|
}
|
||||||
log.WithError(err).Errorf("cannot place buy order")
|
log.WithError(err).Errorf("cannot place buy order")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
log.Infof("orders %v", createdOrders)
|
||||||
if createdOrders != nil {
|
if createdOrders != nil {
|
||||||
s.orderPendingCounter[createdOrders[0].OrderID] = s.minutesCounter
|
s.orderPendingCounter[createdOrders[0].OrderID] = s.minutesCounter
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,3 +21,13 @@ func NewOrderError(e error, o Order) error {
|
||||||
order: o,
|
order: o,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type ZeroAssetError struct {
|
||||||
|
error
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewZeroAssetError(e error) ZeroAssetError {
|
||||||
|
return ZeroAssetError{
|
||||||
|
error: e,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
51
pkg/types/filter.go
Normal file
51
pkg/types/filter.go
Normal file
|
@ -0,0 +1,51 @@
|
||||||
|
package types
|
||||||
|
|
||||||
|
type FilterResult struct {
|
||||||
|
a Series
|
||||||
|
b func(int, float64) bool
|
||||||
|
length int
|
||||||
|
c []int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FilterResult) Last() float64 {
|
||||||
|
return f.Index(0)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FilterResult) Index(j int) float64 {
|
||||||
|
if j >= f.length {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
if len(f.c) > j {
|
||||||
|
return f.a.Index(f.c[j])
|
||||||
|
}
|
||||||
|
l := f.a.Length()
|
||||||
|
k := len(f.c)
|
||||||
|
i := 0
|
||||||
|
if k > 0 {
|
||||||
|
i = f.c[k-1] + 1
|
||||||
|
}
|
||||||
|
for ; i < l; i++ {
|
||||||
|
tmp := f.a.Index(i)
|
||||||
|
if f.b(i, tmp) {
|
||||||
|
f.c = append(f.c, i)
|
||||||
|
if j == k {
|
||||||
|
return tmp
|
||||||
|
}
|
||||||
|
k++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *FilterResult) Length() int {
|
||||||
|
return f.length
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filter function filters Series by using a boolean function.
|
||||||
|
// When the boolean function returns true, the Series value at index i will be included in the returned Series.
|
||||||
|
// The returned Series will find at most `length` latest matching elements from the input Series.
|
||||||
|
// Query index larger or equal than length from the returned Series will return 0 instead.
|
||||||
|
// Notice that any Update on the input Series will make the previously returned Series outdated.
|
||||||
|
func Filter(a Series, b func(i int, value float64) bool, length int) SeriesExtend {
|
||||||
|
return NewSeries(&FilterResult{a, b, length, nil})
|
||||||
|
}
|
|
@ -998,51 +998,6 @@ func Rolling(a Series, window int) *RollingResult {
|
||||||
return &RollingResult{a, window}
|
return &RollingResult{a, window}
|
||||||
}
|
}
|
||||||
|
|
||||||
type FilterResult struct {
|
|
||||||
a Series
|
|
||||||
b func(int, float64) bool
|
|
||||||
length int
|
|
||||||
c []int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FilterResult) Last() float64 {
|
|
||||||
return f.Index(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FilterResult) Index(j int) float64 {
|
|
||||||
if j >= f.length {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
if len(f.c) > j {
|
|
||||||
return f.a.Index(f.c[j])
|
|
||||||
}
|
|
||||||
l := f.a.Length()
|
|
||||||
k := len(f.c)
|
|
||||||
i := 0
|
|
||||||
if k > 0 {
|
|
||||||
i = f.c[k-1] + 1
|
|
||||||
}
|
|
||||||
for ; i < l; i++ {
|
|
||||||
tmp := f.a.Index(i)
|
|
||||||
if f.b(i, tmp) {
|
|
||||||
f.c = append(f.c, i)
|
|
||||||
if j == k {
|
|
||||||
return tmp
|
|
||||||
}
|
|
||||||
k++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func (f *FilterResult) Length() int {
|
|
||||||
return f.length
|
|
||||||
}
|
|
||||||
|
|
||||||
func Filter(a Series, b func(i int, value float64) bool, length int) SeriesExtend {
|
|
||||||
return NewSeries(&FilterResult{a, b, length, nil})
|
|
||||||
}
|
|
||||||
|
|
||||||
type SigmoidResult struct {
|
type SigmoidResult struct {
|
||||||
a Series
|
a Series
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user