Skip to content

Commit

Permalink
Alas Bitget continues
Browse files Browse the repository at this point in the history
  • Loading branch information
cranktakular committed Aug 14, 2024
1 parent ded7a3c commit 913601b
Show file tree
Hide file tree
Showing 11 changed files with 213 additions and 54 deletions.
18 changes: 0 additions & 18 deletions cmd/exchange_wrapper_issues/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -919,24 +919,6 @@ func testWrappers(e exchange.IBotExchange, base *exchange.Base, config *Config)
Response: marginRateHistoryResponse,
})

positionSummaryRequest := &futures.PositionSummaryRequest{
Asset: assetTypes[i],
Pair: p,
}
var positionSummaryResponse *futures.PositionSummary
positionSummaryResponse, err = e.GetPositionSummary(context.TODO(), positionSummaryRequest)
msg = ""
if err != nil {
msg = err.Error()
responseContainer.ErrorCount++
}
responseContainer.EndpointResponses = append(responseContainer.EndpointResponses, EndpointResponse{
SentParams: jsonifyInterface([]interface{}{positionSummaryRequest}),
Function: "GetFuturesPositionSummary",
Error: msg,
Response: jsonifyInterface([]interface{}{positionSummaryResponse}),
})

calculatePNLRequest := &futures.PNLCalculatorRequest{
Pair: p,
Underlying: p.Base,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -579,7 +579,6 @@ var excludedMethodNames = map[string]struct{}{
"CalculatePNL": {},
"CalculateTotalCollateral": {},
"ScaleCollateral": {},
"GetPositionSummary": {},
"GetFuturesPositionSummary": {},
"GetFuturesPositionOrders": {},
"SetCollateralMode": {},
Expand Down
2 changes: 1 addition & 1 deletion exchanges/binance/binance_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3053,7 +3053,7 @@ func TestChangePositionMargin(t *testing.T) {
}
}

func TestGetPositionSummary(t *testing.T) {
func TestGetFuturesPositionSummary(t *testing.T) {
t.Parallel()
sharedtestvalues.SkipTestIfCredentialsUnset(t, b)

Expand Down
20 changes: 9 additions & 11 deletions exchanges/bitget/bitget.go
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,6 @@ func (bi *Bitget) GetMerchantP2POrders(ctx context.Context, startTime, endTime t
params.Values.Set("status", status)
params.Values.Set("side", side)
params.Values.Set("coin", cryptoCurrency)
// params.Values.Set("language", "en-US")
params.Values.Set("fiat", fiatCurrency)
var resp *P2POrdersResp
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodGet,
Expand All @@ -467,7 +466,6 @@ func (bi *Bitget) GetMerchantAdvertisementList(ctx context.Context, startTime, e
params.Values.Set("status", status)
params.Values.Set("side", side)
params.Values.Set("coin", cryptoCurrency)
// params.Values.Set("language", "en-US")
params.Values.Set("fiat", fiatCurrency)
params.Values.Set("orderBy", orderBy)
params.Values.Set("sourceType", sourceType)
Expand Down Expand Up @@ -686,7 +684,7 @@ func (bi *Bitget) GetVirtualSubaccounts(ctx context.Context, limit, pagination i
vals.Set("status", status)
path := bitgetUser + bitgetVirtualSubaccount + "-" + bitgetList
var resp *GetVirSubResp
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate2, http.MethodGet, path, vals,
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodGet, path, vals,
nil, &resp)
}

Expand Down Expand Up @@ -751,7 +749,7 @@ func (bi *Bitget) GetAPIKeys(ctx context.Context, subaccountID string) (*GetAPIK
vals.Set("subAccountUid", subaccountID)
path := bitgetUser + bitgetVirtualSubaccount + "-" + bitgetAPIKey + "-" + bitgetList
var resp *GetAPIKeyResp
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodGet, path, vals,
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate5, http.MethodGet, path, vals,
nil, &resp)
}

Expand Down Expand Up @@ -780,7 +778,7 @@ func (bi *Bitget) GetBotAccountAssets(ctx context.Context, accountType string) (
// GetAssetOverview returns an overview of the user's assets across various account types
func (bi *Bitget) GetAssetOverview(ctx context.Context) (*AssetOverviewResp, error) {
var resp *AssetOverviewResp
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodGet,
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate1, http.MethodGet,
bitgetAccount+bitgetAllAccountBalance, nil, nil, &resp)
}

Expand Down Expand Up @@ -1395,7 +1393,7 @@ func (bi *Bitget) BatchCancelSpotPlanOrders(ctx context.Context, pairs []string)
}
path := bitgetSpot + bitgetTrade + bitgetBatchCancelPlanOrder
var resp *BatchOrderResp
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req,
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate5, http.MethodPost, path, nil, req,
&resp)
}

