| @@ -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" | |||
| }, | |||