exchange: order fee-amount protection

Reduce the order amount to prevent submit rejection because of balance exceeding.

  submit_amount = original_amount / (1 + fee_rate)

Currently supported only by FTX Pro.
This commit is contained in:
Raphanus Lo 2022-08-02 11:32:26 +08:00
parent 609508288c
commit ed7df4ddbe
3 changed files with 39 additions and 9 deletions

View File

@ -39,9 +39,10 @@ type ExchangeSession struct {
SubAccount string `json:"subAccount,omitempty" yaml:"subAccount,omitempty"`
// Withdrawal is used for enabling withdrawal functions
Withdrawal bool `json:"withdrawal,omitempty" yaml:"withdrawal,omitempty"`
MakerFeeRate fixedpoint.Value `json:"makerFeeRate" yaml:"makerFeeRate"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate" yaml:"takerFeeRate"`
Withdrawal bool `json:"withdrawal,omitempty" yaml:"withdrawal,omitempty"`
MakerFeeRate fixedpoint.Value `json:"makerFeeRate" yaml:"makerFeeRate"`
TakerFeeRate fixedpoint.Value `json:"takerFeeRate" yaml:"takerFeeRate"`
ModifyOrderAmountForFee bool `json:"modifyOrderAmountForFee" yaml:"modifyOrderAmountForFee"`
PublicOnly bool `json:"publicOnly,omitempty" yaml:"publicOnly"`
Margin bool `json:"margin,omitempty" yaml:"margin"`
@ -202,6 +203,16 @@ func (session *ExchangeSession) Init(ctx context.Context, environ *Environment)
}
}
if session.ModifyOrderAmountForFee {
amountProtectExchange, ok := session.Exchange.(types.ExchangeAmountFeeProtect)
if !ok {
return fmt.Errorf("exchange %s does not support order amount protection", session.ExchangeName.String())
}
fees := types.ExchangeFee{MakerFeeRate: session.MakerFeeRate, TakerFeeRate: session.TakerFeeRate}
amountProtectExchange.SetModifyOrderAmountForFee(fees)
}
if session.UseHeikinAshi {
session.MarketDataStream = &types.HeikinAshiStream{
StandardStreamEmitter: session.MarketDataStream.(types.StandardStreamEmitter),

View File

@ -37,9 +37,10 @@ var marketDataLimiter = rate.NewLimiter(rate.Every(500*time.Millisecond), 2)
type Exchange struct {
client *ftxapi.RestClient
key, secret string
subAccount string
restEndpoint *url.URL
key, secret string
subAccount string
restEndpoint *url.URL
orderAmountReduceFactor fixedpoint.Value
}
type MarketTicker struct {
@ -91,8 +92,9 @@ func NewExchange(key, secret string, subAccount string) *Exchange {
restEndpoint: u,
key: key,
// pragma: allowlist nextline secret
secret: secret,
subAccount: subAccount,
secret: secret,
subAccount: subAccount,
orderAmountReduceFactor: fixedpoint.One,
}
}
@ -221,6 +223,13 @@ func (e *Exchange) DefaultFeeRates() types.ExchangeFee {
}
}
// SetModifyOrderAmountForFee protects the limit buy orders by reducing amount with taker fee.
// The amount is recalculated before submit: submit_amount = original_amount / (1 + taker_fee_rate) .
// This prevents balance exceeding error while closing position without spot margin enabled.
func (e *Exchange) SetModifyOrderAmountForFee(feeRate types.ExchangeFee) {
e.orderAmountReduceFactor = fixedpoint.One.Add(feeRate.TakerFeeRate)
}
// resolution field in api
// window length in seconds. options: 15, 60, 300, 900, 3600, 14400, 86400, or any multiple of 86400 up to 30*86400
var supportedIntervals = map[types.Interval]int{
@ -406,11 +415,17 @@ func (e *Exchange) SubmitOrders(ctx context.Context, orders ...types.SubmitOrder
logrus.WithError(err).Error("type error")
}
submitQuantity := so.Quantity
switch orderType {
case ftxapi.OrderTypeLimit, ftxapi.OrderTypeStopLimit:
submitQuantity = so.Quantity.Div(e.orderAmountReduceFactor)
}
req := e.client.NewPlaceOrderRequest()
req.Market(toLocalSymbol(TrimUpperString(so.Symbol)))
req.OrderType(orderType)
req.Side(ftxapi.Side(TrimLowerString(string(so.Side))))
req.Size(so.Quantity)
req.Size(submitQuantity)
switch so.Type {
case types.OrderTypeLimit, types.OrderTypeLimitMaker:

View File

@ -106,6 +106,10 @@ type ExchangeDefaultFeeRates interface {
DefaultFeeRates() ExchangeFee
}
type ExchangeAmountFeeProtect interface {
SetModifyOrderAmountForFee(ExchangeFee)
}
type ExchangeTradeHistoryService interface {
QueryTrades(ctx context.Context, symbol string, options *TradeQueryOptions) ([]Trade, error)
QueryClosedOrders(ctx context.Context, symbol string, since, until time.Time, lastOrderID uint64) (orders []Order, err error)