Expand Down Expand Up @@ -1651,7 +1649,7 @@ func (bi *Bitget) SwitchBGBDeductionStatus(ctx context.Context, deduct bool) (*B
}
path := bitgetSpot + bitgetAccount + bitgetSwitchDeduct
var resp *BoolData
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodPost, path, nil, req,
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate1, http.MethodPost, path, nil, req,
&resp)
}

Expand Down Expand Up @@ -1691,7 +1689,7 @@ func (bi *Bitget) GetSubaccountDepositAddress(ctx context.Context, subaccountID,
func (bi *Bitget) GetBGBDeductionStatus(ctx context.Context) (*BGBDeductResp, error) {
path := bitgetSpot + bitgetAccount + bitgetDeductInfo
var resp *BGBDeductResp
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodGet, path, nil, nil,
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate5, http.MethodGet, path, nil, nil,
&resp)
}

Expand Down Expand Up @@ -2055,7 +2053,7 @@ func (bi *Bitget) GetFuturesSubaccountAssets(ctx context.Context, productType st
vals.Set("productType", productType)
path := bitgetMix + bitgetAccount + bitgetSubaccountAssets2
var resp *SubaccountFuturesResp
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate10, http.MethodGet, path, vals, nil,
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate1, http.MethodGet, path, vals, nil,
&resp)
}

Expand Down Expand Up @@ -2295,7 +2293,7 @@ func (bi *Bitget) GetHistoricalPositions(ctx context.Context, pair, productType
}
path := bitgetMix + bitgetPosition + bitgetHistoryPosition
var resp *HistPositionResp
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate5, http.MethodGet, path, params.Values,
return resp, bi.SendAuthenticatedHTTPRequest(ctx, exchange.RestSpot, Rate20, http.MethodGet, path, params.Values,
nil, &resp)
}

Expand Down Expand Up @@ -2428,7 +2426,7 @@ func (bi *Bitget) BatchPlaceFuturesOrders(ctx context.Context, pair, productType
"orderList": orders,
}
path := bitgetMix + bitgetOrder + bitgetBatchPlaceOrder
rLim := Rate10
rLim := Rate5
if isCopyTradeLeader {
rLim = Rate1
}
Expand Down
42 changes: 38 additions & 4 deletions exchanges/bitget/bitget_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -73,12 +73,12 @@ const (

errAPIKeyLimitPartial = `Bitget unsuccessful HTTP status code: 400 raw response: {"code":"40063","msg":"API exceeds the maximum limit added","requestTime":`
errCurrentlyHoldingPositionPartial = `Bitget unsuccessful HTTP status code: 400 raw response: {"code":"45117","msg":"Currently holding positions or orders, the margin mode cannot be adjusted","requestTime":`
errFakePairDoesNotExistPartial = `Bitget unsuccessful HTTP status code: 400 raw response: {"code":"40034","msg":"Parameter FAKEPAIRNOTREALMEOWMEOW does not exist","requestTime"`
)

