2023-03-23 10:18:30 +00:00
|
|
|
package xfunding
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
|
|
|
|
"github.com/c9s/bbgo/pkg/fixedpoint"
|
|
|
|
"github.com/c9s/bbgo/pkg/types"
|
|
|
|
)
|
|
|
|
|
|
|
|
type FuturesTransfer interface {
|
|
|
|
TransferFuturesAccountAsset(ctx context.Context, asset string, amount fixedpoint.Value, io types.TransferDirection) error
|
|
|
|
QueryAccountBalances(ctx context.Context) (types.BalanceMap, error)
|
|
|
|
}
|
|
|
|
|
2023-03-23 14:54:42 +00:00
|
|
|
func (s *Strategy) transferOut(ctx context.Context, ex FuturesTransfer, currency string, trade types.Trade) error {
|
2023-03-23 10:18:30 +00:00
|
|
|
// base asset needs BUY trades
|
2023-03-23 18:52:13 +00:00
|
|
|
if trade.Side != types.SideTypeBuy {
|
2023-03-23 10:18:30 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-23 18:52:13 +00:00
|
|
|
// if transfer done
|
|
|
|
if s.State.TotalBaseTransfer.IsZero() {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// de-leverage and get the collateral base quantity for transfer
|
|
|
|
quantity := trade.Quantity
|
|
|
|
quantity = quantity.Div(s.Leverage)
|
|
|
|
|
2023-03-23 14:54:42 +00:00
|
|
|
balances, err := s.futuresSession.Exchange.QueryAccountBalances(ctx)
|
2023-03-23 10:18:30 +00:00
|
|
|
if err != nil {
|
2023-03-23 18:52:13 +00:00
|
|
|
log.Infof("adding to pending base transfer: %s %s + %s", quantity.String(), currency, s.State.PendingBaseTransfer.String())
|
|
|
|
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
2023-03-23 10:18:30 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, ok := balances[currency]
|
|
|
|
if !ok {
|
2023-03-23 18:52:13 +00:00
|
|
|
log.Infof("adding to pending base transfer: %s %s + %s", quantity.String(), currency, s.State.PendingBaseTransfer.String())
|
|
|
|
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
2023-03-23 10:18:30 +00:00
|
|
|
return fmt.Errorf("%s balance not found", currency)
|
|
|
|
}
|
|
|
|
|
2023-03-23 18:52:13 +00:00
|
|
|
// add the previous pending base transfer and the current trade quantity
|
|
|
|
amount := s.State.PendingBaseTransfer.Add(quantity)
|
2023-03-23 14:54:42 +00:00
|
|
|
|
2023-03-23 18:52:13 +00:00
|
|
|
// try to transfer more if we enough balance
|
|
|
|
amount = fixedpoint.Min(amount, b.Available)
|
|
|
|
|
|
|
|
// we can only transfer the rest quota (total base transfer)
|
|
|
|
amount = fixedpoint.Min(s.State.TotalBaseTransfer, amount)
|
2023-03-23 14:54:42 +00:00
|
|
|
|
2023-03-23 10:18:30 +00:00
|
|
|
// TODO: according to the fee, we might not be able to get enough balance greater than the trade quantity, we can adjust the quantity here
|
2023-03-23 18:52:13 +00:00
|
|
|
if amount.IsZero() {
|
|
|
|
log.Infof("adding to pending base transfer: %s %s + %s ", quantity.String(), currency, s.State.PendingBaseTransfer.String())
|
2023-03-23 14:54:42 +00:00
|
|
|
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
2023-03-23 10:18:30 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-23 18:52:13 +00:00
|
|
|
// de-leverage and get the collateral base quantity
|
|
|
|
collateralBase := s.FuturesPosition.GetBase().Abs().Div(s.Leverage)
|
|
|
|
_ = collateralBase
|
2023-03-23 10:18:30 +00:00
|
|
|
|
2023-03-23 18:52:13 +00:00
|
|
|
// if s.State.TotalBaseTransfer.Compare(collateralBase)
|
2023-03-23 10:18:30 +00:00
|
|
|
|
|
|
|
log.Infof("transfering out futures account asset %s %s", amount, currency)
|
|
|
|
if err := ex.TransferFuturesAccountAsset(ctx, currency, amount, types.TransferOut); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset pending transfer
|
|
|
|
s.State.PendingBaseTransfer = fixedpoint.Zero
|
|
|
|
|
2023-03-23 18:52:13 +00:00
|
|
|
// reduce the transfer in the total base transfer
|
|
|
|
s.State.TotalBaseTransfer = s.State.TotalBaseTransfer.Sub(amount)
|
2023-03-23 10:18:30 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-23 14:54:42 +00:00
|
|
|
func (s *Strategy) transferIn(ctx context.Context, ex FuturesTransfer, currency string, trade types.Trade) error {
|
2023-03-23 10:18:30 +00:00
|
|
|
|
|
|
|
// base asset needs BUY trades
|
|
|
|
if trade.Side == types.SideTypeSell {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-23 14:54:42 +00:00
|
|
|
balances, err := s.spotSession.Exchange.QueryAccountBalances(ctx)
|
2023-03-23 10:18:30 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, ok := balances[currency]
|
|
|
|
if !ok {
|
|
|
|
return fmt.Errorf("%s balance not found", currency)
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: according to the fee, we might not be able to get enough balance greater than the trade quantity, we can adjust the quantity here
|
2023-03-23 14:54:42 +00:00
|
|
|
quantity := trade.Quantity
|
|
|
|
if b.Available.Compare(quantity) < 0 {
|
|
|
|
log.Infof("adding to pending base transfer: %s %s", quantity, currency)
|
|
|
|
s.State.PendingBaseTransfer = s.State.PendingBaseTransfer.Add(quantity)
|
2023-03-23 10:18:30 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-03-23 14:54:42 +00:00
|
|
|
amount := s.State.PendingBaseTransfer.Add(quantity)
|
2023-03-23 10:18:30 +00:00
|
|
|
|
2023-03-23 14:54:42 +00:00
|
|
|
pos := s.SpotPosition.GetBase().Abs()
|
2023-03-23 10:18:30 +00:00
|
|
|
rest := pos.Sub(s.State.TotalBaseTransfer)
|
|
|
|
|
|
|
|
if rest.Sign() < 0 {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
amount = fixedpoint.Min(rest, amount)
|
|
|
|
|
|
|
|
log.Infof("transfering in futures account asset %s %s", amount, currency)
|
|
|
|
if err := ex.TransferFuturesAccountAsset(ctx, currency, amount, types.TransferIn); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// reset pending transfer
|
|
|
|
s.State.PendingBaseTransfer = fixedpoint.Zero
|
|
|
|
|
|
|
|
// record the transfer in the total base transfer
|
|
|
|
s.State.TotalBaseTransfer = s.State.TotalBaseTransfer.Add(amount)
|
|
|
|
return nil
|
|
|
|
}
|