@@ -0,0 +1,3 @@ | |||
/.drone.yml | |||
/docker | |||
/docker-volumes |
@@ -1 +1,2 @@ | |||
.idea | |||
.idea | |||
docker-volumes/ |
@@ -3,4 +3,13 @@ services: | |||
php: | |||
image: arbitry:local | |||
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: | |||
environment: | |||
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 \ | |||
libcurl \ | |||
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 | |||
COPY --from=composer /usr/bin/composer /usr/bin/composer | |||
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_HOSTS='^(localhost|example\.com)$' | |||
###< symfony/framework-bundle ### | |||
DATABASE_URL=mongodb://root:q12asd3@mongo:27017/?authenticationDatabase=admin |
@@ -5,6 +5,7 @@ | |||
"php": "^7.2.5", | |||
"ext-ctype": "*", | |||
"ext-iconv": "*", | |||
"mongodb/mongodb": "^1.6", | |||
"myclabs/php-enum": "^1.7", | |||
"nyholm/psr7": "^1.2", | |||
"psr/http-client": "^1.0", | |||
@@ -4,8 +4,76 @@ | |||
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", | |||
"This file is @generated automatically" | |||
], | |||
"content-hash": "4e5589a47c5b0dc0e0cd3d924e0621aa", | |||
"content-hash": "2ab113991ae6dde7f7448cf4e4e7f8d9", | |||
"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", | |||
"version": "1.7.6", | |||
@@ -11,6 +11,11 @@ services: | |||
autowire: true # Automatically injects dependencies in your services. | |||
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 | |||
# this creates a service per class whose id is the fully-qualified class name | |||
App\: | |||
@@ -29,4 +34,17 @@ services: | |||
$privateKey: 'test' | |||
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\Exchanges\ExchangeProvider; | |||
use App\Exchanges\Kuna\KunaExchange; | |||
use App\Exchanges\MarketInterface; | |||
use App\Infrastructure\Arbitration\Inner\ChainProvider; | |||
use Symfony\Component\Console\Command\Command; | |||
use Symfony\Component\Console\Input\InputInterface; | |||
use Symfony\Component\Console\Output\OutputInterface; | |||
@@ -17,26 +19,34 @@ class TestCommand extends Command | |||
{ | |||
protected static $defaultName = 'test:test'; | |||
/** | |||
* @var KunaExchange | |||
*/ | |||
private KunaExchange $kunaExchange; | |||
private ExchangeProvider $exchangeProvider; | |||
private ChainProvider $chainProvider; | |||
public function __construct( | |||
KunaExchange $kunaExchange | |||
ExchangeProvider $exchangeProvider, | |||
ChainProvider $chainProvider | |||
) | |||
{ | |||
parent::__construct(self::$defaultName); | |||
$this->kunaExchange = $kunaExchange; | |||
$this->exchangeProvider = $exchangeProvider; | |||
$this->chainProvider = $chainProvider; | |||
} | |||
public function execute(InputInterface $input, OutputInterface $output) | |||
{ | |||
$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( | |||
function (MarketInterface $market) { | |||
return $market->getPair()->getBase() . '/' . $market->getPair()->getQuote(); | |||
@@ -9,6 +9,7 @@ namespace App\Exchanges; | |||
interface ExchangeInterface | |||
{ | |||
public function getName(): string; | |||
/** | |||
* @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 | |||
{ | |||
$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; | |||
} | |||
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; | |||
} | |||
public function __toString() | |||
{ | |||
return $this->base . '/' . $this->quote; | |||
} | |||
} |
@@ -1,4 +1,7 @@ | |||
{ | |||
"mongodb/mongodb": { | |||
"version": "1.6.0" | |||
}, | |||
"myclabs/php-enum": { | |||
"version": "1.7.6" | |||
}, | |||