From 5cd77ae05440cea1473794dd7a5fd6529f7ec0a9 Mon Sep 17 00:00:00 2001 From: Tristian Date: Thu, 16 Mar 2017 16:27:16 -0400 Subject: [PATCH] Add LiveCoin trading #68 Insert balance row if it is missing --- web/yaamp/commands/ExchangeCommand.php | 5 +- web/yaamp/core/backend/markets.php | 9 +- web/yaamp/core/backend/rawcoins.php | 10 +- web/yaamp/core/exchange/livecoin.php | 351 +++++++++++++------- web/yaamp/core/trading/livecoin_trading.php | 335 ++++++++++++++++--- web/yaamp/core/trading/trading.php | 9 +- 6 files changed, 537 insertions(+), 182 deletions(-) diff --git a/web/yaamp/commands/ExchangeCommand.php b/web/yaamp/commands/ExchangeCommand.php index cb752eb..0387165 100644 --- a/web/yaamp/commands/ExchangeCommand.php +++ b/web/yaamp/commands/ExchangeCommand.php @@ -164,8 +164,9 @@ class ExchangeCommand extends CConsoleCommand echo("kraken btc: ".json_encode($balance)."\n"); } if (!empty(EXCH_LIVECOIN_KEY)) { - $balance = livecoin_api_user('payment/balance', array('currency'=>'BTC')); - if (!is_object($balance)) echo("livecoin error\n"); + $livecoin = new LiveCoinApi; + $balance = $livecoin->getBalances('BTC'); + if (!$balance) echo("livecoin error\n"); else echo("livecoin btc: ".json_encode($balance)."\n"); // {"type":"available","currency":"BTC","value":0} } diff --git a/web/yaamp/core/backend/markets.php b/web/yaamp/core/backend/markets.php index 8b41576..f84217e 100644 --- a/web/yaamp/core/backend/markets.php +++ b/web/yaamp/core/backend/markets.php @@ -21,7 +21,7 @@ function BackendPricesUpdate() updateBterMarkets(); //updateEmpoexMarkets(); updateJubiMarkets(); - updateLivecoinMarkets(); + updateLiveCoinMarkets(); updateNovaMarkets(); updateShapeShiftMarkets(); @@ -1015,12 +1015,13 @@ function updateEmpoexMarkets() } } -function updateLivecoinMarkets() +function updateLiveCoinMarkets() { $exchange = 'livecoin'; if (exchange_get($exchange, 'disabled')) return; - $markets = livecoin_api_query('exchange/ticker'); + $livecoin = new LiveCoinApi; + $markets = $livecoin->getTickerInfo(); if(!is_array($markets)) return; $list = getdbolist('db_markets', "name='$exchange'"); @@ -1064,7 +1065,7 @@ function updateLivecoinMarkets() if(empty($market->deposit_address) && !$last_checked) { sleep(1); - $data = livecoin_api_user('payment/get/address', array('currency'=>$coin->symbol)); + $data = $livecoin->getDepositAddress($coin->symbol); if(!empty($data) && objSafeVal($data, 'wallet', '') != '') { $addr = arraySafeVal($data, 'wallet'); if (!empty($addr) && $addr != $market->deposit_address) { diff --git a/web/yaamp/core/backend/rawcoins.php b/web/yaamp/core/backend/rawcoins.php index bfb45bb..2a5311a 100644 --- a/web/yaamp/core/backend/rawcoins.php +++ b/web/yaamp/core/backend/rawcoins.php @@ -173,16 +173,18 @@ function updateRawcoins() } if (!exchange_get('livecoin', 'disabled')) { - $list = livecoin_api_query('exchange/ticker'); + $livecoin = new LiveCoinApi; + $list = $livecoin->getTickerInfo(); if(is_array($list)) { dborun("UPDATE markets SET deleted=true WHERE name='livecoin'"); foreach($list as $item) { $e = explode('/', $item->symbol); - $base = strtoupper($e[1]); - if ($base != 'BTC') + $symbol = $e[0]; + $base = $e[1]; + if ($base != 'BTC') { continue; - $symbol = strtoupper($e[0]); + } updateRawCoin('livecoin', $symbol); } } diff --git a/web/yaamp/core/exchange/livecoin.php b/web/yaamp/core/exchange/livecoin.php index a5d3a67..8793723 100644 --- a/web/yaamp/core/exchange/livecoin.php +++ b/web/yaamp/core/exchange/livecoin.php @@ -1,131 +1,234 @@ api_key", + "Sign: $signature" + ); + + if ($post) { + $ch = curl_init($url); + curl_setopt($ch, CURLOPT_POST, 'POST'); + curl_setopt($ch, CURLOPT_POSTFIELDS, $fields); + } else { + $ch = curl_init($url."?".$fields); + } + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; LiveCoin PHP client; '.php_uname('s').'; PHP/'.phpversion().')'); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout/2); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + + $response = curl_exec($ch); + if ($response) { + $a = json_decode($response); + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if (!$a) { + debuglog("LiveCoin: Auth API failed ($status) ".strip_data($response).' '.curl_error($ch)); + } + } + curl_close($ch); + + return isset($a) ? $a : false; + } + + protected function jsonGet($url, $params=array()) + { + + $fields = http_build_query($params, '', '&'); + $ch = curl_init($url."?".$fields); + + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; LiveCoin PHP client; '.php_uname('s').'; PHP/'.phpversion().')'); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, $this->timeout/2); + curl_setopt($ch, CURLOPT_TIMEOUT, $this->timeout); + + $response = curl_exec($ch); + + if ($response) { + $a = json_decode($response); + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + if (!$a) { + debuglog("LiveCoin: Auth API failed ($status) ".strip_data($response).' '.curl_error($ch)); + } + } + curl_close($ch); + + return isset($a) ? $a : false; + } + + // Public data + public function getTickerInfo($pair=false) + { + $params = array(); + if ($pair) { + $params['currencyPair'] = $pair; + } + + return $this->jsonGet($this->api_url.'/exchange/ticker', $params); + } + + public function getLastTrades($pair, $minorhr='false', $type='flase') + { + $params = array('currencyPair' => $pair, + 'minutesOrHour' => $minorhr, + 'type' => $type + ); + + return $this->jsonGet($this->api_url.'/exchange/last_trades', $params); + } + + public function getOrderBook($pair, $group='false', $depth=10) + { + $params = array('currencyPair' => $pair, 'groupByPrice' => $group, 'depth' => $depth); + return $this->jsonGet($this->api_url.'/exchange/order_book', $params); + } + + public function getAllOrderBook($group='false', $depth=10) + { + $params = array('groupByPrice' => $group, 'depth' => $depth); + return $this->jsonGet($this->api_url.'/exchange/all/order_book', $params); + } + + public function getMaxMin($pair=false) + { + $params = array(); + if ($pair) { + $params['currencyPair'] = $pair; + } + + return $this->jsonGet($this->api_url.'/exchange/maxbid_minask', $params); + } + + public function getRestrictions() + { + return $this->jsonGet($this->api_url.'/exchange/restrictions'); + } + + public function getCoinInfo() + { + return $this->jsonGet($this->api_url.'/info/coininfo'); + } + + + // Private user data + public function getTrades($pair=false, $order='true', $limit=100, $offset=0) + { + $params = array('orderDesc' => $order, + 'limit' => $limit, + 'offset' => $offset + ); + if ($pair) { + $params['currencyPair'] = $pair; + } + + return $this->jsonAuth($this->api_url.'/exchange/trades', $params); + } + + public function getClientOrders($pair=false, $open='ALL', $from=false, $to=false, $start=0, $end=2147483646) + { + $params = array('open' => $open, + 'start' => $start, + 'end' => $end + ); + if ($pair) { + $params['currencyPair'] = $pair; + } + if ($from) { + $params['issuedFrom'] = $from; + } + if ($to) { + $params['issuedTo'] = $to; + } + + return $this->jsonAuth($this->api_url.'/exchange/client_orders', $params); + } + + public function getOrder($id) + { + $params = array('orderId' => $id); + return $this->jsonAuth($this->api_url.'/exchange/order', $params); + } + + public function getBalances($currency=false) + { + $params = array(); + if ($currency) { + $params['currency'] = $currency; + } + return $this->jsonAuth($this->api_url.'/payment/balances', $params); + } + + public function getTransactions($start, $end, $types='BUY,SELL,DEPOSIT,WITHDRAWAL', $limit=100, $offset=0) + { + $params = array('start' => $start, + 'end' => $end, + 'types' => $types, + 'limit' => $limit, + 'offset' => $offset + ); + return $this->jsonAuth($this->api_url.'/payment/history/transactions', $params); + } + + + // Orders + public function buyLimit($pair, $price, $quantity) + { + $params = array('currencyPair' => $pair, + 'price' => $price, + 'quantity' => $quantity + ); + return $this->jsonAuth($this->api_url.'/exchange/buylimit', $params, true); + } + + public function sellLimit($pair, $price, $quantity) + { + $params = array('currencyPair' => $pair, + 'price' => $price, + 'quantity' => $quantity + ); + return $this->jsonAuth($this->api_url.'/exchange/selllimit', $params, true); + } + + public function cancelLimitOrder($pair, $id) + { + $params = array('currencyPair' => $pair, + 'orderId' => $id + ); + return $this->jsonAuth($this->api_url.'/exchange/cancellimit', $params, true); + } + + // Deposit and Withdrawal + public function getDepositAddress($symbol) { + $params = array('currency' => $symbol); + return $this->jsonAuth($this->api_url.'/payment/get/address', $params); + } + + public function withdrawCoin($amnt, $currency, $wallet) + { + $params = array('amount' => $amnt, + 'currency' => $currency, + 'wallet' => $wallet + ); + return $this->jsonAuth($this->api_url.'/payment/out/coin', $params, true); + } } diff --git a/web/yaamp/core/trading/livecoin_trading.php b/web/yaamp/core/trading/livecoin_trading.php index d256861..4ca5118 100644 --- a/web/yaamp/core/trading/livecoin_trading.php +++ b/web/yaamp/core/trading/livecoin_trading.php @@ -1,53 +1,298 @@ cancelLimitOrder($pair, $id); - // https://www.livecoin.net/api/userdata#paymentbalances - $balances = livecoin_api_user('payment/balances'); + if ($res->success == 'true') { + $db_order = getdbosql( + 'db_orders', + 'market=:market AND uuid=:uuid', + array(':market'=>'livecoin', ':uuid'=>$id) + ); - if(!$balances || !is_array($balances)) return; - - $savebalance = getdbosql('db_balances', "name='$exchange'"); - if (is_object($savebalance)) { - $savebalance->balance = 0; - $savebalance->save(); - } - - foreach($balances as $balance) - { - if($balance->currency == 'BTC' && $balance->type == "available") { - if (!is_object($savebalance)) continue; - $savebalance->balance = $balance->value; - $savebalance->save(); - continue; - } - - if ($updatebalances) { - // store available balance in market table - $coins = getdbolist('db_coins', "symbol=:symbol OR symbol2=:symbol", - array(':symbol'=>$balance->currency) - ); - if (empty($coins)) continue; - foreach ($coins as $coin) { - $market = getdbosql('db_markets', "coinid=:coinid AND name='$exchange'", array(':coinid'=>$coin->id)); - if (!$market) continue; - if ($balance->type == "available") - $market->balance = $balance->value; - elseif ($balance->type == "trade") - $market->ontrade = $balance->value; - $market->balancetime = time(); - $market->save(); - } - } - } - - if (!YAAMP_ALLOW_EXCHANGE) return; + if ($db_order) { + $db_order->delete(); + } + } +} + +function doLiveCoinTrading($quick = false) +{ + $exchange = 'livecoin'; + $updatebalances = true; + + if (exchange_get($exchange, 'disabled')) { + return; + } + + $livecoin = new LiveCoinApi; + + $savebalance = getdbosql('db_balances', "name='$exchange'"); + if (is_object($savebalance)) { + $savebalance->balance = 0; + $savebalance->save(); + } else { + dborun("INSERT INTO balances (name,balance) VALUES ('$exchange',0)"); + return; + } + + $balances = $livecoin->getBalances(); + if (!$balances || !is_array($balances)) { + return; + } + + foreach ($balances as $balance) { + if ($balance->currency == 'BTC' && $balance->type == "available") { + if (!is_object($savebalance)) { + continue; + } + $savebalance->balance = $balance->value; + $savebalance->save(); + continue; + } + + if ($updatebalances) { + // store available balance in market table + $coins = getdbolist( + 'db_coins', + 'symbol=:symbol OR symbol2=:symbol', + array(':symbol'=>$balance->currency) + ); + + if (empty($coins)) { + continue; + } + + foreach ($coins as $coin) { + $market = getdbosql('db_markets', "coinid=:coinid AND name='$exchange'", array(':coinid'=>$coin->id)); + + if (!$market) { + continue; + } + + $market->balance = arraySafeVal($balance, 'Available', 0.0); + $market->ontrade = arraySafeVal($balance, 'Balance') - $market->balance; + $market->balancetime = time(); + $address = arraySafeVal($balance, 'CryptoAddress'); + if (!empty($address) && $market->deposit_address != $address) { + debuglog("$exchange: {$coin->symbol} deposit address updated"); + $market->deposit_address = $address; + } + $market->save(); + } + } + } + + if (!YAAMP_ALLOW_EXCHANGE) { + return; + } + + $flushall = rand(0, 8) == 0; + if ($quick) { + $flushall = false; + } + + // upgrade orders + $coins = getdbolist('db_coins', "enable=1 AND IFNULL(dontsell,0)=0 AND id IN (SELECT DISTINCT coinid FROM markets WHERE name='livecoin')"); + foreach ($coins as $coin) { + if ($coin->dontsell || $coin->symbol == 'BTC') { + continue; + } + + $pair = $coin->symbol.'/BTC'; + sleep(1); + $orders = $livecoin->getClientOrders($pair, 'OPEN'); + + if (isset($orders->success) || !isset($orders->data)) { + continue; + } + + foreach ($orders->data as $order) { + $uuid = $order['id']; + $pair = $order['currencyPair']; + sleep(1); + $ticker = $livecoin->getTickerInfo($pair); + + if (!$ticker) { + continue; + } + + if ($order['price'] > $cancel_ask_pct*$ticker->best_ask || $flushall) { + sleep(1); + doLiveCoinCancelOrder($pair, $uuid, $livecoin); + } else { + $db_order = getdbosql( + 'db_orders', + 'market=:market AND uuid=:uuid', + array(':market'=>'livecoin', ':uuid'=>$uuid) + ); + + if ($db_order) { + continue; + } + + $db_order = new db_orders; + $db_order->market = 'livecoin'; + $db_order->coinid = $coin->id; + $db_order->amount = $order['quantity']; + $db_order->price = $order['price']; + $db_order->ask = $ticker['best_ask']; + $db_order->bid = $ticker['best_sell']; + $db_order->uuid = $uuid; + $db_order->created = time(); + $db_order->save(); + } + } + $list = getdbolist('db_orders', "coinid=$coin->id and market='livecoin'"); + foreach ($list as $db_order) { + $found = false; + foreach ($orders->data as $order) { + $uuid = $order['id']; + if ($uuid == $db_order->uuid) { + $found = true; + break; + } + } + if (!$found) { + debuglog("LiveCoin: Deleting order $coin->name $db_order->amount"); + $db_order->delete(); + } + } + } + sleep(2); + + /* Update balances and sell */ + $balances = $livecoin->getBalances(); + if (!$balances) { + return; + } + + foreach ($balances as $balance) { + if ($balance->type != 'total') { + continue; + } + + $amount = $balance->value; + $symbol = $balance->currency; + if (!$amount || $symbol == 'BTC') { + continue; + } + + $coin = getdbosql('db_coins', "symbol=:symbol", array(':symbol'=>$symbol)); + if (!$coin || $coin->dontsell) { + continue; + } + + $market2 = getdbosql('db_markets', "coinid={$coin->id} AND (name='bittrex' OR name='poloniex')"); + if ($market2) { + continue; + } + + $market = getdbosql('db_markets', "coinid=$coin->id and name='livecoin'"); + if ($market) { + $market->lasttraded = time(); + $market->save(); + } + + if ($amount*$coin->price < $min_btc_trade) { + continue; + } + + $pair = "$symbol/BTC"; + $maxprice = 0; + $maxamount = 0; + + sleep(1); + $orders = $livecoin->getOrderBook($pair); + + if (!empty($orders) && !empty($orders->bids)) { + foreach ($orders->bids as $order) { + if ($order[0] > $maxprice) { + $maxprice = $order[0]; + $maxamount = $order[1]; + } + } + } + + if ($amount >= $maxamount && $maxamount*$maxprice > $min_btc_trade) { + $sellprice = bitcoinvaluetoa($maxprice); + debuglog("LiveCoin: Selling market $pair, $maxamount, $sellprice"); + sleep(1); + + // This needs to be simplified. + // Make sure API methods return a value? + $res = $livecoin->sellLimit($pair, $sellprice, $maxamount); + if (!$res) { + debuglog('LiveCoin: Sell failed'); + } else { + $success = 'false'; + if (isset($res->success)) { + $success = $res->success; + } + if ($success == 'false') { + debuglog('LiveCoin: Sell failed'); + } else { + $amount -= $maxamount; + } + } + sleep(1); + } + + sleep(1); + $ticker = $livecoin->getTickerInfo($pair); + if (!$ticker) { + continue; + } + + $sellprice = bitcoinvaluetoa($ticker->best_ask); + + sleep(1); + $res = $livecoin->sellLimit($pair, $sellprice, $amount); + + if (!($res->success == 'true' && $res->added == 'true')) { + continue; + } + + $db_order = new db_orders; + $db_order->market = 'livecoin'; + $db_order->coinid = $coin->id; + $db_order->amount = $amount; + $db_order->price = $sellprice; + $db_order->ask = $ticker->best_ask; + $db_order->bid = $ticker->best_bid; + $db_order->uuid = $res->orderId; + $db_order->created = time(); + $db_order->save(); + } + + /* Withdrawals */ + $withdraw_min = exchange_get($exchange, 'withdraw_min_btc', EXCH_AUTO_WITHDRAW); + $withdraw_fee = exchange_get($exchange, 'withdraw_fee_btc', 0.0002); + if (floatval($withdraw_min) > 0 && $savebalance->balance >= ($withdraw_min + $withdraw_fee)) { + $amount = $savebalance->balance - $withdraw_fee; + debuglog("$exchange: withdraw $amount BTC to $btcaddr"); + sleep(1); + $res = $livecoin->withdrawCoin($amount, 'BTC', $btcaddr); + debuglog("$exchange: withdraw ".json_encode($res)); + if ($res) { + $withdraw = new db_withdraws; + $withdraw->market = 'livecoin'; + $withdraw->address = $btcaddr; + $withdraw->amount = $amount; + $withdraw->time = time(); + $withdraw->uuid = $res->id; + $withdraw->save(); + $savebalance->balance = 0; + $savebalance->save(); + } + } } diff --git a/web/yaamp/core/trading/trading.php b/web/yaamp/core/trading/trading.php index d541903..230d86b 100644 --- a/web/yaamp/core/trading/trading.php +++ b/web/yaamp/core/trading/trading.php @@ -1,5 +1,4 @@ uuid); break; + case 'livecoin': + doLiveCoinCancelOrder($order->uuid); + break; + } } @@ -90,8 +93,8 @@ function runExchange($exchangeName=false) break; case 'livecoin': - doLivecoinTrading(true); - updateLivecoinMarkets(); + doLiveCoinTrading(true); + updateLiveCoinMarkets(); break; case 'nova':