// Developer-defined variables to aid testing
var (
fakePair = currency.NewPair(currency.NewCode("FAKEPAIRNOT"), currency.NewCode("REALMEOWMEOW"))
fakeCurrency = currency.NewCode("FAKECURRENCYNOT")
fakePair = currency.NewPair(fakeCurrency, currency.NewCode("REALMEOWMEOW"))
)

var bi = &Bitget{}
Expand Down Expand Up @@ -2778,12 +2778,46 @@ func TestUpdateOrderExecutionLimits(t *testing.T) {
assert.NoError(t, err)
}

func TestUpdateCurrencyStates(t *testing.T) {
t.Parallel()
err := bi.UpdateCurrencyStates(context.Background(), asset.Spot)
assert.NoError(t, err)
}

func TestGetAvailableTransferChains(t *testing.T) {
t.Parallel()
testGetOneArg(t, bi.GetAvailableTransferChains, currency.EMPTYCODE, testCrypto, errCurrencyEmpty, false, false, true)
_, err := bi.GetAvailableTransferChains(context.Background(), currency.NewCode("fakecurrencynotrealmeowmeow"))
_, err := bi.GetAvailableTransferChains(context.Background(), fakeCurrency)
assert.Error(t, err)
}

func TestCalculatePNL(t *testing.T) {
bi.Verbose = true
_, err := bi.CalculatePNL(context.Background(), nil)
assert.NoError(t, err)
}

func TestGetFuturesPositionSummary(t *testing.T) {
t.Parallel()
_, err := bi.GetFuturesPositionSummary(context.Background(), nil)
assert.ErrorIs(t, err, common.ErrNilPointer)
sharedtestvalues.SkipTestIfCredentialsUnset(t, bi)
_, err = bi.GetFuturesPositionSummary(context.Background(), &futures.PositionSummaryRequest{})
assert.ErrorIs(t, err, errPairEmpty)
_, err = bi.GetFuturesPositionSummary(context.Background(), &futures.PositionSummaryRequest{Pair: testPair})
assert.NoError(t, err)
}

func TestGetFuturesPositions(t *testing.T) {
t.Parallel()
_, err := bi.GetFuturesPositions(context.Background(), nil)
assert.ErrorIs(t, err, common.ErrNilPointer)
sharedtestvalues.SkipTestIfCredentialsUnset(t, bi)
req := &futures.PositionsRequest{
Pairs: currency.Pairs{testPair, currency.NewPair(currency.BTC, currency.ETH)},
}
_, err = bi.GetFuturesPositions(context.Background(), req)
assert.NoError(t, err)
// See if there's an established fake currency you can use instead of reinventing this one
}

