I think I have to send 3 messages to the websocket to initialise it. In response, it seems to send the last ticker price reasonably quickly, compared to earlier when it was just waiting for the next available transaction before notifying me.
150 lines
5.1 KiB
C++
150 lines
5.1 KiB
C++
/* Copyright 2022, Michele Santullo
|
|
* This file is part of duckticker.
|
|
*
|
|
* Wrenpp is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Wrenpp is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with duckticker. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "bitcoinity_reader.hpp"
|
|
#include <wrenpp/vm_fun.hpp>
|
|
#include <wrenpp/def_configuration.hpp>
|
|
#include <wrenpp/callback_manager.hpp>
|
|
#include <utility>
|
|
|
|
namespace {
|
|
constexpr char g_script[] =
|
|
R"script(
|
|
foreign class TickerPrice {
|
|
construct new() { }
|
|
|
|
foreign price
|
|
foreign currency
|
|
foreign exchange
|
|
foreign is_valid
|
|
}
|
|
|
|
class Bitcoinity {
|
|
foreign static ticker(currency, exchange)
|
|
}
|
|
|
|
class App {
|
|
construct new(currency, exchange) {
|
|
_currency = currency
|
|
_exchange = exchange
|
|
}
|
|
|
|
start() {
|
|
var price = Bitcoinity.ticker(_currency, _exchange)
|
|
System.print("Ticker: %(price.price) %(price.currency) on %(price.exchange)")
|
|
}
|
|
}
|
|
|
|
var the_app = App.new("USD", "kraken")
|
|
)script";
|
|
|
|
class TickerPrice {
|
|
public:
|
|
TickerPrice() = default;
|
|
TickerPrice (double price, std::string&& currency, std::string&& exchange) :
|
|
m_price(price),
|
|
m_currency(std::move(currency)),
|
|
m_exchange(std::move(exchange))
|
|
{}
|
|
|
|
void set_price (double price) { m_price = price; }
|
|
void set_currency (std::string&& currency) { m_currency = std::move(currency); }
|
|
void set_exchange (std::string&& exchange) { m_exchange = std::move(exchange); }
|
|
double price() const noexcept { return m_price; }
|
|
const char* currency() const noexcept { return m_currency.c_str(); }
|
|
const char* exchange() const noexcept { return m_exchange.c_str(); }
|
|
bool is_valid() const noexcept { return not m_exchange.empty(); }
|
|
|
|
private:
|
|
double m_price{};
|
|
std::string m_currency;
|
|
std::string m_exchange;
|
|
};
|
|
|
|
TickerPrice ticker_price_bitoinity (std::string_view currency, std::string_view exchange) {
|
|
static const TickerPrice error_price {0.0, "", ""};
|
|
|
|
duck::BitcoinityReader bitcoinity{
|
|
std::string{duck::WebsocketReader::BeastVersionString} + " websocket-client-coro",
|
|
exchange,
|
|
currency
|
|
};
|
|
|
|
do {
|
|
//possible values I observed:
|
|
// {"event":"new_msg","payload":{"data":{"currency":"EUR","exchange_name":"paymium","last":28363.44}},"ref":null,"topic":"webs:markets"}
|
|
// {"event":"new_msg","payload":{"data":{"currency":"USD","depth":{"price":30071,"type":1,"volume":-1.4123},"exchange_name":"bitfinex"}},"ref":null,"topic":"webs:markets_bitfinex_USD"}
|
|
// {"event":"new_msg","payload":{"data":{"currency":"USD","exchange_name":"bitfinex","trade":{"amount":0.0486,"date":1652471682,"exchange_name":"bitfinex","price":30072}}},"ref":null,"topic":"webs:markets_bitfinex_USD"}
|
|
|
|
double price_out;
|
|
std::optional<double> price;
|
|
auto msg = bitcoinity.read();
|
|
|
|
if (msg.payload.has_key("data", "depth_shot"))
|
|
continue;
|
|
|
|
if ((price = msg.payload.get_double("data", "last")))
|
|
price_out = *price;
|
|
else if ((price = msg.payload.get_double("data", "depth", "price")))
|
|
price_out = *price;
|
|
else if ((price = msg.payload.get_double("data", "trade", "price")))
|
|
price_out = *price;
|
|
else
|
|
continue; //keep trying, sometimes I get a message like:
|
|
//{"event":"new_msg","payload":{"data":{"connected_count":1355}},"ref":null,"topic":"webs:markets"}
|
|
|
|
std::string_view currency_out = msg.payload.get_string_or_empty("data", "currency");
|
|
std::string_view exchange_out = msg.payload.get_string_or_empty("data", "exchange_name");
|
|
return {price_out, std::string(currency_out), std::string(exchange_out)};
|
|
} while (true);
|
|
}
|
|
|
|
void ticker_price_bitcoinity_wren (wren::VM& vm) {
|
|
TickerPrice* const tp = wren::make_foreign_object<TickerPrice>(vm, "main");
|
|
*tp = ticker_price_bitoinity(wren::get<const char*>(vm, 1), wren::get<const char*>(vm, 2));
|
|
}
|
|
|
|
class MyWrenConfiguration : public wren::DefConfiguration {
|
|
public:
|
|
wren::foreign_class_t foreign_class_fn(
|
|
wren::VM* vm,
|
|
std::string_view module,
|
|
std::string_view class_name
|
|
) {
|
|
if (module == "main" and class_name == "TickerPrice")
|
|
return wren::make_foreign_class<TickerPrice>();
|
|
else
|
|
return {nullptr, nullptr};
|
|
}
|
|
};
|
|
} //unnamed namespace
|
|
|
|
int main() {
|
|
MyWrenConfiguration config;
|
|
wren::VM vm(&config, nullptr);
|
|
vm.callback_manager()
|
|
.add_callback(true, "main", "Bitcoinity", "ticker(_,_)", &ticker_price_bitcoinity_wren)
|
|
.add_callback(false, "main", "TickerPrice", "price", wren::make_method_bindable<&TickerPrice::price>())
|
|
.add_callback(false, "main", "TickerPrice", "currency", wren::make_method_bindable<&TickerPrice::currency>())
|
|
.add_callback(false, "main", "TickerPrice", "is_valid", wren::make_method_bindable<&TickerPrice::is_valid>())
|
|
.add_callback(false, "main", "TickerPrice", "exchange", wren::make_method_bindable<&TickerPrice::exchange>());
|
|
|
|
vm.interpret("main", g_script);
|
|
wren::call<void>(vm, {"main", "the_app"}, "start");
|
|
|
|
return 0;
|
|
}
|