Skip to content

Commit

Permalink
Add Multi Core
Browse files Browse the repository at this point in the history
  • Loading branch information
kleninmaxim committed May 27, 2022
1 parent aa065b9 commit 517de07
Show file tree
Hide file tree
Showing 5 changed files with 443 additions and 0 deletions.
85 changes: 85 additions & 0 deletions config/multi_3t.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
{
"node": "core",
"algorithm": "multi_3t_php",
"instances": {
"binance": "1",
"ascendex": "1"
},
"exchanges": [
"binance",
"ascendex"
],
"expired_orderbook_time": 500000,
"algo_sleep": 10,
"min_profit": {
"BTC": 0,
"ETH": 0,
"USDT": 0
},
"min_deal_amounts": {
"BTC": 0,
"ETH": 0,
"USDT": 0
},
"rates": {
"BTC": 30000,
"ETH": 2000,
"USDT": 1
},
"max_deal_amounts": {
"BTC": 0.01,
"ETH": 0.1,
"USDT": 200
},
"max_depth": 5,
"fees": {
"binance": 0.1,
"ascendex": 0.2
},
"aeron": {
"publishers": {
"gates": {
"binance": {
"channel": "aeron:udp?control=172.31.12.44:40455|control-mode=dynamic",
"stream_id": 1004
},
"ascendex": {
"channel": "aeron:ipc",
"stream_id": 1004
}
},
"log": {
"channel": "aeron:udp?control=172.31.12.44:40454|control-mode=dynamic",
"stream_id": 1008
}
},
"subscribers": {
"balances": {
"channel": "aeron:udp?control-mode=manual",
"destinations": [
"aeron:udp?endpoint=172.31.12.44:40457|control=3.69.53.241:40456",
"aeron:udp?endpoint=172.31.12.44:40460|control=3.69.43.98:40456"
],
"stream_id": 1005
},
"orderbooks": {
"channel": "aeron:udp?control-mode=manual",
"destinations": [
"aeron:udp?endpoint=172.31.12.44:40458|control=3.69.53.241:40456",
"aeron:udp?endpoint=172.31.12.44:40461|control=3.69.43.98:40456"
],
"stream_id": 1006
},
"orders_statuses": {
"channel": "aeron:udp?control-mode=manual",
"destinations": [
"aeron:udp?endpoint=172.31.12.44:40459|control=3.69.53.241:40456",
"aeron:udp?endpoint=172.31.12.44:40462|control=3.69.43.98:40456"
],
"stream_id": 1007
}
}
},
"debug": true,
"made_html_vision_file": "/var/www/html/test.html"
}
97 changes: 97 additions & 0 deletions kernel/multi_3t.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
<?php

use Src\Cross3T;
use Src\Multi\MultiConfigurator;
use Src\Multi\MultiFirstData;
use Aeron\Publisher;

require dirname(__DIR__) . '/index.php';

// подключение к memcached
$memcached = new Memcached();
$memcached->addServer('localhost', 11211);

// очистить все, что есть в memcached
$memcached->flush();

// Получает конфиг от каждой биржи конфигуратора, а также конфиг из файла multi_3t.json
$config = MultiConfigurator::getConfig(dirname(__DIR__) . '/config/multi_3t.json');

// Формируем все неободимые соеденения, классы и т. п.
[$robotrade_apis, $logs, $gate_publishers, $gates, $log_publisher, $multi_core] = MultiFirstData::get($config);

// создаем класс cross 3t
$cross_3t = new Cross3T($config, ['debug' => $config['debug'], 'made_html_vision_file' => $config['made_html_vision_file']]);

while (true) {

usleep($config['sleep']);

// отформировать и отделить все данные, полученные из memcached
$all_data = $multi_core->getFormatData($memcached);

// балансы, ордербуки и неизвестные данные
$balances = $all_data['balances'];
$orderbooks = $all_data['orderbooks'];

// если есть все необходимые данные
if (!empty($balances) && !empty($orderbooks) && !empty($config)) {

// запускаем алгоритм и получаем лучший результат
if ($best_result = $cross_3t->run($balances, $orderbooks)) {

// для каждого шага, если результат выпал на текущую биржу, отправить сообщение на создание ордера
foreach (['step_one', 'step_two', 'step_three'] as $step) {

// отправить гейту на постановку ордера
$gate_publishers[$best_result[$step]['exchange']]->offer(
$robotrade_apis[$best_result[$step]['exchange']]->createOrder(
$best_result[$step]['amountAsset'] . '/' . $best_result[$step]['priceAsset'],
'market',
$best_result[$step]['orderType'],
$best_result[$step]['amount'],
$best_result[$step]['price'],
'Create order ' . $step
)
);

echo '[' . date('Y-m-d H:i:s') . '] Send to gate create order. Pair: ' .
$best_result[$step]['amountAsset'] . '/' . $best_result[$step]['priceAsset'] .
' Type: ' . $best_result[$step]['orderType'] .
' Amount: ' . $best_result[$step]['amount'] .
' Price: ' . $best_result[$step]['price'] .
' Exchange: ' . $best_result[$step]['exchange'] .
PHP_EOL;

}

foreach (['step_one', 'step_two', 'step_three'] as $step) {

// удаляем из memcached данные о балансе
$memcached->delete($best_result[$step]['exchange'] . '_balances');

// Запрос на получение баланса
$gates[$best_result[$step]['exchange']]->getBalances(array_column($config['assets_labels'], 'common'))->send();

}

$log_publisher->offer($logs[$best_result[$step]['exchange']]->sendExpectedTriangle($best_result));

}

} else {

if (empty($balances))
echo '[' . date('Y-m-d H:i:s') . '] [WARNING] Empty $balances' . PHP_EOL;

if (empty($orderbooks))
echo '[' . date('Y-m-d H:i:s') . '] [WARNING] Empty $orderbooks' . PHP_EOL;

if (empty($config))
echo '[' . date('Y-m-d H:i:s') . '] [WARNING] Empty $config' . PHP_EOL;

sleep(1);

}

}
56 changes: 56 additions & 0 deletions src/Multi/MultiConfigurator.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
<?php

