adshao / go-binance

A Go SDK for Binance API
MIT License
1.48k stars 662 forks source link

complete options module #568

Closed xyq-c-cpp closed 1 week ago

xyq-c-cpp commented 3 weeks ago

The complete implementation of the options module.

  1. Implement unimplemented rest interfaces
  2. Add implementation of websocket, all interfaces have been implemented
  3. Fix some bugs
    1. Fix the interface for batch order placement and cancellation. The original interface implementation was completely copied from the Futures module, but compared to the Binance official documentation, it was found that the parameter names were completely incorrect and could not be used in real transactions
    2. In the account and transaction interface section, unify the return structure of interfaces related to orders and use the Order structure uniformly.
      1. Why do we need to do this? Because the previous implementation appears to have been implemented and passed unit testing. But there are still many errors in the actual test. The reason why the order structure can be unified is that this module has not yet been fully implemented, and it would be best to do so now.
  4. Supplement missing unit tests

All newly added rest interfaces and websocket interfaces have been tested on real drives and passed. Including the original interface, after modification and adjustment, it has also been tested on real drives and passed.

By the way, I wanna know whether you mind inviting me as a collaborator with the write or maintain privilege. I am pleased if you don't mind. i'm sorry if you mind. thanks a lot. @adshao

Here is an example of my actual test code(not contain api_key and api_secret):

package main

import (
    "context"
    "fmt"
    "io"
    "log"
    "os"
    "time"

    //lbinance "github.com/adshao/go-binance/v2"
    options "github.com/adshao/go-binance/v2/options"
)

