niXman / binapi

Binance API C++ implementation
Apache License 2.0
252 stars 85 forks source link

Using User Data Stream functionality #37

Closed noobaldrin closed 3 years ago

noobaldrin commented 3 years ago

Hello,

Is there an on-going issue with userdata() in your library?

I've tried testing my app with https://binance-exchange.github.io/websocket-demo/ and it seem to work with the same listen key generated by your api however when I try your main.cpp located in the root folder of your repository, it doesn't seem to receive or show any results for example when opening an order.

niXman commented 3 years ago

https://github.com/niXman/binapi/tree/master/examples/websockets

noobaldrin commented 3 years ago

https://github.com/niXman/binapi/tree/master/examples/websockets

Hi I'm talking specifically about the userdata() function, there's not a userdata() in the example but I saw it in the main.cpp in your repository's root directory and it's not working for me.

niXman commented 3 years ago

ah, sorry..

then please provide a compilable code example demonstrating the problem.

noobaldrin commented 3 years ago

Ok sure. This is from the main.cpp that I've mentioned but omitting some unrelated codes.

#include "binapi/websocket.hpp"
#include "binapi/api.hpp"
#include "binapi/pairslist.hpp"
#include "binapi/reports.hpp"
#include "binapi/flatjson.hpp"

#include <boost/asio/io_context.hpp>
#include <boost/asio/steady_timer.hpp>

#include <iostream>
#include <fstream>
#include <binapi/errors.hpp>

int main(int argc, char **argv) {
    assert(argc == 3);
    const std::string pk = argv[1];
    const std::string sk = argv[2];

    std::cout.precision(8);

    boost::asio::io_context ioctx;
    binapi::ws::websockets wsp(ioctx, "testnet.binance.vision", "9443");

    binapi::rest::api api(
         ioctx
        ,"testnet.binance.vision"
        ,"443"
        ,pk
        ,sk
        ,10000
    );

    auto start_uds = api.start_user_data_stream();
    std::cout << "start_uds=" << start_uds.v << std::endl << std::endl;

    auto user_data_stream = wsp.userdata(start_uds.v.listenKey.c_str(),
        [](const char *fl, int ec, std::string errmsg, binapi::userdata::account_update_t msg) -> bool {
             if ( ec ) {
                 std::cout << "account update: fl=" << fl << ", ec=" << ec << ", errmsg: " << errmsg << ", msg: " << msg << std::endl;
                 return false;
             }

             std::cout << "account update:\n" << msg << std::endl;
             return true;
        }
        ,[](const char *fl, int ec, std::string errmsg, binapi::userdata::balance_update_t msg) -> bool {
            if ( ec ) {
                std::cout << "balance update: fl=" << fl << ", ec=" << ec << ", errmsg: " << errmsg << ", msg: " << msg << std::endl;
                return false;
            }

            std::cout << "balance update:\n" << msg << std::endl;
            return true;
        }
        ,[](const char *fl, int ec, std::string errmsg, binapi::userdata::order_update_t msg) -> bool {
            if ( ec ) {
                std::cout << "order update: fl=" << fl << ", ec=" << ec << ", errmsg: " << errmsg << ", msg: " << msg << std::endl;
                return false;
            }

            std::cout << "order update:\n" << msg << std::endl;
            return true;
        }
    );

    boost::asio::steady_timer unsubscribe_timer{ioctx};
    unsubscribe_timer.expires_after(std::chrono::seconds{20});
    unsubscribe_timer.async_wait(
        [user_data_stream, &wsp]
        (const boost::system::error_code &) {
            std::cout << "unsubscribe userdata stream" << std::endl;
            wsp.unsubscribe(user_data_stream);
        }
    );

    auto ping_uds = api.ping_user_data_stream(start_uds.v.listenKey);
    std::cout << "ping_uds=" << ping_uds.v << std::endl << std::endl;

    ioctx.run();

    return 0;
}
niXman commented 3 years ago

thanks,

please try to run this changed code:

#include "binapi/websocket.hpp"
#include "binapi/api.hpp"
#include "binapi/pairslist.hpp"
#include "binapi/reports.hpp"
#include "binapi/flatjson.hpp"

#include <boost/asio/io_context.hpp>
#include <boost/asio/steady_timer.hpp>

#include <iostream>
#include <fstream>
#include <binapi/errors.hpp>

int main(int argc, char **argv) {
    assert(argc == 3);
    const std::string pk = argv[1];
    const std::string sk = argv[2];

    std::cout.precision(8);

    boost::asio::io_context ioctx;
    binapi::ws::websockets wsp(ioctx, "testnet.binance.vision", "9443");

    binapi::rest::api api(
         ioctx
        ,"testnet.binance.vision"
        ,"443"
        ,pk
        ,sk
        ,10000
    );

    auto start_uds = api.start_user_data_stream();
    std::cout << "start_uds=" << start_uds.v << std::endl << std::endl; // point 0 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    auto user_data_stream = wsp.userdata(start_uds.v.listenKey.c_str(),
        [](const char *fl, int ec, std::string errmsg, binapi::userdata::account_update_t msg) -> bool {
             if ( ec ) {
                 std::cout << "account update: fl=" << fl << ", ec=" << ec << ", errmsg: " << errmsg << ", msg: " << msg << std::endl;
                 return false;
             }

             std::cout << "account update:\n" << msg << std::endl;
             return true;
        }
        ,[](const char *fl, int ec, std::string errmsg, binapi::userdata::balance_update_t msg) -> bool {
            if ( ec ) {
                std::cout << "balance update: fl=" << fl << ", ec=" << ec << ", errmsg: " << errmsg << ", msg: " << msg << std::endl;
                return false;
            }

            std::cout << "balance update:\n" << msg << std::endl;
            return true;
        }
        ,[](const char *fl, int ec, std::string errmsg, binapi::userdata::order_update_t msg) -> bool {
            if ( ec ) {
                std::cout << "order update: fl=" << fl << ", ec=" << ec << ", errmsg: " << errmsg << ", msg: " << msg << std::endl;
                return false;
            }

            std::cout << "order update:\n" << msg << std::endl;
            return true;
        }
    );

    auto ping_uds = api.ping_user_data_stream(start_uds.v.listenKey);
    std::cout << "ping_uds=" << ping_uds.v << std::endl << std::endl; // point 1 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    ioctx.run();

    return 0;
}

