@@ -0,0 +1,3 @@ | |||||
/.drone.yml | |||||
/docker | |||||
/docker-volumes |
@@ -1 +1,2 @@ | |||||
.idea | |||||
.idea | |||||
docker-volumes/ |
@@ -3,4 +3,13 @@ services: | |||||
php: | php: | ||||
image: arbitry:local | image: arbitry:local | ||||
volumes: | volumes: | ||||
- ./project:/srv/project | |||||
- ./project:/projects/main | |||||
mongo: | |||||
command: "--wiredTigerCacheSizeGB 0.25" | |||||
environment: | |||||
MONGO_INITDB_ROOT_USERNAME: root | |||||
MONGO_INITDB_ROOT_PASSWORD: q12asd3 | |||||
MONGO_INITDB_DATABASE: arbitry | |||||
volumes: | |||||
- ./docker-volumes/mongo:/data/db |
@@ -3,4 +3,9 @@ services: | |||||
php: | php: | ||||
environment: | environment: | ||||
TZ: UTC | TZ: UTC | ||||
TERM: xterm | |||||
TERM: xterm | |||||
mongo: | |||||
image: mongo | |||||
restart: unless-stopped | |||||
ports: | |||||
- 27017:27017 |
@@ -7,10 +7,23 @@ ENV TERM xterm | |||||
RUN apk add --update --no-cache \ | RUN apk add --update --no-cache \ | ||||
libcurl \ | libcurl \ | ||||
bash \ | bash \ | ||||
curl | |||||
curl \ | |||||
git \ | |||||
alpine-sdk \ | |||||
openssl-dev | |||||
ENV EXT_MONGODB_VERSION=1.7.4 | |||||
RUN docker-php-source extract \ | |||||
&& git clone --branch $EXT_MONGODB_VERSION --depth 1 https://github.com/mongodb/mongo-php-driver.git /usr/src/php/ext/mongodb \ | |||||
&& cd /usr/src/php/ext/mongodb && git submodule update --init \ | |||||
&& docker-php-ext-install mongodb | |||||
RUN docker-php-ext-install bcmath | RUN docker-php-ext-install bcmath | ||||
COPY --from=composer /usr/bin/composer /usr/bin/composer | COPY --from=composer /usr/bin/composer /usr/bin/composer | ||||
COPY ./project /projects/main | COPY ./project /projects/main | ||||
WORKDIR /projects/main |
@@ -19,3 +19,5 @@ APP_SECRET=2cb22b0116249ce29b70a2b0ab83a8e5 | |||||
#TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 | #TRUSTED_PROXIES=127.0.0.0/8,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16 | ||||
#TRUSTED_HOSTS='^(localhost|example\.com)$' | #TRUSTED_HOSTS='^(localhost|example\.com)$' | ||||
###< symfony/framework-bundle ### | ###< symfony/framework-bundle ### | ||||
DATABASE_URL=mongodb://root:q12asd3@mongo:27017/?authenticationDatabase=admin |
@@ -5,6 +5,7 @@ | |||||
"php": "^7.2.5", | "php": "^7.2.5", | ||||
"ext-ctype": "*", | "ext-ctype": "*", | ||||
"ext-iconv": "*", | "ext-iconv": "*", | ||||
"mongodb/mongodb": "^1.6", | |||||
"myclabs/php-enum": "^1.7", | "myclabs/php-enum": "^1.7", | ||||
"nyholm/psr7": "^1.2", | "nyholm/psr7": "^1.2", | ||||
"psr/http-client": "^1.0", | "psr/http-client": "^1.0", | ||||
@@ -4,8 +4,76 @@ | |||||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | ||||
"This file is @generated automatically" | "This file is @generated automatically" | ||||
], | ], | ||||
"content-hash": "4e5589a47c5b0dc0e0cd3d924e0621aa", | |||||
"content-hash": "2ab113991ae6dde7f7448cf4e4e7f8d9", | |||||
"packages": [ | "packages": [ | ||||
{ | |||||
"name": "mongodb/mongodb", | |||||
"version": "1.6.0", | |||||
"source": { | |||||
"type": "git", | |||||
"url": "https://github.com/mongodb/mongo-php-library.git", | |||||
"reference": "dc43ba25fb593d6a2988e6a535b6f5386eda5b15" | |||||
}, | |||||
"dist": { | |||||
"type": "zip", | |||||
"url": "https://api.github.com/repos/mongodb/mongo-php-library/zipball/dc43ba25fb593d6a2988e6a535b6f5386eda5b15", | |||||
"reference": "dc43ba25fb593d6a2988e6a535b6f5386eda5b15", | |||||
"shasum": "" | |||||
}, | |||||
"require": { | |||||
"ext-hash": "*", | |||||
"ext-json": "*", | |||||
"ext-mongodb": "^1.7", | |||||
"php": "^5.6 || ^7.0" | |||||
}, | |||||
"require-dev": { | |||||
"phpunit/phpunit": "^5.7.27 || ^6.4 || ^8.3", | |||||
"sebastian/comparator": "^1.0 || ^2.0 || ^3.0", | |||||
"squizlabs/php_codesniffer": "^3.4", | |||||
"symfony/phpunit-bridge": "^4.4@dev" | |||||
}, | |||||
"type": "library", | |||||
"extra": { | |||||
"branch-alias": { | |||||
"dev-master": "1.6.x-dev" | |||||
} | |||||
}, | |||||
"autoload": { | |||||
"psr-4": { | |||||
"MongoDB\\": "src/" | |||||
}, | |||||
"files": [ | |||||
"src/functions.php" | |||||
] | |||||
}, | |||||
"notification-url": "https://packagist.org/downloads/", | |||||
"license": [ | |||||
"Apache-2.0" | |||||
], | |||||
"authors": [ | |||||
{ | |||||
"name": "Andreas Braun", | |||||
"email": "andreas.braun@mongodb.com" | |||||
}, | |||||
{ | |||||
"name": "Jeremy Mikola", | |||||
"email": "jmikola@gmail.com" | |||||
}, | |||||
{ | |||||
"name": "Katherine Walker", | |||||
"email": "katherine.walker@mongodb.com" | |||||
} | |||||
], | |||||
"description": "MongoDB driver library", | |||||
"homepage": "https://jira.mongodb.org/browse/PHPLIB", | |||||
"keywords": [ | |||||
"database", | |||||
"driver", | |||||
"mongodb", | |||||
"persistence" | |||||
], | |||||
"time": "2020-02-04T18:16:35+00:00" | |||||
}, | |||||
{ | { | ||||
"name": "myclabs/php-enum", | "name": "myclabs/php-enum", | ||||
"version": "1.7.6", | "version": "1.7.6", | ||||
@@ -11,6 +11,11 @@ services: | |||||
autowire: true # Automatically injects dependencies in your services. | autowire: true # Automatically injects dependencies in your services. | ||||
autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. | autoconfigure: true # Automatically registers your services as commands, event subscribers, etc. | ||||
_instanceof: | |||||
App\Exchanges\ExchangeInterface: | |||||
tags: | |||||
- { name: 'arbitry.exchange'} | |||||
# makes classes in src/ available to be used as services | # makes classes in src/ available to be used as services | ||||
# this creates a service per class whose id is the fully-qualified class name | # this creates a service per class whose id is the fully-qualified class name | ||||
App\: | App\: | ||||
@@ -29,4 +34,17 @@ services: | |||||
$privateKey: 'test' | $privateKey: 'test' | ||||
Http\Client\HttpClient: | Http\Client\HttpClient: | ||||
class: Nyholm\HttpClient\Client | |||||
class: Nyholm\HttpClient\Client | |||||
MongoDB\Client: | |||||
factory: ['App\Infrastructure\MongoClientFactory', 'create'] | |||||
arguments: | |||||
- '%env(resolve:DATABASE_URL)%' | |||||
App\Infrastructure\Arbitration\Inner\ChainProvider: ~ | |||||
App\Arbitration\Inner\ChainProviderInterface: '@App\Infrastructure\Arbitration\Inner\ChainProvider' | |||||
App\Exchanges\ExchangeProvider: | |||||
arguments: | |||||
- !tagged arbitry.exchange |
@@ -0,0 +1,22 @@ | |||||
<?php | |||||
declare(strict_types=1); | |||||
namespace App\Arbitration\Inner; | |||||
use App\Exchanges\ExchangeInterface; | |||||
interface ChainProviderInterface | |||||
{ | |||||
/** | |||||
* @param ExchangeInterface $exchange | |||||
* @return ArbitrationChain[] | |||||
*/ | |||||
public function getChainsForExchange(ExchangeInterface $exchange): array; | |||||
public function addChain(ExchangeInterface $exchange, ArbitrationChain $chain): void; | |||||
public function removeChain(ExchangeInterface $exchange, ArbitrationChain $chain): void; | |||||
} |
@@ -7,8 +7,10 @@ namespace App\Command; | |||||
use App\Arbitration\Inner\Stuff\ArbitrationMarketChainsFinder; | use App\Arbitration\Inner\Stuff\ArbitrationMarketChainsFinder; | ||||
use App\Exchanges\ExchangeProvider; | |||||
use App\Exchanges\Kuna\KunaExchange; | use App\Exchanges\Kuna\KunaExchange; | ||||
use App\Exchanges\MarketInterface; | use App\Exchanges\MarketInterface; | ||||
use App\Infrastructure\Arbitration\Inner\ChainProvider; | |||||
use Symfony\Component\Console\Command\Command; | use Symfony\Component\Console\Command\Command; | ||||
use Symfony\Component\Console\Input\InputInterface; | use Symfony\Component\Console\Input\InputInterface; | ||||
use Symfony\Component\Console\Output\OutputInterface; | use Symfony\Component\Console\Output\OutputInterface; | ||||
@@ -17,26 +19,34 @@ class TestCommand extends Command | |||||
{ | { | ||||
protected static $defaultName = 'test:test'; | protected static $defaultName = 'test:test'; | ||||
/** | |||||
* @var KunaExchange | |||||
*/ | |||||
private KunaExchange $kunaExchange; | |||||
private ExchangeProvider $exchangeProvider; | |||||
private ChainProvider $chainProvider; | |||||
public function __construct( | public function __construct( | ||||
KunaExchange $kunaExchange | |||||
ExchangeProvider $exchangeProvider, | |||||
ChainProvider $chainProvider | |||||
) | ) | ||||
{ | { | ||||
parent::__construct(self::$defaultName); | parent::__construct(self::$defaultName); | ||||
$this->kunaExchange = $kunaExchange; | |||||
$this->exchangeProvider = $exchangeProvider; | |||||
$this->chainProvider = $chainProvider; | |||||
} | } | ||||
public function execute(InputInterface $input, OutputInterface $output) | public function execute(InputInterface $input, OutputInterface $output) | ||||
{ | { | ||||
$finder = new ArbitrationMarketChainsFinder(); | $finder = new ArbitrationMarketChainsFinder(); | ||||
$chains = $finder->findChainsInExchange($this->kunaExchange, 5); | |||||
$exchange = $this->exchangeProvider->getExchanges()[0]; | |||||
// $chains = $finder->findChainsInExchange($exchange, 3); | |||||
// | |||||
// foreach ($chains as $chain) { | |||||
// $this->chainProvider->addChain($exchange, $chain); | |||||
// } | |||||
foreach ($chains as $chain) { | |||||
$fetchedChains = $this->chainProvider->getChainsForExchange($exchange); | |||||
foreach ($fetchedChains as $chain) { | |||||
echo implode(' -> ', array_map( | echo implode(' -> ', array_map( | ||||
function (MarketInterface $market) { | function (MarketInterface $market) { | ||||
return $market->getPair()->getBase() . '/' . $market->getPair()->getQuote(); | return $market->getPair()->getBase() . '/' . $market->getPair()->getQuote(); | ||||
@@ -9,6 +9,7 @@ namespace App\Exchanges; | |||||
interface ExchangeInterface | interface ExchangeInterface | ||||
{ | { | ||||
public function getName(): string; | |||||
/** | /** | ||||
* @return MarketInterface[] | * @return MarketInterface[] | ||||
*/ | */ | ||||
@@ -0,0 +1,33 @@ | |||||
<?php | |||||
declare(strict_types=1); | |||||
namespace App\Exchanges; | |||||
use IteratorAggregate; | |||||
class ExchangeProvider | |||||
{ | |||||
/** | |||||
* @var IteratorAggregate | |||||
*/ | |||||
private IteratorAggregate $exchanges; | |||||
public function __construct( | |||||
IteratorAggregate $exchanges | |||||
) | |||||
{ | |||||
$this->exchanges = $exchanges; | |||||
} | |||||
/** | |||||
* @return ExchangeInterface[] | |||||
*/ | |||||
public function getExchanges(): array | |||||
{ | |||||
return iterator_to_array($this->exchanges->getIterator()); | |||||
} | |||||
} |
@@ -28,13 +28,22 @@ class KunaExchange implements ExchangeInterface | |||||
*/ | */ | ||||
public function getMarkets(): array | public function getMarkets(): array | ||||
{ | { | ||||
$data = $this->apiExecutor->getMarkets(); | |||||
static $markets; | |||||
$markets = []; | |||||
foreach ($data as $marketData) { | |||||
$markets[] = KunaMarket::createFromApiData($marketData, $this->apiExecutor); | |||||
if (empty($markets)) { | |||||
$data = $this->apiExecutor->getMarkets(); | |||||
$markets = []; | |||||
foreach ($data as $marketData) { | |||||
$markets[] = KunaMarket::createFromApiData($marketData, $this->apiExecutor); | |||||
} | |||||
} | } | ||||
return $markets; | return $markets; | ||||
} | } | ||||
public function getName(): string | |||||
{ | |||||
return 'kuna'; | |||||
} | |||||
} | } |
@@ -0,0 +1,99 @@ | |||||
<?php | |||||
declare(strict_types=1); | |||||
namespace App\Infrastructure\Arbitration\Inner; | |||||
use App\Arbitration\Inner\ArbitrationChain; | |||||
use App\Arbitration\Inner\ChainProviderInterface; | |||||
use App\Exchanges\ExchangeInterface; | |||||
use App\Exchanges\MarketInterface; | |||||
use MongoDB\Client; | |||||
use MongoDB\Collection; | |||||
class ChainProvider implements ChainProviderInterface | |||||
{ | |||||
private Collection $collection; | |||||
public function __construct( | |||||
Client $client | |||||
) | |||||
{ | |||||
$this->collection = $client->selectCollection('arbitry', 'inner_arbitration_chains'); | |||||
} | |||||
/** | |||||
* @inheritDoc | |||||
*/ | |||||
public function getChainsForExchange(ExchangeInterface $exchange): array | |||||
{ | |||||
$rows = $this->collection->find(['exchange' => $exchange->getName()]); | |||||
$markets = $exchange->getMarkets(); | |||||
$chains = []; | |||||
foreach ($rows as $row) { | |||||
$chainMarkets = []; | |||||
foreach ($row['chain'] ?? [] as $chainData) { | |||||
foreach ($markets as $market) { | |||||
if ((string)$market->getPair() === $chainData) { | |||||
$chainMarkets[] = $market; | |||||
} | |||||
} | |||||
} | |||||
if (!empty($chainMarkets)) { | |||||
$chains[] = new ArbitrationChain(...$chainMarkets); | |||||
} | |||||
} | |||||
return $chains; | |||||
} | |||||
protected function calculateChainHash(ExchangeInterface $exchange, ArbitrationChain $chain): string | |||||
{ | |||||
return md5( | |||||
$exchange->getName() | |||||
. ';' | |||||
. implode( | |||||
';', | |||||
array_map( | |||||
static function (MarketInterface $market): string | |||||
{ | |||||
return (string)$market->getPair(); | |||||
}, | |||||
$chain->getMarkets() | |||||
) | |||||
) | |||||
); | |||||
} | |||||
public function addChain(ExchangeInterface $exchange, ArbitrationChain $chain): void | |||||
{ | |||||
$this | |||||
->collection | |||||
->insertOne([ | |||||
'_id' => $this->calculateChainHash($exchange, $chain), | |||||
'exchange' => $exchange->getName(), | |||||
'chain' => array_map( | |||||
static function (MarketInterface $market): string | |||||
{ | |||||
return (string)$market->getPair(); | |||||
}, | |||||
$chain->getMarkets() | |||||
) | |||||
]); | |||||
} | |||||
public function removeChain(ExchangeInterface $exchange, ArbitrationChain $chain): void | |||||
{ | |||||
$this | |||||
->collection | |||||
->deleteOne([ | |||||
'_id' => $this->calculateChainHash($exchange, $chain) | |||||
]); | |||||
} | |||||
} |
@@ -0,0 +1,18 @@ | |||||
<?php | |||||
declare(strict_types=1); | |||||
namespace App\Infrastructure; | |||||
use MongoDB\Client; | |||||
class MongoClientFactory | |||||
{ | |||||
public static function create(string $uri): Client | |||||
{ | |||||
return new Client($uri); | |||||
} | |||||
} |
@@ -27,4 +27,9 @@ class CurrencyPair | |||||
{ | { | ||||
return $this->quote; | return $this->quote; | ||||
} | } | ||||
public function __toString() | |||||
{ | |||||
return $this->base . '/' . $this->quote; | |||||
} | |||||
} | } |
@@ -1,4 +1,7 @@ | |||||
{ | { | ||||
"mongodb/mongodb": { | |||||
"version": "1.6.0" | |||||
}, | |||||
"myclabs/php-enum": { | "myclabs/php-enum": { | ||||
"version": "1.7.6" | "version": "1.7.6" | ||||
}, | }, | ||||