namespace Src\Multi;

class MultiConfigurator
{

/**
* Возвращает полноценный конфиг для мульти ядра
*
* @param string $multi_config_path Путь к файлу конфигу multi_3t.json
* @return array Полный конфиг
*/
public static function getConfig(string $multi_config_path): array
{

$config = json_decode(file_get_contents($multi_config_path), true);

foreach ($config['exchanges'] as $exchange) {

$config_from_configurator = json_decode(
file_get_contents('https://configurator.robotrade.io/' . $exchange . '/' . $config['instances'][$exchange] . '?only_new=false'),
true
)['data'];

$routes[$exchange] = $config_from_configurator['routes'];

$assets_labels[$exchange] = $config_from_configurator['assets_labels'];

$markets[$exchange] = $config_from_configurator['markets'];

}

if (empty($routes) || empty($assets_labels) || empty($markets)) {

echo '[' . date('Y-m-d H:i:s') . '] Die $route or $assets_label or $markets empty' . PHP_EOL;

die();

}

$route = array_shift($routes);

$assets_label = array_shift($assets_labels);

$config['routes'] = $route;

$config['assets_label'] = $assets_label;

$config['markets'] = $markets;

return $config;

}

}
125 changes: 125 additions & 0 deletions src/Multi/MultiCore.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
<?php

namespace Src\Multi;

class MultiCore
{

private array $exchanges;
private array $markets;
private int $expired_orderbook_time;
private array $keys;

/**
* @param array $exchanges
* @param array $markets
* @param int $expired_orderbook_time
*/
public function __construct(array $exchanges, array $markets, int $expired_orderbook_time)
{

$this->exchanges = $exchanges;
$this->markets = $markets;
$this->expired_orderbook_time = $expired_orderbook_time;
$this->keys = $this->getAllMemcachedKeys();

}

/**
* Формирует данные в определенном виде
*
* @param $memcached
* @return array[]
*/
public function getFormatData($memcached): array
{

return $this->reformatAndSeparateData($memcached->getMulti($this->keys) ?? []);

}

/**
* Возвращает данные из memcached в определенном формате и отделенные по ордербукам, балансам и т. д.
*
* @param array $memcached_data Сырые данные, взятые напрямую из memcached
* @return array[]
*/
private function reformatAndSeparateData(array $memcached_data): array
{

$microtime = microtime(true);

foreach ($memcached_data as $key => $data) {

if (isset($data)) {

$parts = explode('_', $key);

$exchange = $parts[0];
$action = $parts[1];
$value = $parts[2] ?? null;

if ($action == 'balances') {

$balances[$exchange] = $data;

} elseif ($action == 'orderbook' && $value) {

if (
($microtime - $data['core_timestamp']) <= $this->expired_orderbook_time / 1000000
) {

$orderbooks[$value][$exchange] = $data;

}

} elseif ($action == 'orders') {

$orders[$exchange] = $data;

} else {

$undefined[$key] = $data;

}

}

}

return [
'balances' => $balances ?? [],
'orderbooks' => $orderbooks ?? [],
'orders' => $orders ?? [],
'undefined' => $undefined ?? [],
];

}

/**
* Формирует массив всех ключей для memcached
*
* @return array Возвращает все ключи для memcached
*/
private function getAllMemcachedKeys(): array
{

$keys = [];

foreach ($this->exchanges as $exchange)
$keys = array_merge(
$keys,
preg_filter(
'/^/',
$exchange . '_orderbook_',
array_column($this->markets[$exchange], 'common_symbol')
),
[$exchange . '_balances'], // добавить еще к массиву ключ баланса
[$exchange . '_orders'] // добавить еще к массиву ключ для получения ордеров
);

return $keys;

}

}
Loading

0 comments on commit 517de07

Please sign in to comment.