// The following 3 tests aren't parallel due to collisions with each other, and some other plan order-related tests
Expand Down
165 changes: 164 additions & 1 deletion exchanges/bitget/bitget_wrapper.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
exchange "github.com/thrasher-corp/gocryptotrader/exchanges"
"github.com/thrasher-corp/gocryptotrader/exchanges/account"
"github.com/thrasher-corp/gocryptotrader/exchanges/asset"
"github.com/thrasher-corp/gocryptotrader/exchanges/collateral"
"github.com/thrasher-corp/gocryptotrader/exchanges/currencystate"
"github.com/thrasher-corp/gocryptotrader/exchanges/deposit"
"github.com/thrasher-corp/gocryptotrader/exchanges/fundingrate"
"github.com/thrasher-corp/gocryptotrader/exchanges/futures"
Expand Down Expand Up @@ -1639,6 +1641,9 @@ func (bi *Bitget) GetFuturesContractDetails(ctx context.Context, _ asset.Item) (

// GetLatestFundingRates returns the latest funding rates data
func (bi *Bitget) GetLatestFundingRates(ctx context.Context, req *fundingrate.LatestRateRequest) ([]fundingrate.LatestRateResponse, error) {
if req == nil {
return nil, fmt.Errorf("%T %w", req, common.ErrNilPointer)
}
curRate, err := bi.GetFundingCurrent(ctx, req.Pair.String(), getProductType(req.Pair))
if err != nil {
return nil, err
Expand Down Expand Up @@ -1727,7 +1732,33 @@ func (bi *Bitget) UpdateOrderExecutionLimits(ctx context.Context, a asset.Item)

// UpdateCurrencyStates updates currency states
func (bi *Bitget) UpdateCurrencyStates(ctx context.Context, a asset.Item) error {
return common.ErrNotYetImplemented
payload := make(map[currency.Code]currencystate.Options)
resp, err := bi.GetCoinInfo(ctx, "")
if err != nil {
return err
}
for i := range resp.Data {
var withdraw bool
var deposit bool
var trade bool
for j := range resp.Data[i].Chains {
if resp.Data[i].Chains[j].Withdrawable {
withdraw = true
}
if resp.Data[i].Chains[j].Rechargeable {
deposit = true
}
}
if withdraw && deposit {
trade = true
}
payload[currency.NewCode(resp.Data[i].Coin)] = currencystate.Options{
Withdraw: &withdraw,
Deposit: &deposit,
Trade: &trade,
}
}
return bi.States.UpdateAll(a, payload)
}

// GetAvailableTransferChains returns a list of supported transfer chains based
Expand All @@ -1750,6 +1781,138 @@ func (bi *Bitget) GetAvailableTransferChains(ctx context.Context, cur currency.C
return chains, nil
}

// CalculatePNL is an overridable function to allow PNL to be calculated on an
// open position
// It will also determine whether the position is considered to be liquidated
// For live trading, an overriding function may wish to confirm the liquidation by
// requesting the status of the asset
func (bi *Bitget) CalculatePNL(ctx context.Context, req *futures.PNLCalculatorRequest) (*futures.PNLResult, error) {
if req == nil {
return nil, fmt.Errorf("%T %w", req, common.ErrNilPointer)
}

// Putting this on ice until later, I could copy the code for calculating it offline from futures.go but that seems
// bad. Also unsure whether I could call i.e. GetSinglePosition when CalculateOffline is false
return nil, common.ErrNotYetImplemented
}

// ScaleCollateral is an overridable function to determine how much
// collateral is usable in futures positions
func (bi *Bitget) ScaleCollateral(context.Context, *futures.CollateralCalculator) (*collateral.ByCurrency, error) {
// Skipping until I learn how to calculate collateral
return nil, common.ErrNotYetImplemented
}

// CalculateTotalCollateral takes in n collateral calculators to determine an overall
// standing in a singular currency
func (bi *Bitget) CalculateTotalCollateral(_ context.Context, _ *futures.TotalCollateralCalculator) (*futures.TotalCollateralResponse, error) {
// Skipping until I learn how to calculate collateral
return nil, common.ErrNotYetImplemented
}

// GetCollateralCurrencyForContract returns the collateral currency for an asset and contract pair
func (bi *Bitget) GetCollateralCurrencyForContract(a asset.Item, p currency.Pair) (currency.Code, asset.Item, error) {
// Due to the lack of a context, I can't get this to work unless I deduce the currency from the pair, which
// seems like a real waste of a function
return currency.Code{}, asset.Empty, common.ErrNotYetImplemented
}

// GetCurrencyForRealisedPNL returns where to put realised PNL
// example 1: Bybit universal margin PNL is paid out in USD to your spot wallet
// example 2: Binance coin margined futures pays returns using the same currency eg BTC
func (bi *Bitget) GetCurrencyForRealisedPNL(_ asset.Item, _ currency.Pair) (currency.Code, asset.Item, error) {
// Skipped since I'm not sure where to get this information from
return currency.Code{}, asset.Empty, common.ErrNotYetImplemented
}

// GetMarginRatesHistory returns the margin rate history for the supplied currency
func (bi *Bitget) GetMarginRatesHistory(context.Context, *margin.RateHistoryRequest) (*margin.RateHistoryResponse, error) {
// Skipped since I'm not fully sure waht this means, or where to get it from

Check failure on line 1830 in exchanges/bitget/bitget_wrapper.go

View workflow job for this annotation

GitHub Actions / Spell checker

waht ==> what
return nil, common.ErrNotYetImplemented
}

// GetFuturesPositionSummary returns stats for a future position
func (bi *Bitget) GetFuturesPositionSummary(ctx context.Context, req *futures.PositionSummaryRequest) (*futures.PositionSummary, error) {
if req == nil {
return nil, fmt.Errorf("%T %w", req, common.ErrNilPointer)
}
resp, err := bi.GetSinglePosition(ctx, getProductType(req.Pair), req.Pair.String(), req.Pair.Quote.String())
if err != nil {
return nil, err
}
if len(resp.Data) != 1 {
// I'm not sure that it should actually return one data point in this case, replace this with a properly
// formatted error message once certain
return nil, fmt.Errorf("expected 1 position, received %v", len(resp.Data))
}
summary := &futures.PositionSummary{
Pair: req.Pair,
Asset: req.Asset,
// OpenDelegateSize is "Amount to be filled of the current order", would this be AvailableEquity?
// What is MarginSize?
AvailableEquity: decimal.NewFromFloat(resp.Data[0].Available),
FrozenBalance: decimal.NewFromFloat(resp.Data[0].Locked),
Leverage: decimal.NewFromFloat(resp.Data[0].Leverage),
RealisedPNL: decimal.NewFromFloat(resp.Data[0].AchievedProfits),
AverageOpenPrice: decimal.NewFromFloat(resp.Data[0].OpenPriceAverage),
UnrealisedPNL: decimal.NewFromFloat(resp.Data[0].UnrealizedPL),
// Not sure if these are actually equivalent
MaintenanceMarginRequirement: decimal.NewFromFloat(resp.Data[0].KeepMarginRate),
MarkPrice: decimal.NewFromFloat(resp.Data[0].MarkPrice),
StartDate: resp.Data[0].CreationTime.Time(),
}

// Okay so the only two exchanges which invoke CalculateOffline use it to print a warning that they can't do that
// Why have it then?
// EstimatePosition isn't used at all either
return summary, nil
}

// GetFuturesPositions returns futures positions for all currencies
func (bi *Bitget) GetFuturesPositions(ctx context.Context, req *futures.PositionsRequest) ([]futures.PositionDetails, error) {
if req == nil {
return nil, fmt.Errorf("%T %w", req, common.ErrNilPointer)
}
var resp []futures.PositionDetails
// This exchange essentially needs these listed, since a MarginCoin has to be provided
for i := range req.Pairs {
temp, err := bi.GetAllPositions(ctx, getProductType(req.Pairs[i]), req.Pairs[i].Quote.String())
if err != nil {
return nil, err
}
for x := range temp.Data {
pair, err := pairFromStringHelper(temp.Data[x].Symbol)
if err != nil {
return nil, err
}
ord := []order.Detail{
{
Exchange: bi.Name,
AssetType: req.Asset,
Pair: pair,
Side: sideDecoder(temp.Data[x].HoldSide),
RemainingAmount: temp.Data[x].OpenDelegateSize,
// Is this accurate? If so, ExecutedAmount should = Locked, which sounds weird
Amount: temp.Data[x].Total,
Leverage: temp.Data[x].Leverage,
AverageExecutedPrice: temp.Data[x].OpenPriceAverage,
MarginType: marginDecoder(temp.Data[x].MarginMode),
// Not 100% certain about this one
Price: temp.Data[x].MarkPrice,
Date: temp.Data[x].CreationTime.Time(),
},
}
resp = append(resp, futures.PositionDetails{
Exchange: bi.Name,
Pair: pair,
Asset: req.Asset,
Orders: ord,
})
}
}
return resp, nil
}

// GetProductType is a helper function that returns the appropriate product type for a given currency pair
func getProductType(p currency.Pair) string {
var prodType string
Expand Down
Loading

0 comments on commit 913601b

Please sign in to comment.