func main() {
    log.SetFlags(log.LstdFlags | log.Lshortfile)
    logFile, err := os.OpenFile("debug.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
    if err != nil {
        log.Fatal("cannot open file ", err)
    }
    defer logFile.Close()
    mw := io.MultiWriter(os.Stdout, logFile)
    logger := log.New(mw, "", log.Ldate|log.Ltime|log.Lshortfile)
    log.SetOutput(mw)

    client := options.NewProxiedClient("",
        "", "http://192.168.0.202:7890")
    client.Logger = logger
    client.Debug = false

    err = client.NewPingService().Do(context.Background())
    if err != nil {
        log.Printf("NewPingService err=%+v", err)
    } else {
        log.Printf("NewPingService pass")
    }

    log.Println("")

    ei, err := client.NewExchangeInfoService().Do(context.Background())
    if err != nil {
        log.Printf("NewExchangeInfoService err=%+v", err)
    } else {
        log.Printf("NewExchangeInfoService=%+v", *ei)
    }

    log.Println("")

    symbol := "DOGE-240614-0.19-C"
    dep, err := client.NewDepthService().Symbol(symbol).Limit(
        10).Do(context.Background())
    if err != nil {
        log.Printf("NewDepthService err=%+v", err)
    } else {
        log.Printf("NewDepthService=%+v", *dep)
    }

    log.Println("")

    trades, err := client.NewTradesService().Symbol(symbol).Limit(
        10).Do(context.Background())
    if err != nil {
        log.Printf("NewTradesService err=%+v", err)
    } else {
        log.Printf("NewTradesService=%+v", trades)
    }

    log.Println("")

    historicalTrades, err := client.NewHistoricalTradesService().Symbol(symbol).Limit(
        10).Do(context.Background())
    if err != nil {
        log.Printf("NewHistoricalTradesService err=%+v", err)
    } else {
        log.Printf("NewHistoricalTradesService=%+v", historicalTrades)
    }

    log.Println("")

    klines, err := client.NewKlinesService().Symbol(symbol).Interval("15m").Limit(
        10).Do(context.Background())
    if err != nil {
        log.Printf("NewKlinesService err=%+v", err)
    } else {
        log.Printf("NewKlinesService=%+v", klines)
    }

    log.Println("")

    pair := "DOGEUSDT"

    mps, err := client.NewMarkService().Symbol(symbol).Do(context.Background())
    if err != nil {
        log.Printf("NewMarkService err=%+v", err)
    } else {
        log.Printf("NewMarkService=%+v", mps)
    }

    log.Println("")

    tickers, err := client.NewTickerService().Symbol(symbol).Do(context.Background())
    if err != nil {
        log.Printf("NewTickerService err=%+v", err)
    } else {
        log.Printf("NewTickerService=%+v", tickers)
    }

    log.Println("")

    indexs, err := client.NewIndexService().Underlying(pair).Do(context.Background())
    if err != nil {
        log.Printf("NewIndexService err=%+v", err)
    } else {
        log.Printf("NewIndexService=%+v", *indexs)
    }
    log.Println("")

    ehs, err := client.NewExerciseHistoryService().Underlying(pair).Do(context.Background())
    if err != nil {
        log.Printf("NewExerciseHistoryService err=%+v", err)
    } else {
        log.Printf("NewExerciseHistoryService=%+v", ehs)
    }
    log.Println("")

    time.Sleep(time.Second * 10)

    asset := "DOGE"

    ois, err := client.NewOpenInterestService().UnderlyingAsset(asset).Expiration("240614").Do(context.Background())
    if err != nil {
        log.Printf("NewOpenInterestService err=%+v", err)
    } else {
        log.Printf("NewOpenInterestService=%+v", ois)
    }
    log.Println("")

    account, err := client.NewAccountService().Do(context.Background())
    if err != nil {
        log.Printf("NewAccountService err=%+v", err)
    } else {
        log.Printf("NewAccountService=%+v", *account)
    }
    options.SetWsProxyUrl("http://192.168.0.202:7890")

    listenKey, err := client.NewStartUserStreamService().Do(context.Background())
    if err != nil {
        log.Printf("NewStartUserStreamService err=%+v\n", listenKey)
    } else {
        log.Printf("NewStartUserStreamService listenKey=%+v\n", listenKey)
        doneC, _, err := options.WsUserDataServe(listenKey,
            func(event *options.WsUserDataEvent) {
                if event == nil {
                    log.Printf("WsUserDataServe Handler event=nil\n")
                    return
                }
                log.Printf("WsUserDataServe Handler WsUserDataEvent=%+v\n", *event)

            }, func(err error) {
                log.Printf("WsUserDataServe Handler err=%+v\n", err)
            })
        if err != nil {
            log.Printf("WsUserDataServe err=%+v", err)
        }
        defer func() {
            doneC <- struct{}{}
        }()
        time.Sleep(3 * time.Second)
    }

    price := "0.1100"
    quantity := "0.10"
    clientOrderId1 := "24060901"
    clientOrderId2 := "24060901"
    clientOrderId3 := "24060901"
    // clientOrderId4 := "24060804"
    // clientOrderId5 := "24060805"
    side := options.SideTypeBuy
    t := options.OrderTypeLimit

    order, err := client.NewCreateOrderService().Symbol(symbol).Side(
        side).Price(price).Quantity(quantity).ClientOrderId(
        clientOrderId1).Type(t).Do(context.Background())
    if err != nil {
        log.Printf("NewCreateOrderService err=%+v", err)
    } else {
        log.Printf("NewCreateOrderService=%+v", *order)
    }

    cos1 := options.CreateOrderService{}
    cos1.Symbol(symbol)
    cos1.Side(side)
    cos1.Type(t)
    cos1.Price(price)
    cos1.Quantity(quantity)
    cos1.ClientOrderId(clientOrderId2)

    cos2 := options.CreateOrderService{}
    cos2.Symbol(symbol)
    cos2.Side(side)
    cos2.Type(t)
    cos2.Price(price)
    cos2.Quantity(quantity)
    cos2.ClientOrderId(clientOrderId3)

    orderList := []*options.CreateOrderService{
        &cos1,
        &cos2,
    }

    orders, err := client.NewCreateBatchOrdersService().OrderList(
        orderList).Do(context.Background())
    if err != nil {
        log.Printf("NewCreateBatchOrdersService err=%+v", err)
    } else {
        log.Printf("NewCreateBatchOrdersService=%+v", orders)
    }

    o, err := client.NewGetOrderService().Symbol(
        symbol).ClientOrderId(clientOrderId1).Do(context.Background())
    if err != nil {
        log.Printf("NewGetOrderService err=%+v", err)
    } else {
        log.Printf("NewGetOrderService=%+v", o)
    }

    openOrders, err := client.NewListOpenOrdersService().Symbol(
        symbol).Do(context.Background())
    if err != nil {
        log.Printf("NewListOpenOrdersService err=%+v", err)
    } else {
        log.Printf("NewListOpenOrdersService=%+v", openOrders)
    }

    co, err := client.NewCancelOrderService().Symbol(
        symbol).ClientOrderId(clientOrderId1).Do(context.Background())
    if err != nil {
        log.Printf("NewCancelOrderService err=%+v", err)
    } else {
        log.Printf("NewCancelOrderService=%+v", co)
    }

    clientOrderIds := []string{clientOrderId1, clientOrderId2}

    cbo, err := client.NewCancelBatchOrdersService().Symbol(
        symbol).ClientOrderIds(clientOrderIds).Do(context.Background())
    if err != nil {
        log.Printf("NewCancelBatchOrdersService err=%+v", err)
    } else {
        log.Printf("NewCancelBatchOrdersService=%+v", cbo)
    }

    caoo, err := client.NewCancelAllOpenOrdersService().Symbol(
        symbol).Do(context.Background())
    if err != nil {
        log.Printf("NewCancelAllOpenOrdersService err=%+v", err)
    } else {
        log.Printf("NewCancelAllOpenOrdersService=%+v", caoo)
    }

    time.Sleep(30 * time.Second)

    underlying := "DOGEUSDT"

    caobu, err := client.NewCancelAllOpenOrdersByUnderlyingService().Underlying(
        underlying).Do(context.Background())
    if err != nil {
        log.Printf("NewCancelAllOpenOrdersByUnderlyingService err=%+v", err)
    } else {
        log.Printf("NewCancelAllOpenOrdersByUnderlyingService=%+v", caobu)
    }

    hos, err := client.NewHistoryOrdersService().Symbol(
        symbol).Do(context.Background())
    if err != nil {
        log.Printf("NewHistoryOrdersService err=%+v", err)
    } else {
        log.Printf("NewHistoryOrdersService=%+v", hos)
    }

    ps, err := client.NewPositionService().Do(context.Background())
    if err != nil {
        log.Printf("NewPositionService err=%+v", err)
    } else {
        log.Printf("NewPositionService=%+v", ps)
    }

    uts, err := client.NewUserTradesService().Symbol(symbol).Do(context.Background())
    if err != nil {
        log.Printf("NewUserTradesService err=%+v", err)
    } else {
        log.Printf("NewUserTradesService=%+v", uts)
    }

    ers, err := client.NewExercistRecordService().Symbol(symbol).Do(context.Background())
    if err != nil {
        log.Printf("NewExercistRecordService err=%+v", err)
    } else {
        log.Printf("NewExercistRecordService=%+v", ers)
    }

    bills, err := client.NewBillService().Currency("USDT").Do(context.Background())
    if err != nil {
        log.Printf("NewBillService err=%+v", err)
    } else {
        log.Printf("NewBillService=%+v", bills)
    }

    n := uint64(time.Now().UnixMilli())
    st := n - (10 * 24 * 60 * 60 * 1000)
    et := n

    did, err := client.NewIncomeDownloadIdService().StartTime(st).EndTime(et).Do(context.Background())
    if err != nil {
        log.Printf("NewIncomeDownloadIdService err=%+v", err)
    } else {
        log.Printf("NewIncomeDownloadIdService=%+v", did)

        link, err := client.NewIncomeDownloadLinkService().DownloadId(
            did.DownloadId).Do(context.Background())
        if err != nil {
            log.Printf("NewIncomeDownloadLinkService err=%+v", err)
        } else {
            log.Printf("NewIncomeDownloadLinkService=%+v", link)
        }
    }

    doneC, _, err := options.WsTradeServe("ETH", func(event *options.WsTradeEvent) {
        log.Printf("WsTradeServe Handler event=%+v\n", *event)
    }, func(err error) {
        log.Printf("WsTradeServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsTradeServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    doneC, _, err = options.WsIndexServe("ETHUSDT", func(event *options.WsIndexEvent) {
        log.Printf("WsIndexServe Handler event=%+v\n", *event)
    }, func(err error) {
        log.Printf("WsIndexServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsIndexServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    doneC, _, err = options.WsMarkPriceServe("ETH", func(event []*options.WsMarkPriceEvent) {
        log.Printf("WsMarkPriceServe Handler event=%+v\n", event)
    }, func(err error) {
        log.Printf("WsMarkPriceServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsMarkPriceServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    doneC, _, err = options.WsKlineServe(symbol, "15m", func(event *options.WsKlineEvent) {
        log.Printf("WsKlineServe Handler event=%+v\n", *event)
    }, func(err error) {
        log.Printf("WsKlineServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsKlineServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    doneC, _, err = options.WsTickerServe(symbol, func(event []*options.WsTickerEvent) {
        log.Printf("WsTickerServe Handler event=%+v\n", event)
    }, func(err error) {
        log.Printf("WsTickerServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsTickerServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    doneC, _, err = options.WsTickerWithExpireServe(symbol, "240614", func(event []*options.WsTickerEvent) {
        log.Printf("WsTickerWithExpireServe Handler event=%+v\n", event)
    }, func(err error) {
        log.Printf("WsTickerWithExpireServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsTickerWithExpireServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    doneC, _, err = options.WsOpenInterestServe("BTC", "240611", func(event []*options.WsOpenInterestEvent) {
        log.Printf("WsOpenInterestServeHandler event=%+v\n", event)
    }, func(err error) {
        log.Printf("WsOpenInterestServeerrHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsOpenInterestServefailed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    doneC, _, err = options.WsOptionPairServe(func(event *options.WsOptionPairEvent) {
        log.Printf("WsOptionPairServe Handler event=%+v\n", event)
    }, func(err error) {
        log.Printf("WsOptionPairServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsOptionPairServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    doneC, _, err = options.WsDepthServe(symbol, "10", nil, func(event *options.WsDepthEvent) {
        log.Printf("WsDepthServe Handler event=%+v\n", event)
    }, func(err error) {
        log.Printf("WsDepthServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsDepthServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    combinedSymbol := "ETH-240609-4025-C"

    streamName := []string{
        fmt.Sprintf("%s@ticker", combinedSymbol),
        "DOGE@ticker@240609",
    }
    handler := map[string]interface{}{
        "ticker": (func(event []*options.WsTickerEvent) {
            log.Printf("WsCombinedServe Handler ticker event=%+v\n", event)
        }),
    }

    doneC, _, err = options.WsCombinedServe(streamName, handler, func(err error) {
        log.Printf("WsCombinedServe errHandler err=%+v", err)
    })
    if err != nil {
        log.Printf("call WsCombinedServe failed, err=%+v", err)
    } else {
        defer func() {
            doneC <- struct{}{}
        }()
    }

    time.Sleep(time.Second * 15)
}

Actual transaction test logs: debug.log

@adshao

adshao commented 1 week ago

Thank you so much! @xyq-c-cpp I appreciate your great work. I’m pleased to add you as a maintainer of this repo and look forward to more contributions from you.