Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- #include <algo/volume/volume_strategy.hpp>
- using namespace mira;
- const int volume::VolumeBot::aux_ = StrategyBuilder::add_strategy<VolumeBot>("VOLUME_BOT");
- volume::VolumeBot::VolumeBot(const strategy_config_t& config) : SingleStrategy(config) {
- *static_cast<strategy_config_t*>(&config_) = config;
- using conv_fn = std::function<void(const std::string&)>;
- std::string telemetry_dir = "";
- std::unordered_map<std::string, conv_fn> cnv = {
- {"volume", [this](const auto& v) { config_.volume = std::stod(v); }},
- {"min_order_size", [this](const auto& v) { config_.min_order_size = std::stod(v); }},
- {"symbol", [this](const auto& v) { config_.symbol_str = v; }},
- {"narrow_spread_trading", [this](const auto& v) { config_.narrow_spread_trading = std::stoi(v); }},
- {"lower_bound", [this](const auto& v) { config_.lower_bound = std::stod(v); }}, // TODO: добавить чекер на [0, 1]
- {"upper_bound", [this](const auto& v) { config_.upper_bound = std::stod(v); }},
- {"history_mode", [this](const auto& v) { config_.history_mode = (v == "true" ? true : false); }},
- {"telemetry_dir", [&telemetry_dir](const field_t& v) { telemetry_dir = v.s(); }},
- {"max_spread", [this](const auto& v) { config_.max_spread = std::stoi(v); }},
- {"cooldown",
- [this](const auto& v) {
- config_.cooldown = std::stoi(v);
- config_.sleep_time = config_.cooldown;
- }
- },
- };
- for (const auto &[key, fn] : cnv) {
- try {
- if (config_.args.count(key)) {
- fn(config_.args[key].s());
- }
- } catch (const std::exception& e) {
- config_.logger->log("Error for key " + key + ": " + e.what());
- std::exit(EXIT_FAILURE);
- }
- }
- std::cerr << "cooldown: " << config_.cooldown << '\n';
- std::cerr << "sleep time: " << config_.sleep_time << '\n';
- account_name_t account_name;
- account_name.market = "BINANCE_FUTURES";
- account_name.name = "VOLUME_BINANCE_ORACLE";
- config_.binance_oracle = std::make_unique<binance_futures_api>(account_name, std::unordered_map<std::string, std::string>());
- stream_description_t descriptor;
- descriptor.stream = e_stream::KLINE;
- descriptor.args["symbol"] = "BTCUSDT";
- descriptor.args["interval"] = "5m";
- config_.binance_oracle->subscribe(descriptor, &config_.handler);
- descriptor.args["interval"] = "1d";
- config_.binance_oracle->subscribe(descriptor, &config_.handler);
- config_.binance_oracle->start_streams();
- std::string path_to_log_file =
- (telemetry_dir.empty() ? "/mnt/volume_telemetry/" : telemetry_dir) +
- config_.symbols.back()->base + "_" +
- config_.symbols.back()->quote + "_" +
- lower(config_.symbols.back()->api->market);
- // временный костыль
- if (config_.symbols.back()->api->market == "MEXC_SPOT") {
- path_to_log_file =
- (telemetry_dir.empty() ? "/mnt/volume_telemetry/" : telemetry_dir) +
- config_.symbols.back()->base + "_" +
- config_.symbols.back()->quote + "_mexc";
- }
- std::cerr << path_to_log_file << '\n';
- own_logger_ = std::make_shared<TelemetryLogger>(path_to_log_file, 1);
- config_.time_distribution = std::uniform_real_distribution<>{config_.cooldown / 10., config_.cooldown * 1.9};
- config_.upd_time = get_timestamp() - config_.sleep_time + 2000;
- }
- order_event volume::VolumeBot::_process_single(const market_event& event) {
- order_event res;
- if (event.type == e_market_event::TRADE) {
- auto trade = event.get<market_trade_event>().trade;
- field_t one_trade;
- one_trade["price"] = std::to_string(trade.price);
- one_trade["quantity"] = std::to_string(trade.quantity);
- one_trade["side"] = (trade.is_buy ? "buy" : "sell");
- one_trade["timestamp"] = get_timestamp();
- field_t trade_log = std::vector<field_t>();
- trade_log.v().push_back(one_trade);
- std::string string_to_log = "public_trades " + trade_log.serialize();
- own_logger_->log(string_to_log);
- return res;
- }
- if (get_timestamp() < config_.upd_time + config_.sleep_time) { // 0-момент сюда не попадёт
- return res;
- }
- if (event.type == e_market_event::ORDER_BOOK) {
- auto orderbook_event = event.get<market_order_book_event>();
- config_.upd_time = get_timestamp();
- auto best_book = config_.shared_state->get_data_getter(event.symbol).get_best_book();
- return calculate_trade_event(event, best_book);
- } else if (event.type == e_market_event::TICK) {
- auto best_book = config_.shared_state->get_data_getter(event.symbol).get_best_book();
- config_.upd_time = get_timestamp();
- return calculate_trade_event(event, best_book);
- }
- return res;
- }
- double volume::VolumeBot::calculate_deviated_value(std::normal_distribution<double>&& distribution, double lower_bound, double upper_bound) {
- auto sampled_value = distribution(config_.gen);
- return std::max(std::min(sampled_value, upper_bound), lower_bound);
- }
- order_event volume::VolumeBot::kline_trade_calculation(const ticker_t* symbol, double lower_bound, double upper_bound) {
- order_event res;
- std::vector<kline_t> btc_klines_5m = config_.binance_oracle->get_ws_klines(nullptr, e_candlestick_interval::K_5m, 60);;
- double btc_turnover_1d = 0;
- double current_volume_5m;
- if (config_.history_mode) {
- int64_t start_day = 86400 * std::floor(get_timestamp() / 86400000.);
- int since_start_day_5m = std::floor((get_timestamp() / 1000. - start_day) / 300.);
- btc_turnover_1d = config_.binance_oracle->get_ws_klines(nullptr, e_candlestick_interval::K_1d, 2)[0].turnover;
- double corresponding_history_5m = config_.binance_oracle->get_ws_klines(nullptr, e_candlestick_interval::K_5m, 289 + since_start_day_5m)[since_start_day_5m].turnover;
- current_volume_5m = config_.volume * (corresponding_history_5m / btc_turnover_1d);
- } else {
- auto btc_1d_ticker = config_.binance_oracle->get_ws_klines(nullptr, e_candlestick_interval::K_5m, 289);
- for (size_t i = 0; i < btc_1d_ticker.size() - 1; ++i) {
- btc_turnover_1d += btc_1d_ticker[i].turnover;
- }
- current_volume_5m = config_.volume * (btc_klines_5m[btc_klines_5m.size() - 2].turnover / btc_turnover_1d);
- }
- std::cerr << "current_volume_5m: " << current_volume_5m << '\n';
- std::cerr << "btc_turnover_1d: " << btc_turnover_1d << '\n';
- double price_max = btc_klines_5m[0].close.to_double();
- double price_min = btc_klines_5m[0].close.to_double();
- for (const auto& kline : btc_klines_5m) {
- price_max = std::max(price_max, kline.close.to_double());
- price_min = std::min(price_min, kline.close.to_double());
- }
- if (price_max == btc_klines_5m[btc_klines_5m.size() - 1].close.to_double()) {
- config_.logger->log("now bitcoin 5 hour ATH");
- } else if (price_min == btc_klines_5m[btc_klines_5m.size() - 1].close.to_double()) {
- config_.logger->log("now bitcoin 5 hour minimum");
- }
- config_.sleep_time = static_cast<size_t>(config_.time_distribution(config_.gen));
- double current_volume = current_volume_5m * (config_.sleep_time / 300000.); // 300 * 1000
- std::cerr << "price_max: " << price_max << '\n';
- std::cerr << "price_min: " << price_min << '\n';
- std::cerr << "sleep_time: " << config_.sleep_time << '\n';
- std::cerr << "current_volume: " << current_volume << '\n';
- double step_size = symbol->price_step.to_double();
- auto best_book = config_.shared_state->get_data_getter(symbol).get_best_book();
- double norm_price_coeff = (upper_bound - lower_bound) * (btc_klines_5m[btc_klines_5m.size() - 1].close.to_double() - price_min) / (price_max - price_min) + lower_bound;
- std::cerr << "btc_klines_5m[btc_klines_5m.size() - 1].close: " << btc_klines_5m[btc_klines_5m.size() - 1].close << '\n';
- std::cerr << "norm_price_coeff: " << norm_price_coeff << '\n';
- std::cerr << "(best_book.ask_price - best_book.bid_price - step_size) * norm_price_coeff: " << (best_book.ask_price - best_book.bid_price - step_size) * norm_price_coeff << '\n';
- std::cerr << "(best_book.bid_price + step_size / 2): " << (best_book.bid_price + step_size / 2) << '\n';
- double current_price = (best_book.bid_price + step_size / 2) + (best_book.ask_price - best_book.bid_price - step_size) * norm_price_coeff;
- std::cerr << "current_price: " << current_price << '\n';
- if (current_price >= best_book.ask_price || current_price <= best_book.bid_price) {
- config_.logger->log("current_price >= best_book.ask_price || current_price <= best_book.bid_price");
- return res;
- }
- double quote_balance = config_.shared_state->get_balance_getter(symbol->api).get_balance(symbol->quote).total();
- double base_balance = config_.shared_state->get_balance_getter(symbol->api).get_balance(symbol->base).total();
- double max_trade_available_quantity = std::min(base_balance, quote_balance / current_price) - 1 / current_price;
- std::cerr << "max_trade_available_quantity: " << max_trade_available_quantity << '\n';
- if (max_trade_available_quantity * current_price < config_.min_order_size) {
- std::cerr << "max_trade_available_quantity * current_price < min_order_size\n";
- return res;
- }
- double calculated_current_volume = calculate_deviated_value(std::normal_distribution<>{current_volume, current_volume / 3}, 2 * current_volume / 3, 4 * current_volume / 3);
- std::cerr<< "calculated_current_volume: " << calculated_current_volume << '\n';
- double current_quantity = std::min(max_trade_available_quantity, std::max(calculated_current_volume, 5.5) / current_price);
- // TODO: наверное не нужно будет
- // double current_quantity = std::max(calculated_current_volume, 5.5) / current_price;
- // if (current_quantity * current_price >= quote_balance - 1) {
- // config_.logger->log("current_quantity * current_price >= quote_balance - 1");
- // res.set(order_taker_sell_event(current_quantity)); // TODO: < 5$ может быть
- // res.set_symbol(symbol);
- // return res;
- // }
- // if (current_quantity >= base_balance - 1 / current_price) {
- // config_.logger->log("current_quantity >= base_balance - 1 / current_price");
- // res.set(order_taker_buy_event(0, current_quantity)); // TODO: < 5$ может быть
- // res.set_symbol(symbol);
- // return res;
- // }
- std::cerr << "current_quantity: " << current_quantity << '\n';
- std::cerr << "price/quantity: " << current_price << "/" << current_quantity << '\n';
- res.set(order_self_trade_event(current_price, current_quantity));
- res.set_symbol(symbol);
- config_.logger->log(res, e_severity_level::DEBUG, "volume strategy result");
- return res;
- }
- order_event volume::VolumeBot::calculate_trade_event(const market_event& event, const float_book_t& best_book) {
- order_event res;
- field_t book_log;
- book_log["ask_price"] = std::to_string(best_book.ask_price);
- book_log["bid_price"] = std::to_string(best_book.bid_price);
- std::string string_to_log = "book " + book_log.serialize();
- own_logger_->log(string_to_log);
- auto& balance_getter = config_.shared_state.get()->get_balance_getter(event.symbol->api);
- double base_balance = balance_getter.get_balance(event.symbol->base).total();
- double quote_balance = balance_getter.get_balance(event.symbol->quote).total();
- config_.logger->log(balance_getter.get_balance(event.symbol->base), e_severity_level::DEBUG, "base balance");
- config_.logger->log(balance_getter.get_balance(event.symbol->quote), e_severity_level::DEBUG, "quote balance");
- field_t balance_log;
- balance_log["base"] = std::to_string(base_balance);
- balance_log["quote"] = std::to_string(quote_balance);
- string_to_log = "balance " + balance_log.serialize();
- own_logger_->log(string_to_log);
- if (
- balance_getter.get_balance(event.symbol->base).locked ||
- balance_getter.get_balance(event.symbol->quote).locked
- ) {
- res.set(
- order_limit_grid_event(
- std::vector<limit_order_request_t>(),
- std::vector<limit_order_request_t>()
- )
- );
- res.set_symbol(event.symbol);
- return res;
- }
- if (!best_book.ask_price || !best_book.bid_price) {
- config_.logger->log("No ask or bid");
- return res;
- }
- if (config_.max_spread && (best_book.ask_price / best_book.bid_price - 1 > config_.max_spread / 10000.)) {
- own_logger_->log("STOPPED WIDE SPREAD");
- return res;
- }
- auto ask_price = event.symbol->make_price(best_book.ask_price);
- auto bid_price = event.symbol->make_price(best_book.bid_price);
- std::cout << "ask: " << ask_price << '\n';
- std::cout << "bid: " << bid_price << '\n';
- size_t spread_ticks = (ask_price - bid_price).to_scaled();
- if (spread_ticks == event.symbol->price_step.to_scaled()) {
- if (config_.narrow_spread_trading) {
- config_.logger->log("dealing with narrow spread");
- double current_volume = calculate_deviated_value(std::normal_distribution<>{config_.volume, config_.volume / 3}, 0.7 * config_.volume, 1.3 * config_.volume);
- double ask_trade_quantity = std::min(current_volume / (2 * ask_price.to_double()), best_book.ask_quantity);
- double bid_trade_quantity = std::min(current_volume / (2 * bid_price.to_double()), best_book.bid_quantity);
- limit_order_request_t buy_limit_req;
- buy_limit_req.level.price = ask_price.to_double();
- buy_limit_req.level.quantity = ask_trade_quantity;
- buy_limit_req.repeat_config = repeated_request_config_t{0, 3, 100};
- limit_order_request_t sell_limit_req;
- sell_limit_req.level.price = bid_price.to_double();
- sell_limit_req.level.quantity = bid_trade_quantity;
- sell_limit_req.repeat_config = repeated_request_config_t{0, 3, 100};
- res.set(
- order_limit_grid_event(
- std::vector<limit_order_request_t>{sell_limit_req},
- std::vector<limit_order_request_t>{buy_limit_req}
- )
- );
- res.set_symbol(event.symbol);
- config_.logger->log(res, e_severity_level::INFO, "dealing with narrow spread result");
- return res;
- }
- return res;
- }
- res = kline_trade_calculation(event.symbol, config_.lower_bound, config_.upper_bound);
- res.set_symbol(event.symbol);
- return res;
- }
- order_event volume::VolumeBot::_process_single(const fill_event& event) {
- auto fill_ev = event.get<fill_order_event>();
- if (fill_ev.order) { // т.е account order
- if (
- fill_ev.order.value().status == e_order_status::FILLED ||
- fill_ev.order.value().status == e_order_status::PARTIALLY_FILLED
- ) {
- field_t one_trade;
- one_trade["price"] = std::to_string(fill_ev.fill.fill_quote / fill_ev.fill.fill_base);
- one_trade["quantity"] = std::to_string(fill_ev.fill.fill_base);
- one_trade["side"] = (fill_ev.order.value().side == e_side::BUY ? "buy" : "sell");
- one_trade["timestamp"] = get_timestamp();
- field_t trade_log = std::vector<field_t>();
- trade_log.v().push_back(one_trade);
- std::string string_to_log = "private_trades " + trade_log.serialize();
- own_logger_->log(string_to_log);
- }
- }
- return order_event();
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement