Advertisement
den4ik2003

Untitled

Jan 10th, 2025
12
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.92 KB | None | 0 0
  1. #include <algo/volume/volume_strategy.hpp>
  2.  
  3. using namespace mira;
  4.  
  5. const int volume::VolumeBot::aux_ = StrategyBuilder::add_strategy<VolumeBot>("VOLUME_BOT");
  6.  
  7. volume::VolumeBot::VolumeBot(const strategy_config_t& config) : SingleStrategy(config) {
  8. *static_cast<strategy_config_t*>(&config_) = config;
  9.  
  10. using conv_fn = std::function<void(const std::string&)>;
  11.  
  12. std::string telemetry_dir = "";
  13. std::unordered_map<std::string, conv_fn> cnv = {
  14. {"volume", [this](const auto& v) { config_.volume = std::stod(v); }},
  15. {"min_order_size", [this](const auto& v) { config_.min_order_size = std::stod(v); }},
  16. {"symbol", [this](const auto& v) { config_.symbol_str = v; }},
  17. {"narrow_spread_trading", [this](const auto& v) { config_.narrow_spread_trading = std::stoi(v); }},
  18. {"lower_bound", [this](const auto& v) { config_.lower_bound = std::stod(v); }}, // TODO: добавить чекер на [0, 1]
  19. {"upper_bound", [this](const auto& v) { config_.upper_bound = std::stod(v); }},
  20. {"history_mode", [this](const auto& v) { config_.history_mode = (v == "true" ? true : false); }},
  21. {"telemetry_dir", [&telemetry_dir](const field_t& v) { telemetry_dir = v.s(); }},
  22. {"max_spread", [this](const auto& v) { config_.max_spread = std::stoi(v); }},
  23. {"cooldown",
  24. [this](const auto& v) {
  25. config_.cooldown = std::stoi(v);
  26. config_.sleep_time = config_.cooldown;
  27. }
  28. },
  29. };
  30.  
  31. for (const auto &[key, fn] : cnv) {
  32. try {
  33. if (config_.args.count(key)) {
  34. fn(config_.args[key].s());
  35. }
  36. } catch (const std::exception& e) {
  37. config_.logger->log("Error for key " + key + ": " + e.what());
  38. std::exit(EXIT_FAILURE);
  39. }
  40. }
  41.  
  42. std::cerr << "cooldown: " << config_.cooldown << '\n';
  43. std::cerr << "sleep time: " << config_.sleep_time << '\n';
  44.  
  45. account_name_t account_name;
  46. account_name.market = "BINANCE_FUTURES";
  47. account_name.name = "VOLUME_BINANCE_ORACLE";
  48. config_.binance_oracle = std::make_unique<binance_futures_api>(account_name, std::unordered_map<std::string, std::string>());
  49. stream_description_t descriptor;
  50. descriptor.stream = e_stream::KLINE;
  51. descriptor.args["symbol"] = "BTCUSDT";
  52. descriptor.args["interval"] = "5m";
  53. config_.binance_oracle->subscribe(descriptor, &config_.handler);
  54. descriptor.args["interval"] = "1d";
  55. config_.binance_oracle->subscribe(descriptor, &config_.handler);
  56. config_.binance_oracle->start_streams();
  57.  
  58.  
  59. std::string path_to_log_file =
  60. (telemetry_dir.empty() ? "/mnt/volume_telemetry/" : telemetry_dir) +
  61. config_.symbols.back()->base + "_" +
  62. config_.symbols.back()->quote + "_" +
  63. lower(config_.symbols.back()->api->market);
  64.  
  65. // временный костыль
  66. if (config_.symbols.back()->api->market == "MEXC_SPOT") {
  67. path_to_log_file =
  68. (telemetry_dir.empty() ? "/mnt/volume_telemetry/" : telemetry_dir) +
  69. config_.symbols.back()->base + "_" +
  70. config_.symbols.back()->quote + "_mexc";
  71. }
  72.  
  73. std::cerr << path_to_log_file << '\n';
  74. own_logger_ = std::make_shared<TelemetryLogger>(path_to_log_file, 1);
  75.  
  76. config_.time_distribution = std::uniform_real_distribution<>{config_.cooldown / 10., config_.cooldown * 1.9};
  77.  
  78. config_.upd_time = get_timestamp() - config_.sleep_time + 2000;
  79. }
  80.  
  81. order_event volume::VolumeBot::_process_single(const market_event& event) {
  82. order_event res;
  83.  
  84. if (event.type == e_market_event::TRADE) {
  85. auto trade = event.get<market_trade_event>().trade;
  86. field_t one_trade;
  87. one_trade["price"] = std::to_string(trade.price);
  88. one_trade["quantity"] = std::to_string(trade.quantity);
  89. one_trade["side"] = (trade.is_buy ? "buy" : "sell");
  90. one_trade["timestamp"] = get_timestamp();
  91. field_t trade_log = std::vector<field_t>();
  92. trade_log.v().push_back(one_trade);
  93. std::string string_to_log = "public_trades " + trade_log.serialize();
  94. own_logger_->log(string_to_log);
  95.  
  96. return res;
  97. }
  98.  
  99. if (get_timestamp() < config_.upd_time + config_.sleep_time) { // 0-момент сюда не попадёт
  100. return res;
  101. }
  102.  
  103. if (event.type == e_market_event::ORDER_BOOK) {
  104. auto orderbook_event = event.get<market_order_book_event>();
  105. config_.upd_time = get_timestamp();
  106. auto best_book = config_.shared_state->get_data_getter(event.symbol).get_best_book();
  107. return calculate_trade_event(event, best_book);
  108. } else if (event.type == e_market_event::TICK) {
  109. auto best_book = config_.shared_state->get_data_getter(event.symbol).get_best_book();
  110. config_.upd_time = get_timestamp();
  111. return calculate_trade_event(event, best_book);
  112. }
  113.  
  114. return res;
  115. }
  116.  
  117. double volume::VolumeBot::calculate_deviated_value(std::normal_distribution<double>&& distribution, double lower_bound, double upper_bound) {
  118. auto sampled_value = distribution(config_.gen);
  119. return std::max(std::min(sampled_value, upper_bound), lower_bound);
  120. }
  121.  
  122. order_event volume::VolumeBot::kline_trade_calculation(const ticker_t* symbol, double lower_bound, double upper_bound) {
  123. order_event res;
  124.  
  125. std::vector<kline_t> btc_klines_5m = config_.binance_oracle->get_ws_klines(nullptr, e_candlestick_interval::K_5m, 60);;
  126. double btc_turnover_1d = 0;
  127. double current_volume_5m;
  128.  
  129. if (config_.history_mode) {
  130. int64_t start_day = 86400 * std::floor(get_timestamp() / 86400000.);
  131. int since_start_day_5m = std::floor((get_timestamp() / 1000. - start_day) / 300.);
  132.  
  133. btc_turnover_1d = config_.binance_oracle->get_ws_klines(nullptr, e_candlestick_interval::K_1d, 2)[0].turnover;
  134. 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;
  135. current_volume_5m = config_.volume * (corresponding_history_5m / btc_turnover_1d);
  136. } else {
  137. auto btc_1d_ticker = config_.binance_oracle->get_ws_klines(nullptr, e_candlestick_interval::K_5m, 289);
  138. for (size_t i = 0; i < btc_1d_ticker.size() - 1; ++i) {
  139. btc_turnover_1d += btc_1d_ticker[i].turnover;
  140. }
  141. current_volume_5m = config_.volume * (btc_klines_5m[btc_klines_5m.size() - 2].turnover / btc_turnover_1d);
  142. }
  143.  
  144. std::cerr << "current_volume_5m: " << current_volume_5m << '\n';
  145. std::cerr << "btc_turnover_1d: " << btc_turnover_1d << '\n';
  146.  
  147. double price_max = btc_klines_5m[0].close.to_double();
  148. double price_min = btc_klines_5m[0].close.to_double();
  149. for (const auto& kline : btc_klines_5m) {
  150. price_max = std::max(price_max, kline.close.to_double());
  151. price_min = std::min(price_min, kline.close.to_double());
  152. }
  153.  
  154. if (price_max == btc_klines_5m[btc_klines_5m.size() - 1].close.to_double()) {
  155. config_.logger->log("now bitcoin 5 hour ATH");
  156. } else if (price_min == btc_klines_5m[btc_klines_5m.size() - 1].close.to_double()) {
  157. config_.logger->log("now bitcoin 5 hour minimum");
  158. }
  159.  
  160. config_.sleep_time = static_cast<size_t>(config_.time_distribution(config_.gen));
  161.  
  162. double current_volume = current_volume_5m * (config_.sleep_time / 300000.); // 300 * 1000
  163.  
  164. std::cerr << "price_max: " << price_max << '\n';
  165. std::cerr << "price_min: " << price_min << '\n';
  166. std::cerr << "sleep_time: " << config_.sleep_time << '\n';
  167. std::cerr << "current_volume: " << current_volume << '\n';
  168.  
  169. double step_size = symbol->price_step.to_double();
  170. auto best_book = config_.shared_state->get_data_getter(symbol).get_best_book();
  171. 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;
  172.  
  173. std::cerr << "btc_klines_5m[btc_klines_5m.size() - 1].close: " << btc_klines_5m[btc_klines_5m.size() - 1].close << '\n';
  174. std::cerr << "norm_price_coeff: " << norm_price_coeff << '\n';
  175. 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';
  176. std::cerr << "(best_book.bid_price + step_size / 2): " << (best_book.bid_price + step_size / 2) << '\n';
  177.  
  178. double current_price = (best_book.bid_price + step_size / 2) + (best_book.ask_price - best_book.bid_price - step_size) * norm_price_coeff;
  179.  
  180. std::cerr << "current_price: " << current_price << '\n';
  181.  
  182. if (current_price >= best_book.ask_price || current_price <= best_book.bid_price) {
  183. config_.logger->log("current_price >= best_book.ask_price || current_price <= best_book.bid_price");
  184. return res;
  185. }
  186.  
  187. double quote_balance = config_.shared_state->get_balance_getter(symbol->api).get_balance(symbol->quote).total();
  188. double base_balance = config_.shared_state->get_balance_getter(symbol->api).get_balance(symbol->base).total();
  189. double max_trade_available_quantity = std::min(base_balance, quote_balance / current_price) - 1 / current_price;
  190.  
  191. std::cerr << "max_trade_available_quantity: " << max_trade_available_quantity << '\n';
  192.  
  193. if (max_trade_available_quantity * current_price < config_.min_order_size) {
  194. std::cerr << "max_trade_available_quantity * current_price < min_order_size\n";
  195. return res;
  196. }
  197.  
  198. double calculated_current_volume = calculate_deviated_value(std::normal_distribution<>{current_volume, current_volume / 3}, 2 * current_volume / 3, 4 * current_volume / 3);
  199. std::cerr<< "calculated_current_volume: " << calculated_current_volume << '\n';
  200. double current_quantity = std::min(max_trade_available_quantity, std::max(calculated_current_volume, 5.5) / current_price);
  201.  
  202. // TODO: наверное не нужно будет
  203. // double current_quantity = std::max(calculated_current_volume, 5.5) / current_price;
  204. // if (current_quantity * current_price >= quote_balance - 1) {
  205. // config_.logger->log("current_quantity * current_price >= quote_balance - 1");
  206. // res.set(order_taker_sell_event(current_quantity)); // TODO: < 5$ может быть
  207. // res.set_symbol(symbol);
  208. // return res;
  209. // }
  210. // if (current_quantity >= base_balance - 1 / current_price) {
  211. // config_.logger->log("current_quantity >= base_balance - 1 / current_price");
  212. // res.set(order_taker_buy_event(0, current_quantity)); // TODO: < 5$ может быть
  213. // res.set_symbol(symbol);
  214. // return res;
  215. // }
  216.  
  217. std::cerr << "current_quantity: " << current_quantity << '\n';
  218. std::cerr << "price/quantity: " << current_price << "/" << current_quantity << '\n';
  219.  
  220. res.set(order_self_trade_event(current_price, current_quantity));
  221. res.set_symbol(symbol);
  222.  
  223. config_.logger->log(res, e_severity_level::DEBUG, "volume strategy result");
  224.  
  225. return res;
  226. }
  227.  
  228. order_event volume::VolumeBot::calculate_trade_event(const market_event& event, const float_book_t& best_book) {
  229. order_event res;
  230.  
  231. field_t book_log;
  232. book_log["ask_price"] = std::to_string(best_book.ask_price);
  233. book_log["bid_price"] = std::to_string(best_book.bid_price);
  234. std::string string_to_log = "book " + book_log.serialize();
  235. own_logger_->log(string_to_log);
  236.  
  237. auto& balance_getter = config_.shared_state.get()->get_balance_getter(event.symbol->api);
  238. double base_balance = balance_getter.get_balance(event.symbol->base).total();
  239. double quote_balance = balance_getter.get_balance(event.symbol->quote).total();
  240.  
  241. config_.logger->log(balance_getter.get_balance(event.symbol->base), e_severity_level::DEBUG, "base balance");
  242. config_.logger->log(balance_getter.get_balance(event.symbol->quote), e_severity_level::DEBUG, "quote balance");
  243.  
  244. field_t balance_log;
  245. balance_log["base"] = std::to_string(base_balance);
  246. balance_log["quote"] = std::to_string(quote_balance);
  247. string_to_log = "balance " + balance_log.serialize();
  248. own_logger_->log(string_to_log);
  249.  
  250. if (
  251. balance_getter.get_balance(event.symbol->base).locked ||
  252. balance_getter.get_balance(event.symbol->quote).locked
  253. ) {
  254. res.set(
  255. order_limit_grid_event(
  256. std::vector<limit_order_request_t>(),
  257. std::vector<limit_order_request_t>()
  258. )
  259. );
  260. res.set_symbol(event.symbol);
  261. return res;
  262. }
  263.  
  264. if (!best_book.ask_price || !best_book.bid_price) {
  265. config_.logger->log("No ask or bid");
  266. return res;
  267. }
  268.  
  269. if (config_.max_spread && (best_book.ask_price / best_book.bid_price - 1 > config_.max_spread / 10000.)) {
  270. own_logger_->log("STOPPED WIDE SPREAD");
  271. return res;
  272. }
  273.  
  274. auto ask_price = event.symbol->make_price(best_book.ask_price);
  275. auto bid_price = event.symbol->make_price(best_book.bid_price);
  276.  
  277. std::cout << "ask: " << ask_price << '\n';
  278. std::cout << "bid: " << bid_price << '\n';
  279.  
  280. size_t spread_ticks = (ask_price - bid_price).to_scaled();
  281.  
  282. if (spread_ticks == event.symbol->price_step.to_scaled()) {
  283. if (config_.narrow_spread_trading) {
  284. config_.logger->log("dealing with narrow spread");
  285.  
  286. double current_volume = calculate_deviated_value(std::normal_distribution<>{config_.volume, config_.volume / 3}, 0.7 * config_.volume, 1.3 * config_.volume);
  287. double ask_trade_quantity = std::min(current_volume / (2 * ask_price.to_double()), best_book.ask_quantity);
  288. double bid_trade_quantity = std::min(current_volume / (2 * bid_price.to_double()), best_book.bid_quantity);
  289.  
  290. limit_order_request_t buy_limit_req;
  291. buy_limit_req.level.price = ask_price.to_double();
  292. buy_limit_req.level.quantity = ask_trade_quantity;
  293. buy_limit_req.repeat_config = repeated_request_config_t{0, 3, 100};
  294.  
  295. limit_order_request_t sell_limit_req;
  296. sell_limit_req.level.price = bid_price.to_double();
  297. sell_limit_req.level.quantity = bid_trade_quantity;
  298. sell_limit_req.repeat_config = repeated_request_config_t{0, 3, 100};
  299.  
  300. res.set(
  301. order_limit_grid_event(
  302. std::vector<limit_order_request_t>{sell_limit_req},
  303. std::vector<limit_order_request_t>{buy_limit_req}
  304. )
  305. );
  306. res.set_symbol(event.symbol);
  307. config_.logger->log(res, e_severity_level::INFO, "dealing with narrow spread result");
  308. return res;
  309. }
  310. return res;
  311. }
  312.  
  313. res = kline_trade_calculation(event.symbol, config_.lower_bound, config_.upper_bound);
  314. res.set_symbol(event.symbol);
  315.  
  316. return res;
  317. }
  318.  
  319. order_event volume::VolumeBot::_process_single(const fill_event& event) {
  320. auto fill_ev = event.get<fill_order_event>();
  321. if (fill_ev.order) { // т.е account order
  322. if (
  323. fill_ev.order.value().status == e_order_status::FILLED ||
  324. fill_ev.order.value().status == e_order_status::PARTIALLY_FILLED
  325. ) {
  326. field_t one_trade;
  327. one_trade["price"] = std::to_string(fill_ev.fill.fill_quote / fill_ev.fill.fill_base);
  328. one_trade["quantity"] = std::to_string(fill_ev.fill.fill_base);
  329. one_trade["side"] = (fill_ev.order.value().side == e_side::BUY ? "buy" : "sell");
  330. one_trade["timestamp"] = get_timestamp();
  331. field_t trade_log = std::vector<field_t>();
  332. trade_log.v().push_back(one_trade);
  333. std::string string_to_log = "private_trades " + trade_log.serialize();
  334. own_logger_->log(string_to_log);
  335. }
  336. }
  337. return order_event();
  338. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement