From ab5e4115296a8e3582aedc65b96a3fc5b7c8c248 Mon Sep 17 00:00:00 2001 From: Tanguy Pruvot Date: Sun, 7 Jan 2018 16:57:11 +0100 Subject: [PATCH] kucoin exchange api + balances and icons Signed-off-by: Tanguy Pruvot --- web/keys.sample.php | 1 + web/serverconfig.sample.php | 3 + web/yaamp/commands/CoindbCommand.php | 42 +++++- web/yaamp/commands/ExchangeCommand.php | 5 + web/yaamp/core/backend/markets.php | 59 +++++++- web/yaamp/core/backend/rawcoins.php | 14 ++ web/yaamp/core/exchange/exchange.php | 3 + web/yaamp/core/exchange/kucoin.php | 133 ++++++++++++++++++ web/yaamp/core/trading/kucoin_trading.php | 56 ++++++++ web/yaamp/core/trading/trading.php | 9 ++ web/yaamp/defaultconfig.php | 1 + .../modules/thread/CronjobController.php | 1 + 12 files changed, 322 insertions(+), 5 deletions(-) create mode 100644 web/yaamp/core/exchange/kucoin.php create mode 100644 web/yaamp/core/trading/kucoin_trading.php diff --git a/web/keys.sample.php b/web/keys.sample.php index da2034f..a0fd40b 100644 --- a/web/keys.sample.php +++ b/web/keys.sample.php @@ -16,6 +16,7 @@ define('EXCH_CRYPTOPIA_SECRET', ''); define('EXCH_EMPOEX_SECKEY', ''); define('EXCH_HITBTC_SECRET', ''); define('EXCH_KRAKEN_SECRET',''); +define('EXCH_KUCOIN_SECRET', ''); define('EXCH_LIVECOIN_SECRET', ''); define('EXCH_NOVA_SECRET',''); define('EXCH_POLONIEX_SECRET', ''); diff --git a/web/serverconfig.sample.php b/web/serverconfig.sample.php index 742d166..40c083e 100644 --- a/web/serverconfig.sample.php +++ b/web/serverconfig.sample.php @@ -49,12 +49,15 @@ define('EXCH_BLEUTRADE_KEY', ''); define('EXCH_BTER_KEY', ''); define('EXCH_YOBIT_KEY', ''); define('EXCH_CCEX_KEY', ''); +define('EXCH_CEXIO_ID', ''); +define('EXCH_CEXIO_KEY', ''); define('EXCH_COINMARKETS_USER', ''); define('EXCH_COINMARKETS_PIN', ''); define('EXCH_BITSTAMP_ID',''); define('EXCH_BITSTAMP_KEY',''); define('EXCH_HITBTC_KEY',''); define('EXCH_KRAKEN_KEY', ''); +define('EXCH_KUCOIN_KEY', ''); define('EXCH_LIVECOIN_KEY', ''); define('EXCH_NOVA_KEY', ''); define('EXCH_STOCKSEXCHANGE_KEY', ''); diff --git a/web/yaamp/commands/CoindbCommand.php b/web/yaamp/commands/CoindbCommand.php index 41892a4..7d6913b 100644 --- a/web/yaamp/commands/CoindbCommand.php +++ b/web/yaamp/commands/CoindbCommand.php @@ -56,6 +56,7 @@ class CoindbCommand extends CConsoleCommand $nbUpdated += $this->grabBittrexIcons(); // can be huge ones $nbUpdated += $this->grabCoinExchangeIcons(); $nbUpdated += $this->grabAlcurexIcons(); + $nbUpdated += $this->grabKuCoinIcons(); $nbUpdated += $this->grabNovaIcons(); echo "total updated: $nbUpdated\n"; @@ -504,8 +505,7 @@ class CoindbCommand extends CConsoleCommand echo "alcurex: try to download new icons...\n"; foreach ($coins as $coin) { $coin = getdbo('db_coins', $coin["id"]); - $symbol = $coin->symbol; - if (!empty($coin->symbol2)) $symbol = $coin->symbol2; + $symbol = $coin->getOfficialSymbol(); $local = $this->basePath."/images/coin-{$symbol}.png"; try { $data = @ file_get_contents($url.strtoupper($symbol).'.png'); @@ -525,6 +525,41 @@ class CoindbCommand extends CConsoleCommand return $nbUpdated; } + /** + * Icon grabber - KuCoin + */ + public function grabKuCoinIcons() + { + $url = 'https://assets.kucoin.com/www/1.2.0/assets/coins/';//QLC.png + $nbUpdated = 0; + $sql = "SELECT DISTINCT coins.id FROM coins INNER JOIN markets M ON M.coinid = coins.id ". + "WHERE M.name='kucoin' AND IFNULL(coins.image,'') = ''"; + $coins = dbolist($sql); + if (empty($coins)) + return 0; + echo "kucoin: try to download new icons...\n"; + foreach ($coins as $coin) { + $coin = getdbo('db_coins', $coin["id"]); + $symbol = $coin->getOfficialSymbol(); + $local = $this->basePath."/images/coin-{$symbol}.png"; + try { + $data = @ file_get_contents($url.$symbol.'.png'); + } catch (Exception $e) { + continue; + } + if (strlen($data) < 2048) continue; + echo $symbol." icon found\n"; + file_put_contents($local, $data); + if (filesize($local) > 0) { + $coin->image = "/images/coin-{$symbol}.png"; + $nbUpdated += $coin->save(); + } + } + if ($nbUpdated) + echo "$nbUpdated icons downloaded from kucoin\n"; + return $nbUpdated; + } + /** * Icon grabber - NovaExchange */ @@ -540,8 +575,7 @@ class CoindbCommand extends CConsoleCommand echo "nova: try to download new icons...\n"; foreach ($coins as $coin) { $coin = getdbo('db_coins', $coin["id"]); - $symbol = $coin->symbol; - if (!empty($coin->symbol2)) $symbol = $coin->symbol2; + $symbol = $coin->getOfficialSymbol(); $local = $this->basePath."/images/coin-{$symbol}.png"; try { $data = @ file_get_contents($url.strtolower($symbol).'.png'); diff --git a/web/yaamp/commands/ExchangeCommand.php b/web/yaamp/commands/ExchangeCommand.php index 15f143f..44bc860 100644 --- a/web/yaamp/commands/ExchangeCommand.php +++ b/web/yaamp/commands/ExchangeCommand.php @@ -181,6 +181,11 @@ class ExchangeCommand extends CConsoleCommand $balance = kraken_api_user('Balance'); echo("kraken btc: ".json_encode($balance)."\n"); } + if (!empty(EXCH_KUCOIN_KEY)) { + $balance = kucoin_api_user('account/BTC/balance'); + if (!is_object($balance) || !isset($balance->data)) echo "kucoin error ".json_encode($balance)."\n"; + else echo("kucoin: ".json_encode($balance->data)."\n"); + } if (!empty(EXCH_LIVECOIN_KEY)) { $livecoin = new LiveCoinApi; $balance = $livecoin->getBalances('BTC'); diff --git a/web/yaamp/core/backend/markets.php b/web/yaamp/core/backend/markets.php index 83c5b27..14aed56 100644 --- a/web/yaamp/core/backend/markets.php +++ b/web/yaamp/core/backend/markets.php @@ -14,6 +14,7 @@ function BackendPricesUpdate() updatePoloniexMarkets(); updateBleutradeMarkets(); updateKrakenMarkets(); + updateKuCoinMarkets(); updateCCexMarkets(); updateCryptopiaMarkets(); updateHitBTCMarkets(); @@ -1167,6 +1168,62 @@ function updateEmpoexMarkets() } } +function updateKuCoinMarkets() +{ + $exchange = 'kucoin'; + if (exchange_get($exchange, 'disabled')) return; + + $markets = kucoin_api_query('open/symbols','market=BTC'); + if(!is_object($markets) || !isset($markets->data) || empty($markets->data)) return; + + $coininfo = NULL; //kucoin_api_query('market/open/coins'); + if(!is_object($coininfo) || !isset($coininfo->data) || empty($coininfo->data)) { + $coininfo = NULL; + } + + $list = getdbolist('db_markets', "name='$exchange'"); + foreach($list as $market) + { + $coin = getdbo('db_coins', $market->coinid); + if(!$coin) continue; + + $symbol = $coin->getOfficialSymbol(); + if (market_get($exchange, $symbol, "disabled")) { + $market->disabled = 1; + $market->message = 'disabled from settings'; + $market->save(); + continue; + } + + $pair = strtoupper($symbol).'-BTC'; + + foreach ($markets->data as $ticker) { + if ($ticker->symbol != $pair) continue; + + $market->price = AverageIncrement($market->price, $ticker->buy); + $market->price2 = AverageIncrement($market->price2, $ticker->sell); + if (!empty($coininfo)) foreach ($coininfo->data as $info) { + if ($info->coin == $symbol) { + //todo: $market->withdrawfee = $info->withdrawMinFee; + break; + } + } + $market->txfee = $ticker->feeRate * 100; // is 0.1% for trades (0.001) + $market->priority = -1; + $market->pricetime = time(); + + if (floatval($ticker->vol) > 0.01) + $market->save(); + + if (empty($coin->price2)) { + $coin->price = $market->price; + $coin->price2 = $market->price2; + $coin->save(); + } + } + } +} + function updateLiveCoinMarkets() { $exchange = 'livecoin'; @@ -1489,7 +1546,7 @@ function updateShapeShiftMarkets() $market->price = AverageIncrement($market->price, $ticker['rate']); $market->price2 = AverageIncrement($market->price2, $ticker['rate']); - $market->txfee = $ticker['minerFee']; + $market->txfee = $ticker['minerFee'] * 100; $market->pricetime = time(); $market->priority = -1; // not ready for trading $market->save(); diff --git a/web/yaamp/core/backend/rawcoins.php b/web/yaamp/core/backend/rawcoins.php index 8e7bdf9..670af51 100644 --- a/web/yaamp/core/backend/rawcoins.php +++ b/web/yaamp/core/backend/rawcoins.php @@ -259,6 +259,20 @@ function updateRawcoins() } } + if (!exchange_get('kucoin', 'disabled')) { + $list = kucoin_api_query('market/open/coins'); + if(is_object($list) && isset($list->data) && !empty($list->data)) + { + dborun("UPDATE markets SET deleted=true WHERE name='kucoin'"); + foreach($list->data as $item) { + $symbol = $item->coin; + $name = $item->name; + if (strpos($item->withdrawRemark,'Ethereum')) continue; + updateRawCoin('kucoin', $symbol, $name); + } + } + } + if (!exchange_get('livecoin', 'disabled')) { $list = livecoin_api_query('exchange/ticker'); if(is_array($list)) diff --git a/web/yaamp/core/exchange/exchange.php b/web/yaamp/core/exchange/exchange.php index b690f83..6a8b851 100644 --- a/web/yaamp/core/exchange/exchange.php +++ b/web/yaamp/core/exchange/exchange.php @@ -29,6 +29,7 @@ require_once("alcurex.php"); require_once("binance.php"); require_once("cryptopia.php"); require_once("hitbtc.php"); +require_once("kucoin.php"); require_once("livecoin.php"); require_once("nova.php"); require_once("coinexchange.php"); @@ -101,6 +102,8 @@ function getMarketUrl($coin, $marketName) $url = "http://jubi.com/coin/{$lowsymbol}"; else if($market == 'hitbtc') $url = "https://hitbtc.com/exchange/{$symbol}-to-{$base}"; + else if($market == 'kucoin') + $url = "https://www.kucoin.com/#/trade.pro/{$symbol}-{$base}"; else if($market == 'livecoin') $url = "https://www.livecoin.net/trade/?currencyPair={$symbol}%2F{$base}"; else if($market == 'nova') diff --git a/web/yaamp/core/exchange/kucoin.php b/web/yaamp/core/exchange/kucoin.php new file mode 100644 index 0000000..ac71b67 --- /dev/null +++ b/web/yaamp/core/exchange/kucoin.php @@ -0,0 +1,133 @@ +/wallet/address + +function kucoin_api_user($method, $params=NULL, $isPostMethod=false) +{ + require_once('/etc/yiimp/keys.php'); + if (!defined('EXCH_KUCOIN_SECRET')) define('EXCH_KUCOIN_SECRET', ''); + + if (empty(EXCH_KUCOIN_KEY) || empty(EXCH_KUCOIN_SECRET)) return false; + + $api_host = 'https://api.kucoin.com'; + $mt = explode(' ', microtime()); + $nonce = $mt[1].substr($mt[0], 2, 3); + $url = $endpoint = "/v1/$method"; + $tosign = "$endpoint/$nonce/"; + + if (empty($params)) $params = array(); + $query = http_build_query($params); + if (strlen($query) && !$isPostMethod) { + $url .= '&'.$query; $query = ''; + } + if ($isPostMethod) $post_data = $params; + $hmac = strtolower(hash_hmac('sha256', base64_encode($tosign.$query), EXCH_KUCOIN_SECRET)); + + $headers = array( + 'Content-Type: application/json;charset=UTF-8', + 'KC-API-KEY: '.EXCH_KUCOIN_KEY, + 'KC-API-NONCE: '.$nonce, + 'KC-API-SIGNATURE: '.$hmac, + ); + + $ch = curl_init(); + + curl_setopt($ch, CURLOPT_URL, $api_host.$url); + curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); + if ($isPostMethod) { + curl_setopt($ch, CURLOPT_POST, true); + curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data); + } + curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); + curl_setopt($ch, CURLOPT_CONNECTTIMEOUT, 10); + curl_setopt($ch, CURLOPT_TIMEOUT, 30); + curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; KuCoin API PHP client; '.php_uname('s').'; PHP/'.phpversion().')'); + curl_setopt($ch, CURLOPT_ENCODING , ''); + //curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); + //curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, false); + //curl_setopt($ch, CURLOPT_VERBOSE, 1); + + $res = curl_exec($ch); + if($res === false) { + $e = curl_error($ch); + debuglog("kucoin: $e"); + curl_close($ch); + return false; + } + + $result = json_decode($res); + if(!is_object($result) && !is_array($result)) { + $status = curl_getinfo($ch, CURLINFO_HTTP_CODE); + debuglog("kucoin: $method failed ($status) ".strip_data($res)); + } + + curl_close($ch); + + return $result; +} + +////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +// manual update of one market +function kucoin_update_market($market) +{ + $exchange = 'kucoin'; + if (is_string($market)) + { + $symbol = $market; + $coin = getdbosql('db_coins', "symbol=:sym", array(':sym'=>$symbol)); + if(!$coin) return false; + $pair = $symbol.'-BTC'; + $market = getdbosql('db_markets', "coinid={$coin->id} AND name='$exchange'"); + if(!$market) return false; + + } else if (is_object($market)) { + + $coin = getdbo('db_coins', $market->coinid); + if(!$coin) return false; + $symbol = $coin->getOfficialSymbol(); + $pair = $symbol.'-BTC'; + if (!empty($market->base_coin)) $pair = $symbol.'-'.$market->base_coin; + } + + $t1 = microtime(true); + $query = kucoin_api_query("$pair/open/tick"); + if(!is_object($query) || !isset($query->data)) return false; + $ticker = $ticker->data; + + $price2 = ((double) $ticker->buy + (double)$ticker->sell)/2; + $market->price2 = AverageIncrement($market->price2, $price2); + $market->price = AverageIncrement($market->price, $ticker->buy); + $market->pricetime = time(); + $market->save(); + + $apims = round((microtime(true) - $t1)*1000,3); + user()->setFlash('message', "$exchange $symbol price updated in $apims ms"); + + return true; +} diff --git a/web/yaamp/core/trading/kucoin_trading.php b/web/yaamp/core/trading/kucoin_trading.php new file mode 100644 index 0000000..33fe34d --- /dev/null +++ b/web/yaamp/core/trading/kucoin_trading.php @@ -0,0 +1,56 @@ +data)) return; + + $savebalance = getdbosql('db_balances', "name='$exchange'"); + + if (is_array($data->data)) + foreach($data->data as $balance) + { + if ($balance->coinType == 'BTC') { + if (is_object($savebalance)) { + $savebalance->balance = $balance->balance; + $savebalance->save(); + } + continue; + } + + if ($updatebalances) { + // store available balance in market table + $coins = getdbolist('db_coins', "symbol=:symbol OR symbol2=:symbol", + array(':symbol'=>$balance->coinType) + ); + if (empty($coins)) continue; + foreach ($coins as $coin) { + $market = getdbosql('db_markets', + "coinid=:coinid AND name='$exchange' ORDER BY balance" + , array(':coinid'=>$coin->id) + ); + if (!$market) continue; + $market->balance = $balance->balance; + $market->ontrade = $balance->freezeBalance; + $market->balancetime = time(); + $market->save(); + } + } + } + + if (!YAAMP_ALLOW_EXCHANGE) return; + + // real trading, todo.. +} diff --git a/web/yaamp/core/trading/trading.php b/web/yaamp/core/trading/trading.php index 525fa25..1ce4773 100644 --- a/web/yaamp/core/trading/trading.php +++ b/web/yaamp/core/trading/trading.php @@ -10,6 +10,7 @@ require_once('alcurex_trading.php'); require_once('coinsmarkets_trading.php'); require_once('cryptopia_trading.php'); require_once('hitbtc_trading.php'); +require_once('kucoin_trading.php'); require_once('livecoin_trading.php'); require_once('nova_trading.php'); @@ -37,6 +38,9 @@ function cancelExchangeOrder($order=false) case 'hitbtc': doHitBTCCancelOrder($order->uuid); break; + case 'kucoin': + doKuCoinCancelOrder($order->uuid); + break; case 'livecoin': doLiveCoinCancelOrder($order->uuid); break; @@ -113,6 +117,11 @@ function runExchange($exchangeName=false) updateKrakenMarkets(); break; + case 'kucoin': + doKuCoinTrading(true); + updateKucoinMarkets(); + break; + case 'livecoin': doLiveCoinTrading(true); updateLiveCoinMarkets(); diff --git a/web/yaamp/defaultconfig.php b/web/yaamp/defaultconfig.php index c260f44..26ff494 100644 --- a/web/yaamp/defaultconfig.php +++ b/web/yaamp/defaultconfig.php @@ -39,6 +39,7 @@ if (!defined('EXCH_HITBTC_KEY')) define('EXCH_HITBTC_KEY', ''); if (!defined('EXCH_POLONIEX_KEY')) define('EXCH_POLONIEX_KEY', ''); if (!defined('EXCH_YOBIT_KEY')) define('EXCH_YOBIT_KEY', ''); if (!defined('EXCH_KRAKEN_KEY')) define('EXCH_KRAKEN_KEY', ''); +if (!defined('EXCH_KUCOIN_KEY')) define('EXCH_KUCOIN_KEY', ''); if (!defined('EXCH_LIVECOIN_KEY')) define('EXCH_LIVECOIN_KEY', ''); if (!defined('EXCH_NOVA_KEY')) define('EXCH_NOVA_KEY', ''); if (!defined('EXCH_STOCKSEXCHANGE_KEY')) define('EXCH_STOCKSEXCHANGE_KEY', ''); diff --git a/web/yaamp/modules/thread/CronjobController.php b/web/yaamp/modules/thread/CronjobController.php index b6bd7db..9d00dfd 100644 --- a/web/yaamp/modules/thread/CronjobController.php +++ b/web/yaamp/modules/thread/CronjobController.php @@ -147,6 +147,7 @@ class CronjobController extends CommonController doCCexTrading(); doBterTrading(); doBleutradeTrading(); + doKuCoinTrading(); doNovaTrading(); doCoinsMarketsTrading(); break;