(you should to change any char for listenKey string before posting)

noobaldrin commented 3 years ago

Please notice that I've changed the endpoints to testnet.binance.vision

Here's something that I expect to get from the code I provided.

>{"result":null,"id":1}

>{"stream":"HiUYbSPLhoyFxFy4mlVgxovIdQMoRq38W5HipKBxM7rghScPwwXMd5MSeHT0","data":{"e":"executionReport","E":1623846480417,"s":"XRPBUSD","c":"baOX6C2nBzkivMa2SzAaoA","S":"SELL","o":"LIMIT","f":"GTC","q":"24.00000000","p":"1.12000000","P":"0.00000000","F":"0.00000000","g":-1,"C":"","x":"NEW","X":"NEW","r":"NONE","i":7324,"l":"0.00000000","z":"0.00000000","L":"0.00000000","n":"0","N":null,"T":1623846480416,"t":-1,"I":15007,"w":true,"m":false,"M":false,"O":1623846480416,"Z":"0.00000000","Y":"0.00000000","Q":"0.00000000"}}

>{"stream":"HiUYbSPLhoyFxFy4mlVgxovIdQMoRq38W5HipKBxM7rghScPwwXMd5MSeHT0","data":{"e":"outboundAccountPosition","E":1623846480417,"u":1623846480416,"B":[{"a":"BUSD","f":"10021.05760000","l":"0.00000000"},{"a":"XRP","f":"567.00000000","l":"700.00000000"}]}}

>End of subscription.

I got this result from https://binance-exchange.github.io/websocket-demo/ when opening an order.

noobaldrin commented 3 years ago

Hi I'm sorry. I'm still getting the same result. Still not receiving data from opening an order.

niXman commented 3 years ago

please show me the output for point 0 and 1. (you should to change any char for listenKey string before posting)

noobaldrin commented 3 years ago

Output for point 0: start_uds=<changed listenKey>

Output for point 1: ping_uds={"ok":true}

niXman commented 3 years ago

please put this codeline std::cout << "msg.data: " << msg.data << std::endl; into this line and provide me the output.

noobaldrin commented 3 years ago

Hi I've cleaned my build directory and recompiled everything.

I don't see an added output like "msg.data: "

It's exactly the same.

noobaldrin commented 3 years ago
websockets::handle websockets::userdata(
     const char *lkey
    ,on_account_update_cb account_update
    ,on_balance_update_cb balance_update
    ,on_order_update_cb order_update)
{
    auto cb = [acb=std::move(account_update), bcb=std::move(balance_update), ocb=std::move(order_update)]
        (const char *fl, int ec, std::string errmsg, userdata::userdata_stream_t msg)
    {
        if ( ec ) {
            acb(fl, ec, errmsg, userdata::account_update_t{});
            bcb(fl, ec, errmsg, userdata::balance_update_t{});
            ocb(fl, ec, std::move(errmsg), userdata::order_update_t{});

            return false;
        }
        std::cout << "msg.data: " << msg.data << std::endl; // <<<<<<<<<<<<< ADDED LINE
        const flatjson::fjson json{msg.data.c_str(), msg.data.length()};
        assert(json.contains("e"));
        const auto e = json.at("e");
        const auto es = e.to_sstring();
        const auto ehash = fnv1a(es.data(), es.size());
        switch ( ehash ) {
            case fnv1a("outboundAccountPosition"): {
                userdata::account_update_t res = userdata::account_update_t::construct(json);
                return acb(fl, ec, std::move(errmsg), std::move(res));
            }
            case fnv1a("balanceUpdate"): {
                userdata::balance_update_t res = userdata::balance_update_t::construct(json);
                return bcb(fl, ec, std::move(errmsg), std::move(res));
            }
            case fnv1a("executionReport"): {
                userdata::order_update_t res = userdata::order_update_t::construct(json);
                return ocb(fl, ec, std::move(errmsg), std::move(res));
            }
            case fnv1a("listStatus"): {
                assert(!"not implemented");
                return false;
            }
            default: {
                assert(!"unreachable");
                return false;
            }
        }

        return false;
    };

    return pimpl->start_channel(nullptr, lkey, std::move(cb));
}
niXman commented 3 years ago

hmm... smth was broken according userdata... I need some time to fix it.

noobaldrin commented 3 years ago

Sure thing man. If I find anything I'll let you know though I don't fully understand the library yet.

noobaldrin commented 3 years ago

Hi dude, it's me again.

I added this in cmake: add_definitions(-DBOOST_ASIO_ENABLE_HANDLER_TRACKING) to track the chain of boost asio handlers (or was it called completion handlers?). On testnet it gets stuck on async_connect(). It turns out that websocket for testnet uses port number 443 instead of 9443. I'm getting a different error this time but I can see that it is able to send/receive data.

I tested on my live api account and your binapi is working as it should.

niXman commented 3 years ago

=)

then close please.

noobaldrin commented 3 years ago

Ok.. But I'll update if there's a way to make the testnet to work.

Thanks for the time.