jaggedsoft / php-binance-api

PHP Binance API is an asynchronous PHP library for the Binance API designed to be easy to use. https://github.com/binance-exchange/php-binance-api
MIT License
604 stars 496 forks source link

Feature: Rate Limiting #155

Closed dmzoneill closed 6 years ago

dmzoneill commented 6 years ago

@jaggedsoft

Shes back to work again, little bit more time available. :) This is a feature that would help people avoid "429 too many requests" errors

Consider below (decorator class), where $redis i can replace with a standard linked list.

The functionality can be integrated into httpRequest, or we can keep it as a decorator class and develop it as a separate feature

using the magic function ___call and call_user_func_array to provide wrapping and redirection.

This approach while might provide some overhead, can allow use to provide additional functionality or plugin behaviour, pre, post

new FeatureC( New Feature B( new Binance() ) );

users can instantiate as follows

$apibase = new BinanceApi( $k, $p );
$api = new BinanceApiRateLimiter( $apibase  ) ; 
$api->MarketOrder( "BNBBTC" , 1);

Class

class BinanceApiRateLimiter
{
    private $weights = null;
    private $binance = null;
    private $exchangeRequestsRateLimit = 10;
    private $redisHandle = null;

    public function __construct($api, $redis)
    {
        $this->redisHandle = $redis;
        $this->binance = $api;

        $this->weights = array();
        $this->weights['account'] = 5;
        $this->weights['addToTransfered'] = 1;
        $this->weights['aggTrades'] = 1;
        $this->weights['balances'] = 1;
        $this->weights['bookPrices'] = 1;
        $this->weights['buy'] = 1;
        $this->weights['buyTest'] = 1;
        $this->weights['cancel'] = 1;
        $this->weights['candlesticks'] = 1;
        $this->weights['chart'] = 1;
        $this->weights['cumulative'] = 1;
        $this->weights['depositAddress'] = 1;
        $this->weights['depositHistory'] = 1;
        $this->weights['depth'] = 1;
        $this->weights['depthCache'] = 1;
        $this->weights['displayDepth'] = 1;
        $this->weights['exchangeInfo'] = 1;
        $this->weights['first'] = 1;
        $this->weights['getProxyUriString'] = 1;
        $this->weights['getRequestCount'] = 1;
        $this->weights['getTransfered'] = 1;
        $this->weights['highstock'] = 1;
        $this->weights['history'] = 5;
        $this->weights['keepAlive'] = 1;
        $this->weights['kline'] = 1;
        $this->weights['last'] = 1;
        $this->weights['marketBuy'] = 1;
        $this->weights['marketBuyTest'] = 1;
        $this->weights['marketSell'] = 1;
        $this->weights['marketSellTest'] = 1;
        $this->weights['miniTicker'] = 1;
        $this->weights['openOrders'] = 2;
        $this->weights['order'] = 1;
        $this->weights['orders'] = 5;
        $this->weights['orderStatus'] = 1;
        $this->weights['prevDay'] = 2;
        $this->weights['prices'] = 1;
        $this->weights['report'] = 1;
        $this->weights['sell'] = 1;
        $this->weights['sellTest'] = 1;
        $this->weights['setProxy'] = 1;
        $this->weights['sortDepth'] = 1;
        $this->weights['terminate'] = 1;
        $this->weights['ticker'] = 1;
        $this->weights['time'] = 1;
        $this->weights['trades'] = 5;
        $this->weights['userData'] = 1;
        $this->weights['useServerTime'] = 1;
        $this->weights['withdraw'] = 1;
        $this->weights['withdrawFee'] = 1;
        $this->weights['withdrawHistory'] = 1;

        $exchangeLimits = $this->binance->exchangeInfo()['rateLimits'];

        if (is_array($exchangeLimits) === false) {
            print "Problem getting exchange limits\n";
            return;
        }

        $limit = intval($exchangeLimit['interval']) === 0 ? 1000 : intval($exchangeLimit['interval']);
        $this->exchangeRequestsRateLimit = $limit;
    }

    public function __call($name, $arguments)
    {
        print $this->redisHandle->lSize('limitqueue') . "/" . $this->exchangeRequestsRateLimit . "active connections\n";

        if ($this->redisHandle->lSize('limitqueue') > 0) {
            while ($this->redisHandle->lSize('limitqueue') > $this->exchangeRequestsRateLimit) {
                $oldest = $this->redisHandle->lGet('limitqueue', 0);
                while ($oldest < time() - 60) {
                    $this->redisHandle->lPop('limitqueue');
                    $oldest = $this->redisHandle->lGet('limitqueue', 0);
                    print ".\n";
                }
                print "Rate limiting in effect " . $this->redisHandle->lSize('limitqueue') . "/" . $this->exchangeRequestsRateLimit . "\n";
                sleep(1);
            }
        }

        $weight = $this->weights[$name];

        for ($w = 0; $w < $weight; $w++) {
            $this->redisHandle->rPush('limitqueue', time());
        }

        echo "Calling object method '$name' " . implode(', ', $arguments) . "\n";

        return call_user_func_array(array(&$this->binance, $name), $arguments);
    }
}
dmzoneill commented 6 years ago

completed