Просмотр исходного кода

[WIP] arbitary chain storage & exchange provider

master
komarov 4 лет назад
Родитель
Сommit
14127a4b91
19 измененных файлов: 338 добавлений и 18 удалений
  1. +3
    -0
      .dockerignore
  2. +2
    -1
      .gitignore
  3. +10
    -1
      docker-compose.override.yaml
  4. +6
    -1
      docker-compose.yaml
  5. +14
    -1
      docker/php/Dockerfile
  6. +2
    -0
      project/.env
  7. +1
    -0
      project/composer.json
  8. +69
    -1
      project/composer.lock
  9. +19
    -1
      project/config/services.yaml
  10. +0
    -0
      project/src/Arbitration/Inner/ArbitrationChain.php
  11. +22
    -0
      project/src/Arbitration/Inner/ChainProviderInterface.php
  12. +18
    -8
      project/src/Command/TestCommand.php
  13. +1
    -0
      project/src/Exchanges/ExchangeInterface.php
  14. +33
    -0
      project/src/Exchanges/ExchangeProvider.php
  15. +13
    -4
      project/src/Exchanges/Kuna/KunaExchange.php
  16. +99
    -0
      project/src/Infrastucture/Arbitration/Inner/ChainProvider.php
  17. +18
    -0
      project/src/Infrastucture/MongoClientFactory.php
  18. +5
    -0
      project/src/Utils/Money/CurrencyPair.php
  19. +3
    -0
      project/symfony.lock

+ 3
- 0
.dockerignore Просмотреть файл

@@ -0,0 +1,3 @@
/.drone.yml
/docker
/docker-volumes

+ 2
- 1
.gitignore Просмотреть файл

@@ -1 +1,2 @@
.idea
.idea
docker-volumes/

+ 10
- 1
docker-compose.override.yaml Просмотреть файл

@@ -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

+ 6
- 1
docker-compose.yaml Просмотреть файл

@@ -3,4 +3,9 @@ services:
php:
environment:
TZ: UTC
TERM: xterm
TERM: xterm
mongo:
image: mongo
restart: unless-stopped
ports:
- 27017:27017

+ 14
- 1
docker/php/Dockerfile Просмотреть файл

@@ -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

+ 2
- 0
project/.env Просмотреть файл

@@ -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

+ 1
- 0
project/composer.json Просмотреть файл

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


+ 69
- 1
project/composer.lock Просмотреть файл

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


+ 19
- 1
project/config/services.yaml Просмотреть файл

@@ -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

project/src/Arbitration/ArbitrationChain.php → project/src/Arbitration/Inner/ArbitrationChain.php Просмотреть файл


+ 22
- 0
project/src/Arbitration/Inner/ChainProviderInterface.php Просмотреть файл

@@ -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;
}

+ 18
- 8
project/src/Command/TestCommand.php Просмотреть файл

@@ -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();


+ 1
- 0
project/src/Exchanges/ExchangeInterface.php Просмотреть файл

@@ -9,6 +9,7 @@ namespace App\Exchanges;
interface ExchangeInterface
{

public function getName(): string;
/**
* @return MarketInterface[]
*/


+ 33
- 0
project/src/Exchanges/ExchangeProvider.php Просмотреть файл

@@ -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());
}
}

+ 13
- 4
project/src/Exchanges/Kuna/KunaExchange.php Просмотреть файл

@@ -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';
}
}

+ 99
- 0
project/src/Infrastucture/Arbitration/Inner/ChainProvider.php Просмотреть файл

@@ -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)
]);
}
}

+ 18
- 0
project/src/Infrastucture/MongoClientFactory.php Просмотреть файл

@@ -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);
}
}

+ 5
- 0
project/src/Utils/Money/CurrencyPair.php Просмотреть файл

@@ -27,4 +27,9 @@ class CurrencyPair
{
return $this->quote;
}

public function __toString()
{
return $this->base . '/' . $this->quote;
}
}

+ 3
- 0
project/symfony.lock Просмотреть файл

@@ -1,4 +1,7 @@
{
"mongodb/mongodb": {
"version": "1.6.0"
},
"myclabs/php-enum": {
"version": "1.7.6"
},


Загрузка…
Отмена
Сохранить