More loses, multiple pairs

Hi Guys,

Script is up and running all the time and generating loses since it started trading at once on 7 pairs: ‘BTC-EUR’, ‘ETH-BTC’, ‘LTC-BTC’, ‘BCH-EUR’, ‘ETC-EUR’, ‘ETH-EUR’, ‘LTC-EUR’ under 1 exchange account.

After giving some thoughts into above situation I believe main cause of generating loses is exactly because of trading on multiple pairs. That’s a bigger change to the script. Current version of the script:


<?php

require( 'CoinbaseExchangeRequest.php' );
require( 'CoinbaseExchange.php' );
require __DIR__ . '/vendor/autoload.php';

$percentage = 0.001; // 0.1%

$lastPrice = array(); 
$lastPriceTime = array(); 
$lastPrice[ 'BTC-EUR' ] = 0;
$lastPrice[ 'ETH-EUR' ] = 0;
$lastPrice[ 'LTC-EUR' ] = 0;
$lastPrice[ 'BCH-EUR' ] = 0;
$lastPrice[ 'ETH-BTC' ] = 0;
$lastPrice[ 'LTC-BTC' ] = 0;
$lastPrice[ 'BCH-BTC' ] = 0;

$exchange = new CoinbaseExchange();
$exchange->auth( '***', '***', '***');

\Ratchet\Client\connect('wss://ws-feed.gdax.com')->then(function($conn) {
    $conn->on('message', function($msg) use ($conn) {
        global $exchange, $lastPrice, $percentage, $lastPriceTime;
        $data = json_decode($msg,1);

        if( $data[ 'type' ] == 'match' ) {
            if( in_array( $data[ 'product_id' ], array( 'BTC-EUR', 'ETH-BTC', 'LTC-BTC', 'ETH-EUR', 'LTC-EUR', 'BCH-EUR', 'BCH-BTC' )  ) ) {
                $pair1 = substr( $data[ 'product_id' ], 0, 3 );
                $pair2 = substr( $data[ 'product_id' ], 4, 6 );

                $openOrders = $exchange->listOrders();
                $countOpenOrders = 0;
                $uniquePairs = array();
                $now = new DateTime('now');
                foreach( $openOrders as $order ) {
                    $createdAt = new DateTime( $order[ 'created_at' ] );
                    $diff = $now->diff( $createdAt );
                    $diff = $diff->format("%h");

                    /**
                     * let's cancell orders older than a few hours
                     */
                    if( $diff > 3 ) {
                        $exchange->cancelOrder( $order[ 'id' ] );
                        $lastPrice[ $order[ 'product_id' ] ] = 0;
                        echo "\nKilling orders for pair ".$order[ 'product_id' ];
                        continue;
                    }

                    $uniquePairs[ $order[ 'product_id' ] ] = 1;
                    if( $order[ 'product_id' ] == $data[ 'product_id' ] ) {
                        $countOpenOrders++;
                    }
                }

                if( $countOpenOrders > 1 ) { // let's not have more than 2 open transactions per 1 pair
                    return;
                }

                $countUniquePairs = 0;
                foreach( $uniquePairs as $uniquePair ) {
                    if( $uniquePair ) {
                        $countUniquePairs++;
                    }
                }
                
                $balanceBTCAvailable = $exchange->getAccount( '***' )[ 'available' ];
                $balanceEURAvailable = $exchange->getAccount( '***' )[ 'available' ];
                $balanceBCHAvailable = $exchange->getAccount( '***' )[ 'available' ];
                $balanceETHAvailable = $exchange->getAccount( '***' )[ 'available' ];
                $balanceLTCAvailable = $exchange->getAccount( '***' )[ 'available' ];
                $balanceETCAvailable = $exchange->getAccount( '***' )[ 'available' ];

                /**
                 * @TODO catch exceptions from exchange on getAccount method. happens sometimes
                 */

                $balancePair1Available = 'balance'.$pair1.'Available';
                $balancePair2Available = 'balance'.$pair2.'Available';

                /**
                 * restart lastPrice per pair every 4 hours
                 */
                if( isset( $lastPriceTime[ $data[ 'product_id' ] ] ) )  {
                    $now = new DateTime('now'); 
                    $diff = $now->diff( $createdAt );
                    $diff = $diff->format("%h");    

                    if( $diff > 5 ) {
                        $lastPriceTime[ $data[ 'product_id' ] ] = new DateTime( 'now' );
                        $lastPrice[ $data[ 'product_id' ] ] = 0;
                    }
                }
                
                /**
                 * selling
                 */
                if( $data[ 'price' ] > (1 + $percentage) * $lastPrice[ $data[ 'product_id' ] ] && $$balancePair1Available > 0 ) { 
                    $book = $exchange->getOrderBook( $data[ 'product_id' ] );
                    $maxBid = $book[ 'asks' ][ 0 ][ 0 ];
                    
                    if( $pair2 == 'EUR' ) {
                        $price = round( max( $maxBid + 0.01, $data[ 'price' ] ), 2 );
                    } else {
                        $price = round( max( $maxBid + 0.00001, $data[ 'price' ] ), 6 );
                    }
                    
                    $size = $$balancePair1Available;
                
                    if( $pair1 == 'BTC' && $size < 0.001 || 
                        $pair1 == 'LTC' && $size < 0.1 ||
                        $pair1 == 'ETC' && $size < 0.1 ||
                        $pair1 == 'ETH' && $size < 0.01 || 
                        $pair1 == 'BCH' && $size < 0.01) {
                            
                        return;
                    }

                    if( $pair1 == 'BTC' ) {
                        $size = round( $size * 10, 7 ) / 10; // just rounding may be inaccurate. kind of floor operation..
                    }

                    echo "\n$data[product_id], Selling $size $pair1 at price $price $pair2, time=".date('Y-m-d H:i:s');
                    
                    $sell = $exchange->placeOrder('sell', $price, $size, $data[ 'product_id' ]); 

                    if( isset( $sell[ 'id' ] ) ) {
                        $lastPrice[ $data[ 'product_id' ] ] = $price;
                        $lastPriceTime[ $data[ 'product_id' ] ] = new DateTime( 'now' );
                    }
                    print_r( $sell );
                }
                /**
                 * buying
                 */
                if( ( !$lastPrice[ $data[ 'product_id' ] ] || $data[ 'price' ] <= $lastPrice[ $data[ 'product_id' ] ] ) ) { 
                    $book = $exchange->getOrderBook( $data[ 'product_id' ] );
                    $minAsk = $book[ 'asks' ][ 0 ][ 0 ];
                
                    if( $pair2 == 'EUR' ) {
                        $price = round( min( $minAsk - 0.01, $data[ 'price' ] ), 2 );
                    } else {
                        $price = round( min( $minAsk - 0.00001, $data[ 'price' ] ), 6 );
                    }
                    
                    $size = round( 0.99*$$balancePair2Available / $price / ( max( 1, 6 - $countUniquePairs ) ), 7 ); 
                
                    if( $pair1 == 'BTC' && $size < 0.001 || 
                        $pair1 == 'LTC' && $size < 0.1 ||
                        $pair1 == 'ETC' && $size < 0.1 ||
                        $pair1 == 'ETH' && $size < 0.01 || 
                        $pair1 == 'BCH' && $size < 0.01) {

                        return;
                    }
                
                    echo "\n$data[product_id], Buying $size $pair1 at price $price $pair2, time=".date('Y-m-d H:i:s');
                    
                    $buy = $exchange->placeOrder('buy', $price, $size, $data[ 'product_id' ] ); 

                    if( isset( $buy[ 'id' ] ) ) {
                        $lastPrice[ $data[ 'product_id' ] ] = $price;
                        $lastPriceTime[ $data[ 'product_id' ] ] = new DateTime( 'now' );
                    }

                    print_r( $buy );
                }
            }
        }
    });

    /**
     * websocket connection to Coinbase Pro to fetch real-time trades (matches)
     */
    $conn->send( '{ "type" : "subscribe", 
                    "product_ids" : [ "BTC-EUR", "ETH-BTC", "LTC-BTC", "ETH-EUR", "LTC-EUR", "BCH-EUR", "BCH-BTC" ],
                    "channels" : [ "matches" ],
                    "secret": "***",
                    "key": "***",
                    "passphrase": "***"}' );
}, function ($e) {
    echo "Could not connect: {$e->getMessage()}\n";
});
?>

Thanks for reading,
Łukasz.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s