From c91df106fde6158e3f3283966dc3d3ee6efcc82c Mon Sep 17 00:00:00 2001 From: root Date: Sat, 3 Feb 2018 11:54:52 +0000 Subject: [PATCH 01/10] Add other exchanges IDs --- bot/xchange/map.11 | 1 + bot/xchange/map.13 | 1 + bot/xchange/map.5 | 1 + bot/xchange/map.7 | 1 + web/xchange/11.ico | Bin 0 -> 1150 bytes web/xchange/13.ico | Bin 0 -> 16958 bytes web/xchange/5.ico | Bin 0 -> 16958 bytes 7 files changed, 4 insertions(+) create mode 100644 bot/xchange/map.11 create mode 100644 bot/xchange/map.13 create mode 100644 bot/xchange/map.5 create mode 100644 bot/xchange/map.7 create mode 100644 web/xchange/11.ico create mode 100755 web/xchange/13.ico create mode 100644 web/xchange/5.ico diff --git a/bot/xchange/map.11 b/bot/xchange/map.11 new file mode 100644 index 0000000..797ef74 --- /dev/null +++ b/bot/xchange/map.11 @@ -0,0 +1 @@ +Bxintx \ No newline at end of file diff --git a/bot/xchange/map.13 b/bot/xchange/map.13 new file mode 100644 index 0000000..cd86046 --- /dev/null +++ b/bot/xchange/map.13 @@ -0,0 +1 @@ +Kraken \ No newline at end of file diff --git a/bot/xchange/map.5 b/bot/xchange/map.5 new file mode 100644 index 0000000..f30be2b --- /dev/null +++ b/bot/xchange/map.5 @@ -0,0 +1 @@ +Bitfinex \ No newline at end of file diff --git a/bot/xchange/map.7 b/bot/xchange/map.7 new file mode 100644 index 0000000..1ae6928 --- /dev/null +++ b/bot/xchange/map.7 @@ -0,0 +1 @@ +Hitbtc \ No newline at end of file diff --git a/web/xchange/11.ico b/web/xchange/11.ico new file mode 100644 index 0000000000000000000000000000000000000000..d4b2fbff978dd8fe1173d779f7f7469ca47f77bd GIT binary patch literal 1150 zcmZQzU<5(|0R|wcz>vYhz#zuJz@P!dKp~(AL>x#lFaYHSEBN2&VYwt&f6Hoi#YdaTch7(Bt#Y~$EAT?n3Hh4BI z2Fe5NTCz6D?Elj?uXAwCK*s;>0ES(8w*Mbhxt;)tf2efXyf9D??6>t1MxQ}?w?>=1 z{$K0Hur;KmuFmor+{x|Jj3;*{AB&?1x{(m&d;@1Cq51?7@4DTvjrb6967o`7ex#Q!F znU)LIrCTiiJt1J<_FS9)yMgxqZvy&va^TJ#Kt9k7a4SIJw=dr8;f4sKmmvLHa;*RF zEwBS)kiENs_o#u<2DQo gjX=x@#1;|^3^t7r3}S=CK$snfK`~Je)dSK80MK6>Z2$lO literal 0 HcmV?d00001 diff --git a/web/xchange/13.ico b/web/xchange/13.ico new file mode 100755 index 0000000000000000000000000000000000000000..a9261776c88cca5bb94d7c864701c33335d99d1f GIT binary patch literal 16958 zcmeI2X^a#_6vt;7VMV}E$*xy)Q6g?43F3)6K}{g~6^-%0M9nTSUJ<@X zh(=-r)EGQt3>q}(pv1TujZyq!3=SUPs;i?MuCmhp{ykOWOfy|Q*UlbvZ+`o_tLnXa z?{#%`b?qh+E&R-#o8b4fL}ynb(Vj>oI)S1@52)Nf=H^BGJI+9yfj9$k2I36F8Hh6w zXCR+}jSroeFib(72baU?a41xiCbfeJFbl4NE;t%|+fhbrJEN0EUIf!22~p~Pz1qP< zm<{vc3^)uzeXw=Nnea07!vO4rm2eC=`&T_Ecgx@h_#1Y?7PuHf`X>wH< zJNCqskB0bPz}J#F$oO8#_#X@LuP0v3b@tZz&okEtOZ-~fDb|BE{?~}TWF4nmn4Cn- z|6^hNzaGawV(-7!KN%Rx_y16if35$1bNCb%f>PY8$T*v2Vv_!y&GJ*4^po_g5CRNl-~ql z-;c8U3A90&{_)g(1z|qyrS1xczSi}t9s07nO8UYc=2|&f17Uru{C#K#tLjT#gWf4o z`Z?NMX4)5Q*lXmqup3-HJWhSd`b~x?6Vt(!KiNIt9vWTgtIpN;Kc>wj=@j)Ve-^{< z;L=y!2ha)DXDd&mtQVs6f2NJ{RQQJDTsAa!DmU6^aM`$xdcRLUfd6x+vYX-G2>m_O zuYn8UR5%|V1wEUter=un`v`6T>5CiTW3X+a>$f6+KTg}QIS z_A%DvR$|i}YlnV8stoiDZGy2Dw~gl&V$(cbh})j1%>pvI z#!{_c+lghCVv9P|2F8QyZz0w8KiutC63+o^=5o0(@hb;fCun^$+`iY_zwW)(iT>Cw z7jv-8v`r!(fLhje_4?c>efi!4;~*Gj=(XA!PC#yfM)}ihJDc&{U+*cNY7VtGT!P#Q z&Gx$T_g6cqMSqx^1kJ$)+&+6%r8GSG~9LC^Wya3wTsj;}R0 z_+ZCE8|RB*6I8N?TJ3ny{>27Z2%3leerxr+(R$V&q~4R4!YcRzv=*skZ#I|@t=$!~ z_QfBAc`yY=RNTQlTK|w*H)`!YAMS(o&){LFzFa2)6!kJ@;fmDjkRI0JD8;ta$Yh%*ppAkILo z8Sol(45SCrgB^n%IsK#tQrT24m21tlW;x3uGyE=+Npn3!GTWNrC!3_tWKS~F!g<0! zLS-&rp9;!>e0fJu4(7|#R_@PVcYAs16=aVJT0Ac!ne@D@WUJ@-9i=?aH{9WQzJYG; ZhU#@wGJbQvAkkXcSx`$f~&- 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 Date: Sat, 3 Feb 2018 12:07:14 +0000 Subject: [PATCH 02/10] Add IDs to js --- web/ui.js | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/web/ui.js b/web/ui.js index 94f317a..bfd8730 100644 --- a/web/ui.js +++ b/web/ui.js @@ -91,7 +91,7 @@ $(function() { function no2e(x, title) { var num = parseInt(x); - if (num <= 9 && num >= 0) { + if (num <= 100 && num >= 0) { return '' + '' + ''; @@ -111,10 +111,16 @@ $(function() { return "BLTRD"; else if (x === "3" || x === 3) return "BTTRX"; + else if (x === "5" || x === 5) + return "BTFNX"; else if (x === "7" || x === 7) return "HTBTC"; else if (x === "9" || x === 9) return "BINCE"; + else if (x === "11" || x === 11) + return "BXTH"; + else if (x === "13" || x === 13) + return "KRKN"; else return "?"; @@ -137,10 +143,16 @@ $(function() { return "Bleutrade"; else if (x === "3" || x === 3) return "Bittrex"; + else if (x === "5" || x === 5) + return "Bitfinex"; else if (x === "7" || x === 7) return "HitBTC"; else if (x === "9" || x === 9) return "Binance"; + else if (x === "11" || x === 11) + return "Bxinth"; + else if (x === "13" || x === 13) + return "Kraken"; else return "?"; From 4153b6b7039a6d22f9d12840535e6f0b82927fb8 Mon Sep 17 00:00:00 2001 From: root Date: Sun, 4 Feb 2018 07:45:34 +0000 Subject: [PATCH 03/10] Hitbtc --- bot/xchange/Hitbtc.php | 348 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 348 insertions(+) create mode 100644 bot/xchange/Hitbtc.php diff --git a/bot/xchange/Hitbtc.php b/bot/xchange/Hitbtc.php new file mode 100644 index 0000000..2268232 --- /dev/null +++ b/bot/xchange/Hitbtc.php @@ -0,0 +1,348 @@ +exchange->rateLimit; + return $limit; + } + + public function isMarketActive( $market ) { + + $tradeable = $market[ 'base' ]; + + if ( !$this->exchange->currencies[ $tradeable ][ 'payout' ] || !$this->exchange->currencies[ $tradeable ][ 'payin' ] ) { + logg( $this->prefix() . "Coin '$tradeable' is disabled by Exchange!", true ); + return false; + } + + return $market[ 'active' ]; + } + + public function checkAPIReturnValue( $result ) { + if ( isset( $result[ 'error' ] ) ) { + return false; + } + if ( isset( $result[ 'address' ] ) ) { + return true; + } + if ( isset( $result[ 'id' ] ) ) { + return true; + } + return $result[ 'result' ] === true; + } + + public function getDepositHistory() { + + $history = []; + $results = $this->exchange->privateGetAccountTransactions(); + + if(is_array($results)){ + foreach ($results as $row){ + if(!in_array($row[ 'type' ], [ 'payin', 'deposit'])){ + continue; + } + $history[] = $row; + } + } + + return array( + + 'history' => $history, + 'statusKey' => 'status', + 'coinKey' => 'currency', + 'amountKey' => 'amount', + 'timeKey' => 'createdAt', + 'txidKey' => 'id', + 'addressKey' => 'address', + 'pending' => 'pending', + 'completed' => 'success', + + ); + + } + + public function getWithdrawalHistory() { + + $history = []; + $results = $this->exchange->privateGetAccountTransactions(); + + if(is_array($results)){ + foreach ($results as $row){ + if(!in_array($row[ 'type' ], [ 'payout', 'withdraw'])){ + continue; + } + $history[] = $row; + } + } + + return array( + + 'history' => $history, + 'statusKey' => 'status', + 'coinKey' => 'currency', + 'amountKey' => 'amount', + 'timeKey' => 'createdAt', + 'txidKey' => 'id', + 'addressKey' => 'address', + 'pending' => 'pending', + 'completed' => 'success', + + ); + + } + + // Changed functions! + + public function getTickers( $currency ) { + + $currency = $this->coinNames[ $currency ]; + $markets = $this->exchange->loadMarkets(); + + $tickers = $this->exchange->fetchTickers( [] ); + $ticker = [ ]; + foreach ( $markets as $market ) { + if ( $market[ 'quote' ] != $currency ) { + continue; + } + // The API doesn't provide the last price, so we just take an average into our spread. :-( + $ticker[ $market[ 'base' ] ] = formatBTC( $tickers[ $market[ 'symbol' ] ][ 'info' ][ 'last' ] ); + } + + return $ticker; + + } + + public function getDepositAddress( $coin ) { + + $coin = $this->coinNames[ $coin ]; + if ( !$this->exchange->currencies[ $coin ][ 'payin' ] ) { + logg( $this->prefix() . "Coin '$coin' is disabled now for deposit!", true ); + return null; + } + $result = $this->exchange->fetch_deposit_address( $coin ); + if ( !isset( $result[ 'address' ] ) ) { + logg( $this->prefix() . "Please generate a deposit address for $coin!", true ); + return null; + } + return $result[ 'address' ]; + + } + + public function withdraw( $coin, $amount, $address ) { + + $coin = $this->coinNames[ $coin ]; + if ( !$this->exchange->currencies[ $coin ][ 'payout' ] ) { + logg( $this->prefix() . "Coin '$coin' is disabled now for withdraw!", true ); + return false; + } + if ( floatval( $amount ) === 0 ) { + // Simulate an error which ccxt may not raise for us + throw new Exception( "API error response: Amount must be greater than zero" ); + } + + $precisions = $this->getPrecision( $coin, 'BTC' ); + $amount = sprintf( "%." . $precisions[ 'amount' ] . "f", $amount ); + + $result = $this->exchange->privatePostAccountTransfer(array ( + 'currency' => $coin, + 'amount' => floatval ($amount), + 'type' => 'exchangeToBank', + )); + sleep(2); + + return $this->checkAPIReturnValue( $this->exchange->withdraw( $coin, $amount, $address, null, [ 'includeFee' => 'true' ] ) ); + + } + + private function common_currency_code ($currency) { +// if ($currency == 'BTC') +// return 'XBT'; +// if ($currency == 'DASH') +// return 'DRK'; + if ($currency == 'BITCLAVE') + return 'CAT'; +// if ($currency == 'USDT') +// return 'USD'; + return $currency; + } + + public function buy( $tradeable, $currency, $rate, $amount ) { + try { + $precisions = $this->getPrecision( $tradeable, $currency ); + $limits = $this->getLimits( $tradeable, $currency ); + $amount = round($amount, $precisions[ 'amount' ]); + if($limits[ 'amount' ][ 'min' ] > $amount){ + logg( $this->prefix() . "Amount for $tradeable less MIN in buy()!" ); + return null; + } + + $result = $this->exchange->create_order( $this->common_currency_code ($tradeable) . '/' . $this->common_currency_code ($currency), 'limit', 'buy', + $amount, $rate ); + return $currency . '_' . $tradeable . ':' . $result[ 'id' ]; + } + catch ( Exception $ex ) { + logg( $this->prefix() . "Got an exception in buy(): " . $ex->getMessage() ); + return null; + } + } + + public function sell( $tradeable, $currency, $rate, $amount ) { + try { + $precisions = $this->getPrecision( $tradeable, $currency ); + $limits = $this->getLimits( $tradeable, $currency ); + $amount = round($amount, $precisions[ 'amount' ]); + if($limits[ 'amount' ][ 'min' ] > $amount){ + logg( $this->prefix() . "Amount for $tradeable less MIN in sell()!" ); + return null; + } + + $result = $this->exchange->create_order( $this->common_currency_code ($tradeable) . '/' . $this->common_currency_code ($currency), 'limit', 'sell', + $amount, $rate ); + return $currency . '_' . $tradeable . ':' . $result[ 'id' ]; + } + catch ( Exception $ex ) { + logg( $this->prefix() . "Got an exception in sell(): " . $ex->getMessage() ); + return null; + } + } + + public function cancelAllOrders() { + + $pairs = $this->getWithdrawablePairs(); + foreach ( $pairs as $pair ) { + $split = explode( '_', $pair ); + $currency = $split[ 1 ]; + $tradeable = $split[ 0 ]; + $currency = $this->coinNames[ $currency ]; + $tradeable = $this->coinNames[ $tradeable ]; + + $orders = $this->exchange->fetch_open_orders( $tradeable . '/' . $currency ); + foreach ( $orders as $order ) { + $orderID = $order[ 'order' ]; + $split = explode( '/', $order[ 'symbol' ] ); + $tradeable = $split[ 0 ]; + $currency = $split[ 1 ]; + $tradeable = $this->coinNames[ $tradeable ]; + $currency = $this->coinNames[ $currency ]; + $this->cancelOrder( $currency . '_' . $tradeable . ':' . $orderID ); + } + } + + } + + private function exchangeBalanceToTrade() { + $balances = $this->exchange->privateGetAccountBalance(); + if(is_array($balances)){ + foreach ($balances as $balance){ + $coin = $balance[ 'currency' ]; + $amount = $balance[ 'available' ]; + if($amount == 0){ + continue; + } + $this->exchange->privatePostAccountTransfer( array( + 'currency' => $coin, + 'amount' => $amount, + 'type' => 'bankToExchange' + ) ); + } + } + } + + private function queryBalances() { + return $this->exchange->fetch_balance()[ 'free' ]; + } + + public function refreshWallets( $inBetweenTrades = false ) { + + $this->exchangeBalanceToTrade(); + + if ( !$inBetweenTrades ) { + $this->preRefreshWallets(); + } + + $wallets = [ ]; + + $currencies = $this->exchange->currencies; + foreach ($currencies as $coin => $row){ + $wallets[ strtoupper( $coin ) ] = 0; + } + + $arr = $this->queryBalances(); + foreach ( $arr as $coin => $balance ) { + $wallets[ strtoupper( $coin ) ] = $balance; + } + + $this->wallets = $wallets; + + if ( !$inBetweenTrades ) { + $this->postRefreshWallets(); + } + + } + + public function refreshExchangeData() { + + $pairs = [ ]; + $precisions = [ ]; + $limits = [ ]; + $tradeFees = [ ]; + $conf = [ ]; + $markets = $this->exchange->loadMarkets(); + + foreach ( $markets as $market ) { + + $tradeable = $market[ 'base' ]; + $currency = $market[ 'quote' ]; + + if ( !Config::isCurrency( $currency ) || + Config::isBlocked( $tradeable ) ) { + continue; + } + + if ( !$this->isMarketActive( $market ) ) { //Oleg + continue; + } + + $this->coinNames[ strtoupper( $tradeable ) ] = $tradeable; + $this->coinNames[ strtoupper( $currency ) ] = $currency; + + $pair = strtoupper( $tradeable . "_" . $currency ); + $pairs[] = $pair; + $precisions[ $pair ] = $market[ 'precision' ]; + $limits[ $pair ] = $market[ 'limits' ]; + // We can't be sure whether we'll be the maker or the taker in each trade, so we'll assume the worst. + $tradeFees[ $pair ] = max( $market[ 'maker' ], $market[ 'taker' ] ); + $conf[ $tradeable ] = 0; // unknown! + } + + $this->pairs = $pairs; + $this->confirmationTimes = $conf; + $this->tradeFees = $tradeFees; + $this->precisions = $precisions; + $this->limits = $limits; + $this->withdrawFees = $this->exchange->fees[ 'funding' ][ 'withdraw' ]; + $this->depositFees = $this->exchange->fees[ 'funding' ][ 'deposit' ]; + + $this->calculateTradeablePairs(); + + } + +}; From 8ad9a4b7672b09856f7a025f294635f7838f6653 Mon Sep 17 00:00:00 2001 From: OlegVic Date: Sun, 4 Feb 2018 07:59:17 +0000 Subject: [PATCH 04/10] Not all exchenges have key ACTIVE --- bot/CCXTAdapter.php | 2 +- bot/xchange/Binance.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/bot/CCXTAdapter.php b/bot/CCXTAdapter.php index ea3bb76..d983f2a 100644 --- a/bot/CCXTAdapter.php +++ b/bot/CCXTAdapter.php @@ -392,7 +392,7 @@ public function refreshExchangeData() { continue; } - if ( !$market[ 'active' ] || !$this->isMarketActive( $market ) ) { + if ( !$this->isMarketActive( $market ) ) { continue; } diff --git a/bot/xchange/Binance.php b/bot/xchange/Binance.php index 376806c..11909b9 100644 --- a/bot/xchange/Binance.php +++ b/bot/xchange/Binance.php @@ -31,7 +31,7 @@ public function getRateLimit() { } public function isMarketActive( $market ) { - return $market[ 'info' ][ 'status' ] == 'TRADING'; + return $market[ 'active' ] || $market[ 'info' ][ 'status' ] == 'TRADING'; } public function checkAPIReturnValue( $result ) { From 2b4aab5a53747b0d687adefe1e31fced5b93dbf4 Mon Sep 17 00:00:00 2001 From: OlegVic Date: Fri, 9 Feb 2018 11:14:36 +0000 Subject: [PATCH 05/10] Added Cryptopia and some changes in Hitbtc --- bot/CoinManager.php | 8 +- bot/xchange/Cryptopia.php | 282 ++++++++++++++++++++++++++++++++++++++ bot/xchange/Hitbtc.php | 31 ++++- bot/xchange/map.6 | 1 + web/ui.js | 4 + web/xchange/6.ico | Bin 0 -> 15086 bytes 6 files changed, 317 insertions(+), 9 deletions(-) create mode 100644 bot/xchange/Cryptopia.php create mode 100644 bot/xchange/map.6 create mode 100755 web/xchange/6.ico diff --git a/bot/CoinManager.php b/bot/CoinManager.php index 8fa87bd..270f044 100644 --- a/bot/CoinManager.php +++ b/bot/CoinManager.php @@ -872,15 +872,15 @@ public function withdraw( $source, $target, $coin, $amount ) { if ( !is_null( $limits[ 'amount' ][ 'min' ] ) && floatval( $limits[ 'amount' ][ 'min' ] ) > $amount ) { - logg( sprintf( "Withdrawal amount %s below minimum trade amount %s", - $amount, $limits[ 'amount' ][ 'min' ] ) ); + logg( sprintf( "[%s] Withdrawal amount %s below minimum trade amount %s", + $source->getName(), $amount, $limits[ 'amount' ][ 'min' ] ) ); return false; } if ( !is_null( $limits[ 'amount' ][ 'max' ] ) && floatval( $limits[ 'amount' ][ 'max' ] ) < $amount ) { - logg( sprintf( "Withdrawal amount %s above maximum trade amount %s", - $amount, $limits[ 'amount' ][ 'max' ] ) ); + logg( sprintf( "[%s] Withdrawal amount %s above maximum trade amount %s", + $source->getName(), $amount, $limits[ 'amount' ][ 'max' ] ) ); return false; } } diff --git a/bot/xchange/Cryptopia.php b/bot/xchange/Cryptopia.php new file mode 100644 index 0000000..3482d3f --- /dev/null +++ b/bot/xchange/Cryptopia.php @@ -0,0 +1,282 @@ +exchange->rateLimit; + return $limit; + } + + public function isMarketActive( $market ) { + return $market[ 'active' ]; + } + + public function checkAPIReturnValue( $result ) { +// if ( !$result[ 'info' ][ 'Error' ] ) { +// return false; +// } + return $result[ 'info' ][ 'Success' ] === true; + } + + public function getDepositHistory() { + + $history = [ ]; + + $result = $this->exchange->privatePostGetTransactions( [ 'Type' => 'Deposit' ] ); + + if($result[ 'Success' ]) { + $history = $result[ 'Data' ]; + } + + return array( + + 'history' => $history, + 'statusKey' => 'Status', + 'coinKey' => 'Currency', + 'amountKey' => 'Amount', + 'timeKey' => 'TimeStamp', + 'pending' => 'Pending', + 'completed' => 'Success', + + ); + + } + + public function getWithdrawalHistory() { + + $history = [ ]; + + $result = $this->exchange->privatePostGetTransactions( [ 'Type' => 'Deposit' ] ); + + if($result[ 'Success' ]) { + $history = $result[ 'Data' ]; + } + + return array( + + 'history' => $history, + 'statusKey' => 'Status', + 'coinKey' => 'Currency', + 'amountKey' => 'Amount', + 'timeKey' => 'TimeStamp', + 'txidKey' => 'TxId', + 'addressKey' => 'Address', + 'pending' => 'Pending', + 'completed' => 'Success', + + ); + + } + + // Changed functions! + + public function refreshExchangeData() { + + $pairs = [ ]; + $precisions = [ ]; + $limits = [ ]; + $tradeFees = [ ]; + $conf = [ ]; + $fees = [ ]; + $markets = $this->exchange->loadMarkets(); + + $currencies = $this->exchange->fetch_currencies(); + + foreach ( $markets as $market ) { + + $tradeable = $market[ 'base' ]; + $currency = $market[ 'quote' ]; + + if ( !Config::isCurrency( $currency ) || + Config::isBlocked( $tradeable ) ) { + continue; + } + + if ( !$this->isMarketActive( $market ) ) { + continue; + } + + if ( !key_exists( $tradeable, $currencies ) || !$currencies[ $tradeable ] ) { + logg( $this->prefix() . "Coin '$tradeable' is disabled by Exchange!", true ); + continue; + } + + $this->coinNames[ strtoupper( $tradeable ) ] = $tradeable; + $this->coinNames[ strtoupper( $currency ) ] = $currency; + + $pair = strtoupper( $tradeable . "_" . $currency ); + $pairs[] = $pair; + $precisions[ $pair ] = $market[ 'precision' ]; + $limits[ $pair ] = $market[ 'limits' ]; + // We can't be sure whether we'll be the maker or the taker in each trade, so we'll assume the worst. + $tradeFees[ $pair ] = max( $market[ 'maker' ], $market[ 'taker' ] ); + $fees[ $tradeable ] = $currencies[ $tradeable ][ 'fee' ]; + $conf[ $tradeable ] = 0; // unknown! + } + + $this->pairs = $pairs; + $this->confirmationTimes = $conf; + $this->tradeFees = $tradeFees; + $this->precisions = $precisions; + $this->limits = $limits; + $this->withdrawFees = $fees; + $this->depositFees = $fees; + + $this->calculateTradeablePairs(); + + } + + public function getTickers( $currency ) { + + $currency = $this->coinNames[ $currency ]; + $markets = $this->exchange->loadMarkets(); + + $ticker = [ ]; + foreach ( $markets as $market ) { + if ( $market[ 'quote' ] != $currency ) { + continue; + } + + $ticker[] = $market[ 'symbol' ]; + } + + $tickers = $this->exchange->fetchTickers( $ticker ); + $ticker = [ ]; + foreach ( $tickers as $row ) { + $split = explode( '/', $row[ 'symbol' ] ); + + // The API doesn't provide the last price, so we just take an average into our spread. :-( + $ticker[ strtoupper( $split[ 0 ] ) ] = formatBTC( $row[ 'last' ] ); + } + + return $ticker; + + } + + public function withdraw( $coin, $amount, $address, $tag = null ) { + + $coin = $this->coinNames[ $coin ]; + if ( floatval( $amount ) === 0 ) { + // Simulate an error which ccxt may not raise for us + return false; + } + + return $this->checkAPIReturnValue( $this->exchange->withdraw( $coin, formatBTC( $amount ), $address, $tag ) ); + + } + + public function getDepositAddress( $coin ) { + + $coin = $this->coinNames[ $coin ]; + $currencies = $this->exchange->fetch_currencies(); + + if ( !key_exists( $coin, $currencies ) || !$currencies[ $coin ] ) { + logg( $this->prefix() . "Coin '$coin' is disabled by Exchange!", true ); + return null; + } + + $result = $this->exchange->fetch_deposit_address( $coin ); + if ( !isset( $result[ 'address' ] ) ) { + logg( $this->prefix() . "Please generate a deposit address for $coin!", true ); + return null; + } + if ( key_exists( 'tag', $result ) && !is_null( $result[ 'tag' ] ) ) { + return array( $result[ 'address' ], $result[ 'tag' ] ); + } + return $result[ 'address' ]; + + } + + public function buy( $tradeable, $currency, $rate, $amount ) { + try { + $precisions = $this->getPrecision( $tradeable, $currency ); + $limits = $this->getLimits( $tradeable, $currency ); + $fee = $this->tradeFees[ $tradeable . "_" . $currency ]; + if($limits[ 'amount' ][ 'min' ] > $amount){ + logg( $this->prefix() . "Amount for $tradeable less MIN in buy()!" ); + return null; + } + $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); + print( $this->prefix() . "INFO BUY [$tradeable]:\n" ); + print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . "\n" ); + print( $this->prefix() . "Amount = " . $amount . "\n" ); + print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee ); + $amount = round($amount + $amount_fee, $precisions[ 'amount' ]); + print( $this->prefix() . "Total = " . $amount . "\n" ); + + $result = $this->exchange->create_order( $tradeable . '/' . $currency, 'limit', 'buy', + $amount, $rate ); + return $currency . '_' . $tradeable . ':' . $result[ 'id' ]; + } + catch ( Exception $ex ) { + logg( $this->prefix() . "Got an exception in buy(): " . $ex->getMessage() ); + return null; + } + } + + public function sell( $tradeable, $currency, $rate, $amount ) { + try { + $precisions = $this->getPrecision( $tradeable, $currency ); + $limits = $this->getLimits( $tradeable, $currency ); + $fee = $this->tradeFees[ $tradeable . "_" . $currency ]; + if($limits[ 'amount' ][ 'min' ] > $amount){ + logg( $this->prefix() . "Amount for $tradeable less MIN in sell() [" . $limits[ 'amount' ][ 'min' ] . "]!" ); + return null; + } + $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); + print( $this->prefix() . "INFO SELL [$tradeable]:\n" ); + print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . "\n" ); + print( $this->prefix() . "Amount = " . $amount . "\n" ); + print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee ); + $amount = round($amount - $amount_fee, $precisions[ 'amount' ]); + print( $this->prefix() . "Total = " . $amount . "\n" ); + + $result = $this->exchange->create_order( $tradeable . '/' . $currency, 'limit', 'sell', + $amount, $rate ); + return $currency . '_' . $tradeable . ':' . $result[ 'id' ]; + } + catch ( Exception $ex ) { + logg( $this->prefix() . "Got an exception in sell(): " . $ex->getMessage() ); + return null; + } + } + + public function testAccess_() { + + $this->refreshExchangeData(); + //DCZPJac9cf6zBx8Cn8AyDsQy5pAmFbCkEz +// $markets = $this->exchange->loadMarkets(); +// var_dump($markets['DOGE/BTC']);die(); + var_dump($this->exchange->fetch_currencies()['DOGE']);die(); + var_dump($this->exchange->currencies);die(); +// var_dump($this->exchange->privatePostGetTransactions( [ 'Type' => 'Deposit' ] ));die(); +// var_dump($this->getDepositAddress('DGB'));die(); +// withdraw( $coin, $amount, $address, $tag = null ) { +// var_dump($this->withdraw('DGB', '1.5', 'DCZPJac9cf6zBx8Cn8AyDsQy5pAmFbCkEz'));die(); +// $rr = $this->exchange->getWithdrawablePairs(); +// $markets = $this->getTickers('BTC'); + +// var_dump($this->exchange->currencies['BLZ']); +// var_dump($this->withdrawFees['BLZ_BTC']); +// die(); + + } + +}; diff --git a/bot/xchange/Hitbtc.php b/bot/xchange/Hitbtc.php index 2268232..3c23c4b 100644 --- a/bot/xchange/Hitbtc.php +++ b/bot/xchange/Hitbtc.php @@ -28,6 +28,10 @@ public function isMarketActive( $market ) { $tradeable = $market[ 'base' ]; + if ( !$this->exchange->currencies[ $tradeable ][ 'payout' ] || !$this->exchange->currencies[ $tradeable ][ 'payin' ] ) { + logg( $this->prefix() . "Coin '$tradeable' is disabled by Exchange!", true ); + return false; + } if ( !$this->exchange->currencies[ $tradeable ][ 'payout' ] || !$this->exchange->currencies[ $tradeable ][ 'payin' ] ) { logg( $this->prefix() . "Coin '$tradeable' is disabled by Exchange!", true ); return false; @@ -142,11 +146,14 @@ public function getDepositAddress( $coin ) { logg( $this->prefix() . "Please generate a deposit address for $coin!", true ); return null; } + if ( !is_null( $result[ 'tag' ] ) ) { + return array( $result[ 'address' ], $result[ 'tag' ] ); + } return $result[ 'address' ]; } - public function withdraw( $coin, $amount, $address ) { + public function withdraw( $coin, $amount, $address, $tag = null ) { $coin = $this->coinNames[ $coin ]; if ( !$this->exchange->currencies[ $coin ][ 'payout' ] ) { @@ -166,7 +173,7 @@ public function withdraw( $coin, $amount, $address ) { 'amount' => floatval ($amount), 'type' => 'exchangeToBank', )); - sleep(2); + sleep(1); return $this->checkAPIReturnValue( $this->exchange->withdraw( $coin, $amount, $address, null, [ 'includeFee' => 'true' ] ) ); @@ -188,11 +195,18 @@ public function buy( $tradeable, $currency, $rate, $amount ) { try { $precisions = $this->getPrecision( $tradeable, $currency ); $limits = $this->getLimits( $tradeable, $currency ); - $amount = round($amount, $precisions[ 'amount' ]); + $fee = $this->tradeFees[ $tradeable . "_" . $currency ]; if($limits[ 'amount' ][ 'min' ] > $amount){ logg( $this->prefix() . "Amount for $tradeable less MIN in buy()!" ); return null; } + $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); + print( $this->prefix() . "INFO BUY [$tradeable]:\n" ); + print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . "\n" ); + print( $this->prefix() . "Amount = " . $amount . "\n" ); + print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee ); + $amount = round($amount + $amount_fee, $precisions[ 'amount' ]); + print( $this->prefix() . "Total = " . $amount . "\n" ); $result = $this->exchange->create_order( $this->common_currency_code ($tradeable) . '/' . $this->common_currency_code ($currency), 'limit', 'buy', $amount, $rate ); @@ -208,11 +222,18 @@ public function sell( $tradeable, $currency, $rate, $amount ) { try { $precisions = $this->getPrecision( $tradeable, $currency ); $limits = $this->getLimits( $tradeable, $currency ); - $amount = round($amount, $precisions[ 'amount' ]); + $fee = $this->tradeFees[ $tradeable . "_" . $currency ]; if($limits[ 'amount' ][ 'min' ] > $amount){ - logg( $this->prefix() . "Amount for $tradeable less MIN in sell()!" ); + logg( $this->prefix() . "Amount for $tradeable less MIN in sell() [" . $limits[ 'amount' ][ 'min' ] . "]!" ); return null; } + $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); + print( $this->prefix() . "INFO SELL [$tradeable]:\n" ); + print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . "\n" ); + print( $this->prefix() . "Amount = " . $amount . "\n" ); + print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee ); + $amount = round($amount - $amount_fee, $precisions[ 'amount' ]); + print( $this->prefix() . "Total = " . $amount . "\n" ); $result = $this->exchange->create_order( $this->common_currency_code ($tradeable) . '/' . $this->common_currency_code ($currency), 'limit', 'sell', $amount, $rate ); diff --git a/bot/xchange/map.6 b/bot/xchange/map.6 new file mode 100644 index 0000000..65132e7 --- /dev/null +++ b/bot/xchange/map.6 @@ -0,0 +1 @@ +Cryptopia \ No newline at end of file diff --git a/web/ui.js b/web/ui.js index e9c7ab7..b48d3a0 100644 --- a/web/ui.js +++ b/web/ui.js @@ -113,6 +113,8 @@ $(function() { return "BTTRX"; else if (x === "5" || x === 5) return "BTFNX"; + else if (x === "6" || x === 6) + return "CRPTP"; else if (x === "7" || x === 7) return "HTBTC"; else if (x === "9" || x === 9) @@ -145,6 +147,8 @@ $(function() { return "Bittrex"; else if (x === "5" || x === 5) return "Bitfinex"; + else if (x === "6" || x === 6) + return "Cryptopia"; else if (x === "7" || x === 7) return "HitBTC"; else if (x === "9" || x === 9) diff --git a/web/xchange/6.ico b/web/xchange/6.ico new file mode 100755 index 0000000000000000000000000000000000000000..21600067090913b381c6cdbe8533e4d04bb9a7a7 GIT binary patch literal 15086 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 literal 0 HcmV?d00001 From 5ebde4b64f134bf53c5ef3fcf2f2bb05f3b43cb7 Mon Sep 17 00:00:00 2001 From: OlegVic <32229154+OlegVic@users.noreply.github.com> Date: Fri, 9 Feb 2018 13:56:01 +0200 Subject: [PATCH 06/10] getWithdrawalHistory correct --- bot/xchange/Cryptopia.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bot/xchange/Cryptopia.php b/bot/xchange/Cryptopia.php index 3482d3f..ac81c97 100644 --- a/bot/xchange/Cryptopia.php +++ b/bot/xchange/Cryptopia.php @@ -63,7 +63,7 @@ public function getWithdrawalHistory() { $history = [ ]; - $result = $this->exchange->privatePostGetTransactions( [ 'Type' => 'Deposit' ] ); + $result = $this->exchange->privatePostGetTransactions( [ 'Type' => 'Whitdraw' ] ); if($result[ 'Success' ]) { $history = $result[ 'Data' ]; From 46561086cb1d327abccedb21daa871314fe1baf0 Mon Sep 17 00:00:00 2001 From: OlegVic <32229154+OlegVic@users.noreply.github.com> Date: Fri, 9 Feb 2018 14:05:12 +0200 Subject: [PATCH 07/10] ########## --- bot/xchange/Cryptopia.php | 21 --------------------- 1 file changed, 21 deletions(-) diff --git a/bot/xchange/Cryptopia.php b/bot/xchange/Cryptopia.php index ac81c97..3b0729d 100644 --- a/bot/xchange/Cryptopia.php +++ b/bot/xchange/Cryptopia.php @@ -258,25 +258,4 @@ public function sell( $tradeable, $currency, $rate, $amount ) { } } - public function testAccess_() { - - $this->refreshExchangeData(); - //DCZPJac9cf6zBx8Cn8AyDsQy5pAmFbCkEz -// $markets = $this->exchange->loadMarkets(); -// var_dump($markets['DOGE/BTC']);die(); - var_dump($this->exchange->fetch_currencies()['DOGE']);die(); - var_dump($this->exchange->currencies);die(); -// var_dump($this->exchange->privatePostGetTransactions( [ 'Type' => 'Deposit' ] ));die(); -// var_dump($this->getDepositAddress('DGB'));die(); -// withdraw( $coin, $amount, $address, $tag = null ) { -// var_dump($this->withdraw('DGB', '1.5', 'DCZPJac9cf6zBx8Cn8AyDsQy5pAmFbCkEz'));die(); -// $rr = $this->exchange->getWithdrawablePairs(); -// $markets = $this->getTickers('BTC'); - -// var_dump($this->exchange->currencies['BLZ']); -// var_dump($this->withdrawFees['BLZ_BTC']); -// die(); - - } - }; From 4e803b4041a58bcfb55574834f6e226c1bec0c23 Mon Sep 17 00:00:00 2001 From: OlegVic Date: Fri, 9 Feb 2018 23:53:06 +0000 Subject: [PATCH 08/10] Update --- bot/CCXTAdapter.php | 6 +-- bot/xchange/Cryptopia.php | 32 ++++++++---- bot/xchange/Hitbtc.php | 107 +++++++++++++++++++++++++++++++------- 3 files changed, 112 insertions(+), 33 deletions(-) diff --git a/bot/CCXTAdapter.php b/bot/CCXTAdapter.php index ef552c2..5d6a63a 100644 --- a/bot/CCXTAdapter.php +++ b/bot/CCXTAdapter.php @@ -36,9 +36,9 @@ abstract class CCXTAdapter extends Exchange { private $name = ''; protected $coinNames = [ ]; - private $tradeFees = [ ]; - private $precisions = [ ]; - private $limits = [ ]; + protected $tradeFees = [ ]; + protected $precisions = [ ]; + protected $limits = [ ]; private $lastStuckReportTime = [ ]; private $lastDuplicateWithdrawalTime = 0; diff --git a/bot/xchange/Cryptopia.php b/bot/xchange/Cryptopia.php index 3b0729d..e5c321f 100644 --- a/bot/xchange/Cryptopia.php +++ b/bot/xchange/Cryptopia.php @@ -25,7 +25,7 @@ public function getRateLimit() { } public function isMarketActive( $market ) { - return $market[ 'active' ]; + return $market[ 'active' ] && $market[ 'info' ][ 'Status' ] == 'OK'; } public function checkAPIReturnValue( $result ) { @@ -187,7 +187,7 @@ public function getDepositAddress( $coin ) { $coin = $this->coinNames[ $coin ]; $currencies = $this->exchange->fetch_currencies(); - if ( !key_exists( $coin, $currencies ) || !$currencies[ $coin ] ) { + if ( !key_exists( $coin, $currencies ) || !$currencies[ $coin ][ 'active' ] ) { logg( $this->prefix() . "Coin '$coin' is disabled by Exchange!", true ); return null; } @@ -213,12 +213,14 @@ public function buy( $tradeable, $currency, $rate, $amount ) { logg( $this->prefix() . "Amount for $tradeable less MIN in buy()!" ); return null; } - $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); +// $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); + $amount_fee = $amount * $fee; print( $this->prefix() . "INFO BUY [$tradeable]:\n" ); - print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . "\n" ); + print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . " Precisions = " . $precisions[ 'amount' ] . "\n" ); print( $this->prefix() . "Amount = " . $amount . "\n" ); - print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee ); - $amount = round($amount + $amount_fee, $precisions[ 'amount' ]); + print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee . "\n" ); +// $amount = round($amount + $amount_fee, $precisions[ 'amount' ]); + $amount = $amount + $amount_fee; print( $this->prefix() . "Total = " . $amount . "\n" ); $result = $this->exchange->create_order( $tradeable . '/' . $currency, 'limit', 'buy', @@ -240,12 +242,14 @@ public function sell( $tradeable, $currency, $rate, $amount ) { logg( $this->prefix() . "Amount for $tradeable less MIN in sell() [" . $limits[ 'amount' ][ 'min' ] . "]!" ); return null; } - $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); +// $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); + $amount_fee = $amount * $fee; print( $this->prefix() . "INFO SELL [$tradeable]:\n" ); - print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . "\n" ); + print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . " Precisions = " . $precisions[ 'amount' ] . "\n" ); print( $this->prefix() . "Amount = " . $amount . "\n" ); - print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee ); - $amount = round($amount - $amount_fee, $precisions[ 'amount' ]); + print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee . "\n" ); +// $amount = round($amount - $amount_fee, $precisions[ 'amount' ]); + $amount = $amount - $amount_fee; print( $this->prefix() . "Total = " . $amount . "\n" ); $result = $this->exchange->create_order( $tradeable . '/' . $currency, 'limit', 'sell', @@ -258,4 +262,12 @@ public function sell( $tradeable, $currency, $rate, $amount ) { } } + public function testAccess_() { + $this->refreshExchangeData(); +// $markets = $this->exchange->loadMarkets(); +// var_dump($this->getDepositAddress( 'OMG' ));die(); + var_dump($this->exchange->fetch_currencies()['OMG']);die(); +// var_dump($markets['REP/BTC']);die(); + } + }; diff --git a/bot/xchange/Hitbtc.php b/bot/xchange/Hitbtc.php index 3c23c4b..aea783c 100644 --- a/bot/xchange/Hitbtc.php +++ b/bot/xchange/Hitbtc.php @@ -200,12 +200,14 @@ public function buy( $tradeable, $currency, $rate, $amount ) { logg( $this->prefix() . "Amount for $tradeable less MIN in buy()!" ); return null; } - $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); +// $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); + $amount_fee = $amount * $fee; print( $this->prefix() . "INFO BUY [$tradeable]:\n" ); - print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . "\n" ); + print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . " Precisions = " . $precisions[ 'amount' ] . "\n" ); print( $this->prefix() . "Amount = " . $amount . "\n" ); - print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee ); - $amount = round($amount + $amount_fee, $precisions[ 'amount' ]); + print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee . "\n" ); +// $amount = round($amount + $amount_fee, $precisions[ 'amount' ]); + $amount = $amount + $amount_fee; print( $this->prefix() . "Total = " . $amount . "\n" ); $result = $this->exchange->create_order( $this->common_currency_code ($tradeable) . '/' . $this->common_currency_code ($currency), 'limit', 'buy', @@ -227,12 +229,14 @@ public function sell( $tradeable, $currency, $rate, $amount ) { logg( $this->prefix() . "Amount for $tradeable less MIN in sell() [" . $limits[ 'amount' ][ 'min' ] . "]!" ); return null; } - $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); +// $amount_fee = round($amount * $fee, $precisions[ 'amount' ]); + $amount_fee = $amount * $fee; print( $this->prefix() . "INFO SELL [$tradeable]:\n" ); - print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . "\n" ); + print( $this->prefix() . "Limits = " . $limits[ 'amount' ][ 'min' ] . " Precisions = " . $precisions[ 'amount' ] . "\n" ); print( $this->prefix() . "Amount = " . $amount . "\n" ); - print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee ); - $amount = round($amount - $amount_fee, $precisions[ 'amount' ]); + print( $this->prefix() . "Fee = " . $fee . " => " . $amount_fee . "\n" ); +// $amount = round($amount - $amount_fee, $precisions[ 'amount' ]); + $amount = $amount - $amount_fee; print( $this->prefix() . "Total = " . $amount . "\n" ); $result = $this->exchange->create_order( $this->common_currency_code ($tradeable) . '/' . $this->common_currency_code ($currency), 'limit', 'sell', @@ -291,21 +295,14 @@ private function queryBalances() { return $this->exchange->fetch_balance()[ 'free' ]; } - public function refreshWallets( $inBetweenTrades = false ) { + public function refreshWallets( $tradesMade = array() ) { $this->exchangeBalanceToTrade(); - if ( !$inBetweenTrades ) { - $this->preRefreshWallets(); - } + $this->preRefreshWallets(); $wallets = [ ]; - $currencies = $this->exchange->currencies; - foreach ($currencies as $coin => $row){ - $wallets[ strtoupper( $coin ) ] = 0; - } - $arr = $this->queryBalances(); foreach ( $arr as $coin => $balance ) { $wallets[ strtoupper( $coin ) ] = $balance; @@ -313,9 +310,71 @@ public function refreshWallets( $inBetweenTrades = false ) { $this->wallets = $wallets; - if ( !$inBetweenTrades ) { - $this->postRefreshWallets(); + $this->postRefreshWallets( $tradesMade ); + + } + +// public function refreshWallets( $inBetweenTrades = false ) { +// +// $this->exchangeBalanceToTrade(); +// +//// if ( !$inBetweenTrades ) { +//// $this->preRefreshWallets(); +//// } +// +// $wallets = [ ]; +// +// $currencies = $this->exchange->currencies; +// foreach ($currencies as $coin => $row){ +// $wallets[ strtoupper( $coin ) ] = 0; +// } +// +// $arr = $this->queryBalances(); +// foreach ( $arr as $coin => $balance ) { +// $wallets[ strtoupper( $coin ) ] = $balance; +// } +// +// $this->wallets = $wallets; +// +//// if ( !$inBetweenTrades ) { +//// $this->postRefreshWallets(); +//// } +// +// } + + protected function fetchOrderbook( $tradeable, $currency ) { + + $tradeableInternal = $this->coinNames[ $tradeable ]; + $currencyInternal = $this->coinNames[ $currency ]; + $orderbook = $this->exchange->fetch_order_book( $tradeableInternal . '/' . $currencyInternal ); + if ( count( $orderbook ) == 0 ) { + return null; + } + + if ( !key_exists( 'asks', $orderbook ) ) { + return null; + } + $asks = $orderbook[ 'asks' ]; + if ( count( $asks ) == 0 ) { + return null; + } + $bestAsk = $asks[ 0 ]; + + if ( !key_exists( 'bids', $orderbook ) ) { + return null; + } + $bids = $orderbook[ "bids" ]; + if ( count( $bids ) == 0 ) { + return null; } + $bestBid = $bids[ 0 ]; + + return new Orderbook( // + $this, $tradeable, // + $currency, // + new OrderbookEntry( $bestAsk[ 1 ], $bestAsk[ 0 ] ), // + new OrderbookEntry( $bestBid[ 1 ], $bestBid[ 0 ] ) // + ); } @@ -338,7 +397,7 @@ public function refreshExchangeData() { continue; } - if ( !$this->isMarketActive( $market ) ) { //Oleg + if ( !$this->isMarketActive( $market ) ) { continue; } @@ -366,4 +425,12 @@ public function refreshExchangeData() { } + public function testAccess_() { + $this->refreshExchangeData(); +// $markets = $this->exchange->loadMarkets(); + + var_dump($this->coinNames['KMD']);die(); +// var_dump($this->isMarketActive($markets['KMD/BTC']));die(); + } + }; From 70f60a52ca44649f6573c79bdb772e232cbe93dd Mon Sep 17 00:00:00 2001 From: OlegVic Date: Sat, 10 Feb 2018 07:41:12 +0000 Subject: [PATCH 09/10] Changes --- bot/CoinManager.php | 10 ---------- 1 file changed, 10 deletions(-) diff --git a/bot/CoinManager.php b/bot/CoinManager.php index 9e8d7ea..7f79fc5 100644 --- a/bot/CoinManager.php +++ b/bot/CoinManager.php @@ -1,15 +1,5 @@ Date: Sat, 10 Feb 2018 09:46:34 +0200 Subject: [PATCH 10/10] Delete CoinManager.php --- bot/CoinManager.php | 958 -------------------------------------------- 1 file changed, 958 deletions(-) delete mode 100644 bot/CoinManager.php diff --git a/bot/CoinManager.php b/bot/CoinManager.php deleted file mode 100644 index 7f79fc5..0000000 --- a/bot/CoinManager.php +++ /dev/null @@ -1,958 +0,0 @@ -exchanges = &$exchanges; - - foreach ( $exchanges as $exchange ) { - $this->exchangesID[ $exchange->getID() ] = $exchange; - } - - } - - public function doManage( &$arbitrator ) { - - logg( "doManage()" ); - $this->stats = Database::getStats(); - - $this->checkKeys(); - - $stats = &$this->stats; - $hadAction = true; - - try { - // - if ( $stats[ self::STAT_NEXT_MANAGEMENT ] <= time() ) { - // - self::manageWallets( $arbitrator ); - $stats[ self::STAT_NEXT_MANAGEMENT ] = time() + Config::get( Config::INTERVAL_MANAGEMENT, Config::DEFAULT_INTERVAL_MANAGEMENT ) * 1800; - // - } - else if ( $stats[ self::STAT_NEXT_TAKE_PROFIT ] <= time() ) { - // - self::takeProfit(); - $stats[ self::STAT_NEXT_TAKE_PROFIT ] = time() + Config::get( Config::INTERVAL_TAKE_PROFIT, Config::DEFAULT_INTERVAL_TAKE_PROFIT ) * 3600; - // - } - else if ( $stats[ self::STAT_NEXT_STUCK_DETECTION ] <= time() ) { - // - self::stuckDetection(); - $stats[ self::STAT_NEXT_STUCK_DETECTION ] = time() + Config::get( Config::INTERVAL_STUCK_DETECTION, Config::DEFAULT_INTERVAL_STUCK_DETECTION ) * 3600; - // - } - else if ( $stats[ self::STAT_NEXT_DUPLICATE_DETECTION ] <= time() ) { - // - self::duplicateDetection(); - $stats[ self::STAT_NEXT_DUPLICATE_DETECTION ] = time() + Config::get( Config::INTERVAL_DUPLICATE_DETECTION, Config::DEFAULT_INTERVAL_DUPLICATE_DETECTION ) * 3600; - // - } - else if ( $stats[ self::STAT_NEXT_UNUSED_COIN_DETECTION ] <= time() ) { - // - self::unusedCoinsDetection(); - $stats[ self::STAT_NEXT_UNUSED_COIN_DETECTION ] = time() + Config::get( Config::INTERVAL_UNUSED_COIN_DETECTION, Config::DEFAULT_INTERVAL_UNUSED_COIN_DETECTION ) * 3600; - // - } - else if ( $stats[ self::STAT_NEXT_DB_CLEANUP ] <= time() ) { - // - self::dbCleanup(); - $stats[ self::STAT_NEXT_DB_CLEANUP ] = time() + Config::get( Config::INTERVAL_DB_CLEANUP, Config::DEFAULT_INTERVAL_DB_CLEANUP ) * 3600; - // - } - else { - $hadAction = false; - } - // - } - catch ( Exception $ex ) { - logg( "ERROR during management task: " . $ex->getMessage() . "\n" . $ex->getTraceAsString() ); - } - - Database::saveStats( $this->stats ); - - return $hadAction; - - } - - private function checkKeys() { - - $stats = &$this->stats; - - // load sensible defaults for stats... - if ( !key_exists( self::STAT_NEXT_MANAGEMENT, $stats ) ) { - $stats[ self::STAT_NEXT_MANAGEMENT ] = 0; - } - if ( !key_exists( self::STAT_NEXT_TAKE_PROFIT, $stats ) ) { - $stats[ self::STAT_NEXT_TAKE_PROFIT ] = time() + 48 * 3600; - } - if ( !key_exists( self::STAT_NEXT_STUCK_DETECTION, $stats ) ) { - $stats[ self::STAT_NEXT_STUCK_DETECTION ] = time() + 24 * 3600; - } - if ( !key_exists( self::STAT_NEXT_DUPLICATE_DETECTION, $stats ) ) { - $stats[ self::STAT_NEXT_DUPLICATE_DETECTION ] = time() + 24 * 3600; - } - if ( !key_exists( self::STAT_NEXT_UNUSED_COIN_DETECTION, $stats ) ) { - $stats[ self::STAT_NEXT_UNUSED_COIN_DETECTION ] = time() + 24 * 3600; - } - if ( !key_exists( self::STAT_NEXT_DB_CLEANUP, $stats ) ) { - $stats[ self::STAT_NEXT_DB_CLEANUP ] = time() + 7 * 24 * 3600; - } - if ( !key_exists( self::STAT_NEXT_CURRENCY_AGGRESSIVE_BALANCE_ALLOWED, $stats ) ) { - $stats[ self::STAT_NEXT_CURRENCY_AGGRESSIVE_BALANCE_ALLOWED ] = 0; - } - if ( !key_exists( self::STAT_BOT_AGE, $stats ) ) { - $stats[ self::STAT_BOT_AGE ] = time(); - } - if ( !key_exists( self::STAT_AUTOBUY_FUNDS, $stats ) ) { - $stats[ self::STAT_AUTOBUY_FUNDS ] = "0"; - } - - } - - private function dbCleanup() { - - logg( "dbCleanup()" ); - $rows = Database::cleanup(); - - logg( "Deleted $rows records" ); - - } - - private function saveSnapshot() { - - logg( "saveSnapshot()" ); - - $time = time(); - $balances = array( ); - foreach ( $this->exchanges as $exchange ) { - - $exid = $exchange->getID(); - $exname = $exchange->getName(); - - $wallets = $exchange->getWalletsConsideringPendingDeposits(); - $ticker = $exchange->getTickers( 'BTC' ); - - foreach ( $wallets as $coin => $value ) { - - $balance = formatBTC( $value ); - if ( isset( $balances[ $coin ] ) ) { - $balances[ $coin ] += $value; - } else { - $balances[ $coin ] = $value; - } - - if ( Config::isCurrency( $coin ) ) { - Database::saveSnapshot( $coin, $balance, $balance, 0, $exid, $time ); - continue; - } - - if ( !key_exists( $coin, $ticker ) ) { - logg( "$exname | Skipping $coin as it isn't traded against BTC" ); - if ( $value > 0 ) { - logg( "You have $balance $coin at $exname which cannot be traded against BTC!" ); - } - continue; - } - - $rate = formatBTC( $ticker[ $coin ] ); - - // Calculate desired balance: - $desiredBalance = 0; - $averageRate = 0; - $uses = Database::getOpportunityCount( $coin, 'BTC', $exid ); - // Desired balance can only raise above zero if the coin is required at this exchange - if ( $uses >= Config::get( Config::REQUIRED_OPPORTUNITIES, Config::DEFAULT_REQUIRED_OPPORTUNITIES ) ) { - - // Retrieve average exchange rates for this coin: - $averageRate = Database::getAverageRate( $coin ); - - $depositFee = abs( $exchange->getDepositFee( $coin, 1 ) * $averageRate ); - $withdrawFee = abs( $exchange->getWithdrawFee( $coin, 1 ) * $averageRate ); - $confTime = $exchange->getConfirmationTime( $coin ); - - if ($depositFee < Config::get( Config::MAX_TX_FEE_ALLOWED, Config::DEFAULT_MAX_TX_FEE_ALLOWED ) && - $withdrawFee < Config::get( Config::MAX_TX_FEE_ALLOWED, Config::DEFAULT_MAX_TX_FEE_ALLOWED ) && - $confTime < Config::get( Config::MAX_MIN_CONFIRMATIONS_ALLOWED, Config::DEFAULT_MAX_MIN_CONFIRMATIONS_ALLOWED ) ) { - $maxTradeSize = Config::get( Config::MAX_TRADE_SIZE, Config::DEFAULT_MAX_TRADE_SIZE ); - $balanceFactor = Config::get( Config::BALANCE_FACTOR, Config::DEFAULT_BALANCE_FACTOR ); - - $desiredBalance = formatBTC( $maxTradeSize / $averageRate * $balanceFactor ); - $diff = abs( $desiredBalance - $balance ); - // Only allow a diff if the need is fulfillable: - if ( $diff * $averageRate < $exchange->getSmallestOrderSize( $coin, 'BTC', 'buy' ) && - $diff * $averageRate < $exchange->getSmallestOrderSize( $coin, 'BTC', 'sell' ) ) { - $desiredBalance = $balance; - } - } - } - - logg( "$exname | $coin | AVAILABLE: $balance | RATE: $rate | AVG.RATE: $averageRate | USES: $uses | TARGET: $desiredBalance" ); - Database::saveSnapshot( $coin, $balance, $desiredBalance, $rate, $exid, $time ); - } - } - - $link = Database::connect(); - - foreach ( $balances as $coin => $balance ) { - Database::saveBalance( $coin, $balance, '0', $time, $link ); - } - - mysql_close( $link ); - - } - - private function balanceCurrencies() { - logg( "balanceCurrencies()" ); - $this->balance( 'BTC', true ); - - } - - private function balance( $coin, $skipUsageCheck = false, $safetyFactorArg = 0.01 ) { - - logg( "balance($coin)" ); - if ( Config::isBlocked( $coin ) ) { - logg( "Skipping: $coin is blocked!" ); - return; - } - - $exchanges = [ ]; - $exchangeWallets = [ ]; - foreach ( $this->exchanges as $exchange ) { - $wallets = $exchangeWallets[ $exchange->getName() ] = - $exchange->getWalletsConsideringPendingDeposits(); - if ( key_exists( $coin, $wallets ) ) { - $exchanges[] = $exchange; - } - } - - $requiredOpportunities = Config::get( Config::REQUIRED_OPPORTUNITIES, Config::DEFAULT_REQUIRED_OPPORTUNITIES ); - if ( $skipUsageCheck ) { - $requiredOpportunities = 0; - } - - $kickedExchanges = 0; - $totalCoins = 0; - foreach ( $exchanges as $exchange ) { - - $wallets = $exchangeWallets[ $exchange->getName() ]; - $balance = $wallets[ $coin ]; - $opportunityCount = Database::getOpportunityCount( $coin, 'BTC', $exchange->getID() ); - - logg( str_pad( $exchange->getName(), 10, ' ', STR_PAD_LEFT ) . ": $balance $coin ($opportunityCount usages)" ); - - $totalCoins += $balance; - - if ( $opportunityCount < $requiredOpportunities ) { - $kickedExchanges++; - continue; - } - } - - $count = count( $exchanges ) - $kickedExchanges; - - if ( $count == 0 ) { - logg( "Not balancing as this coin is unused!" ); - return; - } - - logg( " TOTAL: $totalCoins $coin" ); - $averageCoins = formatBTC( $totalCoins / $count ); - logg( " AVERAGE: $averageCoins $coin" ); - - $positiveExchanges = [ ]; - $negativeExchanges = [ ]; - - $minXFER = 0; - $safetyFactor = $safetyFactorArg; - if ( $coin == 'BTC' ) { - $minXFER = Config::get( Config::MIN_BTC_XFER, Config::DEFAULT_MIN_BTC_XFER ); - // Be a bit more conservative with BTC, since it's our profits after all! - $safetyFactor /= Config::get( Config::BTC_XFER_SAFETY_FACTOR, - Config::DEFAULT_BTC_XFER_SAFETY_FACTOR ); - } - $oneIsZero = false; - $oneIsNearZero = false; // Only used for BTC - $nearZeroThreshold = Config::get( Config::NEAR_ZERO_BTC_VALUE, Config::DEFAULT_NEAR_ZERO_BTC_VALUE ); - foreach ( $exchanges as $exchange ) { - // Allow max 1% of coin amount to be transfer fee: - $minXFER = max( $minXFER, $this->getSafeWithdrawFee( $exchange, $coin, $averageCoins ) / $safetyFactor ); - - $wallets = $exchange->getWallets(); - if ( $wallets[ $coin ] == 0 ) { - $oneIsZero = true; - } - if ( $coin == 'BTC' && $wallets[ $coin ] <= $nearZeroThreshold ) { - $oneIsNearZero = true; - } - } - logg( "XFER THRES.: $minXFER $coin" ); - - foreach ( $exchanges as $exchange ) { - - $opportunityCount = Database::getOpportunityCount( $coin, 'BTC', $exchange->getID() ); - - $wallets = $exchange->getWallets(); - $difference = formatBTC( $wallets[ $coin ] - ($opportunityCount < $requiredOpportunities ? 0 : $averageCoins) ); - - if ( $oneIsZero && $wallets[ $coin ] > 2 * $minXFER && - $difference == 0 ) { - // If the other exchange has run out of balance, send a minimum transfer to - // the other side to unblock it. - $difference = $minXFER; - } - - if ( abs( $difference ) < $minXFER ) { - logg( $exchange->getName() . " diff $difference $coin below xfer threshold!" ); - continue; - } - - if ( $difference < 0 ) { - logg( $exchange->getName() . " is missing $difference $coin" ); - $negativeExchanges[] = ['e' => $exchange, 'd' => abs( $difference ) ]; - } - else if ( $difference > 0 ) { - logg( $exchange->getName() . " could give $difference $coin" ); - $positiveExchanges[] = ['e' => $exchange, 'd' => $difference ]; - } - } - - if ( count( $positiveExchanges ) == 0 || count( $negativeExchanges ) == 0 ) { - if ( $oneIsNearZero && count( $exchanges ) > 2 ) { - // While balancing currencies with more than 2 exchanges, if we get to a situation - // where one of our exchanges is running out of its balance but we have been unable - // to perform a rebalancing, if we just give up we may end up stuck in this local - // minima for quite a while. So to avoid this, we try to be less conservative and - // relax our safety factor a bit to see if we'll manage to rebalance that way. - if ( $safetyFactorArg <= 0.09 && // Don't lose more than 10% of our balance in transfers! - // Throttle how often this feature kicks in. - time() >= $this->stats[ self::STAT_NEXT_CURRENCY_AGGRESSIVE_BALANCE_ALLOWED ] ) { - $this->stats[ self::STAT_NEXT_CURRENCY_AGGRESSIVE_BALANCE_ALLOWED ] = time() + - Config::get( Config::INTERVAL_CURRENCY_AGGRESSIVE_BALANCE, - Config::DEFAULT_INTERVAL_CURRENCY_AGGRESSIVE_BALANCE ) * 1800; - Database::saveStats( $this->stats ); - - logg( sprintf( "Failed to rebalance with a safety factor of %d%%, trying %d%% now...", - floor( $safetyFactorArg * 100 ), - floor( ( $safetyFactorArg + 0.01 ) * 100 ) ) ); - return $this->balance( $coin, $skipUsageCheck, $safetyFactorArg + 0.01 ); - } - } - logg( "No exchange in need or available to give" ); - return; - } - - while ( 'A' != 'B' ) { - - $from = -1; - $to = -1; - - for ( $i = 0; $i < count( $negativeExchanges ); $i++ ) { - $missing = $negativeExchanges[ $i ][ 'd' ]; - if ( $missing < $minXFER ) { - continue; - } - $to = $i; - - for ( $j = 0; $j < count( $positiveExchanges ); $j++ ) { - $offering = $positiveExchanges[ $j ][ 'd' ]; - if ( $offering < $minXFER ) { - continue; - } - $from = $j; - - break; - } - - break; - } - - if ( $from < 0 || $to < 0 ) { - break; - } - - $amount = min( $positiveExchanges[ $from ][ 'd' ], $negativeExchanges[ $to ][ 'd' ] ); - $source = $positiveExchanges[ $from ][ 'e' ]; - $target = $negativeExchanges[ $to ][ 'e' ]; - - $this->withdraw( $source, $target, $coin, $amount ); - - $positiveExchanges[ $from ][ 'd' ] -= $amount; - $negativeExchanges[ $to ][ 'd' ] -= $amount; - - // - } - - } - - private function stuckDetection() { - - if ( !Config::get( Config::MODULE_STUCK_DETECTION, Config::DEFAULT_MODULE_STUCK_DETECTION ) ) { - return; - } - - logg( "stuckDetection()" ); - foreach ( $this->exchanges as $exchange ) { - $exchange->detectStuckTransfers(); - } - - } - - private function duplicateDetection() { - - if ( !Config::get( Config::MODULE_DUPLICATE_DETECTION, Config::DEFAULT_MODULE_DUPLICATE_DETECTION ) ) { - return; - } - - logg( "duplicateDetection()" ); - foreach ( $this->exchanges as $exchange ) { - $exchange->detectDuplicateWithdrawals(); - } - - } - - private $unusedCoins = [ ]; - - private function unusedCoinsDetection() { - - if ( !Config::get( Config::MODULE_UNUSED_COINS_DETECTION, Config::DEFAULT_MODULE_UNUSED_COINS_DETECTION ) ) { - return; - } - - logg( "unusedCoinsDetection()" ); - foreach ( $this->exchanges as $exchange ) { - - $xid = $exchange->getID(); - $ticker = $exchange->getTickers( 'BTC' ); - $wallets = $exchange->getWalletsConsideringPendingDeposits(); - - foreach ( $wallets as $coin => $balance ) { - - if ( !key_exists( $xid, $this->unusedCoins ) || !key_exists( $coin, $this->unusedCoins[ $xid ] ) ) { - logg( "[DEBUG] $xid $coin => init, set 0" ); - $this->unusedCoins[ $xid ][ $coin ] = 0; - } - - if ( $balance == 0 ) { - logg( "[DEBUG] $xid $coin => zero balance, set 0" ); - $this->unusedCoins[ $xid ][ $coin ] = 0; - continue; - } - - if ( Config::isCurrency( $coin ) ) { - logg( "[DEBUG] $xid $coin => is currency, set 0" ); - $this->unusedCoins[ $xid ][ $coin ] = 0; - continue; - } - - if ( key_exists( $coin, $ticker ) && !Config::isBlocked( $coin ) ) { - logg( "[DEBUG] $xid $coin => not blocked and traded, set 0" ); - $this->unusedCoins[ $xid ][ $coin ] = 0; - continue; - } - - if ( $this->unusedCoins[ $xid ][ $coin ] == 0 ) { - logg( "Queueing $coin @ " . $exchange->getName() . " for liquidation notification..." ); - $this->unusedCoins[ $xid ][ $coin ] = time(); - continue; - } - - if ( time() > $this->unusedCoins[ $xid ][ $coin ] + 3600 * 24 ) { - $this->unusedCoins[ $xid ][ $coin ] = 0; - logg( "Unused $balance $coin @ " . $exchange->getName() . ". It is safe to liquidate this coin!", true ); - } - } - } - - } - - private function takeProfit() { - - if ( !Config::get( Config::MODULE_TAKE_PROFIT, Config::DEFAULT_MODULE_TAKE_PROFIT ) ) { - return; - } - - logg( "takeProfit()" ); - - $profitAddress = Config::get( Config::TAKE_PROFIT_ADDRESS, Config::DEFAULT_TAKE_PROFIT_ADDRESS ); - $profitLimit = Config::get( Config::TAKE_PROFIT_AMOUNT, Config::DEFAULT_TAKE_PROFIT_AMOUNT ); - - if ( is_null( $profitAddress ) || is_null( $profitLimit ) || strlen( $profitAddress ) < 34 ) { - logg( "Skipping: Configuration is incomplete/invalid" ); - return; - } - - $totalBTC = 0; - - $highestExchange = null; - $highestExchangeAmount = 0; - - foreach ( $this->exchanges as $exchange ) { - $balance = $exchange->getWallets()[ 'BTC' ]; - - if ( $balance > $highestExchangeAmount ) { - $highestExchangeAmount = $balance; - $highestExchange = $exchange; - } - - $totalBTC += $balance; - } - - $averageBTC = formatBTC( $totalBTC / count( $this->exchanges ) ); - - $profit = formatBTC( $totalBTC - $profitLimit ); - logg( "Profit: " . $profit ); - $restockCash = formatBTC( min( Config::get( Config::TAKE_PROFIT_MIN_RESTOCK_CASH, - Config::DEFAULT_TAKE_PROFIT_MIN_RESTOCK_CASH ), - $profit * Config::get( Config::TAKE_PROFIT_RESTOCK_CASH_PERCENTAGE, - Config::DEFAULT_TAKE_PROFIT_RESTOCK_CASH_PERCENTAGE ) ) ); - - $minXFER = Config::get( Config::MIN_BTC_XFER, Config::DEFAULT_MIN_BTC_XFER ); - // Be a bit more conservative with BTC, since it's our profits after all! - $safetyFactor = 0.01 / Config::get( Config::BTC_XFER_SAFETY_FACTOR, - Config::DEFAULT_BTC_XFER_SAFETY_FACTOR ); - foreach ( $this->exchanges as $exchange ) { - // Allow max 1%/safetyFactor of coin amount to be transfer fee: - $minXFER = max( $minXFER, $this->getSafeWithdrawFee( $exchange, 'BTC', $averageBTC ) / $safetyFactor ); - } - - $remainingProfit = formatBTC( $profit - $restockCash ); - if ( $remainingProfit < $minXFER ) { - logg( "Not enough profit yet..." ); - return; - } - - logg( "Withdrawing profit: $remainingProfit BTC to $profitAddress", true ); - if ( $highestExchange->withdraw( 'BTC', $remainingProfit, $profitAddress ) ) { - $txFee = $this->getSafeWithdrawFee( $highestExchange, 'BTC', $averageBTC ); - Database::recordProfit( $remainingProfit - $txFee, 'BTC', $profitAddress, time() ); - Database::saveWithdrawal( 'BTC', $remainingProfit, $profitAddress, $highestExchange->getID(), 0, - $highestExchange->getWithdrawFee( 'BTC', $remainingProfit ) ); - - // ------------------------------------------------------------------------- - $restockFunds = $this->stats[ self::STAT_AUTOBUY_FUNDS ]; - logg( "Overwriting restock funds..." ); - logg( "Restock cash before: $restockFunds BTC" ); - $this->stats[ self::STAT_AUTOBUY_FUNDS ] = $restockCash; - logg( " Restock cash after: $restockCash BTC" ); - logg( " Remaining profit: $remainingProfit BTC" ); - // ------------------------------------------------------------------------- - } - - } - - private function autobuyAltcoins( &$arbitrator ) { - - if ( !Config::get( Config::MODULE_AUTOBUY, Config::DEFAULT_MODULE_AUTOBUY ) ) { - return; - } - - logg( "autobuyAltcoins()" ); - - $autobuyFunds = formatBTC( $this->stats[ self::STAT_AUTOBUY_FUNDS ] ); - logg( "Autobuy funds: $autobuyFunds BTC" ); - - $autobuyAmount = formatBTC( min( Config::get( Config::MAX_BUY, Config::DEFAULT_MAX_BUY ), $autobuyFunds ) ); - logg( "Autobuying for $autobuyAmount BTC" ); - if ( $autobuyAmount < 0.0001 ) { - logg( "Not enough autobuy funds" ); - $this->stats[ self::STAT_AUTOBUY_FUNDS ] = 0; - return; - } - - $needs = [ ]; - $allWallets = [ ]; - foreach ( $this->exchanges as $exchange ) { - $allWallets[ $exchange->getID() ] = $exchange->getWalletsConsideringPendingDeposits(); - } - - logg( "Getting need..." ); - - $results = Database::getCurrentSimulatedProfitRate(); - $stats = Database::getWalletStats(); - foreach ( $results as $row ) { - - $currency = $row[ 'currency' ]; - $coin = $row[ 'coin' ]; - if ( !Config::isCurrency( $currency ) ) { - logg( "Skipping: $currency is not a currency!" ); - continue; - } - if ( Config::isCurrency( $coin ) || Config::isBlocked( $coin ) ) { - logg( "Skipping: $coin is blocked!" ); - continue; - } - - $exchangeID = $row[ 'ID_exchange_target']; - $stat = $stats[ $coin ][ $exchangeID ]; - $exchange = $this->exchangesID[ $exchangeID ]; - $balance = $allWallets[ $exchangeID ][ $coin ]; - $desiredBalance = $stat[ 'desired_balance' ]; - - $diff = formatBTC( $desiredBalance - $balance ); - if ( $diff > 0 ) { - logg( "Need $diff $coin @ " . $exchange->getName() ); - // We add an entry to the $needs array ratio times to get a weighted randomized autobuy function. - for ( $i = 0; $i < $row[ 'ratio' ]; $i++ ) { - $needs[] = ['coin' => $coin, 'amount' => $diff, 'exchange' => $exchangeID ]; - } - } - } - - if ( count( $needs ) == 0 ) { - logg( "No need!" ); - return; - } - - // Try up to ten times to fill a need - for ( $i = 0; $i < 10; $i++ ) { - - logg( "Rolling the dice..." ); - shuffle( $needs ); - $need = $needs[ 0 ]; - - $exchange = $this->exchangesID[ $need[ 'exchange' ] ]; - $coin = $need[ 'coin' ]; - $needAmount = $need[ 'amount' ]; - - logg( "Filling need $needAmount $coin @ " . $exchange->getName() ); - - $orderbook = $exchange->getOrderbook( $coin, 'BTC' ); - if ( is_null( $orderbook ) ) { - logg( "Invalid orderbook!" ); - continue; - } - $rate = formatBTC( $orderbook->getBestAsk()->getPrice() ); - $askAmount = $orderbook->getBestAsk()->getAmount(); - $buyAmount = formatBTC( min( $needAmount, min( $askAmount, $autobuyAmount / ($rate * 1.01) ) ) ); - $buyPrice = formatBTC( $buyAmount * $rate ); - if ( $buyPrice < $exchange->getSmallestOrderSize( $coin, 'BTC', 'buy' ) ) { - logg( "Not enough coins at top of the orderbook!" ); - continue; - } - - $tradeableBefore = $exchange->getWallets()[ $coin ]; - logg( "Posting buy order to " . $exchange->getName() . ": $buyAmount $coin for $buyPrice @ $rate" ); - $orderID = $exchange->buy( $coin, 'BTC', $rate, $buyAmount ); - if ( !is_null( $orderID ) ) { - logg( "Waiting for order execution..." ); - sleep( Config::get( Config::ORDER_CHECK_DELAY, Config::DEFAULT_ORDER_CHECK_DELAY ) ); - - if ( !$exchange->cancelOrder( $orderID ) ) { - // Cancellation failed: Order has been executed! - logg( "Order executed!" ); - Database::saveManagement( $coin, $buyAmount, $rate, $exchange->getID() ); - $this->stats[ self::STAT_AUTOBUY_FUNDS ] = formatBTC( $autobuyFunds - $buyPrice ); - - // Make sure the wallets are updated for pending deposit calculations. - $tradesMade = array( - $exchange->getID() => array( - $tradeable => $buyAmount, - ) - ); - $exchange->refreshWallets( $tradesMade ); - - $arbitrator->getTradeMatcher()->handlePostTradeTasks( $arbitrator, $exchange, $coin, 'BTC', 'buy', - $orderID, $buyAmount ); - return; - } - } - } - - } - - private function manageWallets( &$arbitrator ) { - - logg( "manageWallets()" ); - - self::saveSnapshot(); - - self::autobuyAltcoins( $arbitrator ); - - self::balanceCurrencies(); - - self::balanceAltcoins(); - - self::liquidateAltcoins( $arbitrator ); - - } - - private function balanceAltcoins() { - - logg( "balanceAltcoins()" ); - - if ( !Config::get( Config::MODULE_AUTOBALANCE, Config::DEFAULT_MODULE_AUTOBALANCE ) ) { - logg( "Module disabled: Skipping balancing!" ); - return; - } - - $allcoins = [ ]; - foreach ( $this->exchanges as $exchange ) { - $wallets = $exchange->getWalletsConsideringPendingDeposits(); - foreach ( $wallets as $coin => $balance ) { - if ( $balance > 0 ) { - $allcoins[] = $coin; - } - } - } - - $coins = array_unique( $allcoins ); - foreach ( $coins as $coin ) { - if ( $coin == 'BTC' ) { - continue; - } - $this->balance( $coin ); - } - - } - - private function liquidateAltcoins( &$arbitrator ) { - logg( "liquidateAltcoins()" ); - - if ( !Config::get( Config::MODULE_LIQUIDATE, Config::DEFAULT_MODULE_LIQUIDATE ) ) { - logg( "Module disabled: Skipping liquidation!" ); - return; - } - - if ( $this->getBotAgeInDays() < 7 ) { - logg( "Bot not active long enough: Skipping liquidation!" ); - return; - } - - logg( "Calculating overbalances..." ); - $overbalances = [ ]; - - $stats = Database::getWalletStats(); - foreach ( $stats as $coin => $data ) { - - if ( Config::isCurrency( $coin ) || Config::isBlocked( $coin ) ) { - logg( "Skipping: $coin is blocked!" ); - continue; - } - - $entry = [ ]; - - $coinIsNeeded = false; - foreach ( $data as $exchangeID => $stat ) { - - $exchange = $this->exchangesID[ $exchangeID ]; - $wallets = $exchange->getWallets(); - $balance = $wallets[ $coin ]; - $desiredBalance = $stat[ 'desired_balance' ]; - - $diff = formatBTC( $desiredBalance - $balance ); - if ( $diff == 0 ) { - continue; - } - - if ( $diff > 0 ) { - logg( "Need $diff $coin @ " . $exchange->getName() ); - $coinIsNeeded = true; - continue; - } - - $aDiff = abs( $diff ); - - logg( "Unneeded $aDiff $coin @ " . $exchange->getName() ); - $entry[] = ['coin' => $coin, 'amount' => $aDiff, 'exchange' => $exchangeID ]; - } - - if ( $coinIsNeeded ) { - logg( "$coin is needed: Not liquidating!" ); - continue; - } - - $overbalances = array_merge( $entry, $overbalances ); - } - - if ( count( $overbalances ) == 0 ) { - logg( "No overbalances!" ); - return; - } - - foreach ( $overbalances as $overbalance ) { - - $exchange = $this->exchangesID[ $overbalance[ 'exchange' ] ]; - $coin = $overbalance[ 'coin' ]; - $liquidationAmount = $overbalance[ 'amount' ]; - - logg( "Liquidating $liquidationAmount $coin @ " . $exchange->getName() ); - - $orderbook = $exchange->getOrderbook( $coin, 'BTC' ); - if ( is_null( $orderbook ) ) { - logg( "Invalid orderbook!" ); - continue; - } - $rate = formatBTC( $orderbook->getBestBid()->getPrice() ); - $bidAmount = $orderbook->getBestBid()->getAmount(); - $sellAmount = formatBTC( min( $liquidationAmount, $bidAmount ) ); - $sellPrice = formatBTC( $sellAmount * $rate ); - if ( $sellPrice < $exchange->getSmallestOrderSize( $coin, 'BTC', 'sell' ) ) { - logg( "Not enough coins at top of the orderbook!" ); - continue; - } - - $tradeableBefore = $exchange->getWallets()[ $coin ]; - logg( "Posting sell order to " . $exchange->getName() . ": $sellAmount $coin for $sellPrice @ $rate" ); - $orderID = $exchange->sell( $coin, 'BTC', $rate, $sellAmount ); - if ( !is_null( $orderID ) ) { - logg( "Waiting for order execution..." ); - sleep( Config::get( Config::ORDER_CHECK_DELAY, Config::DEFAULT_ORDER_CHECK_DELAY ) ); - - if ( !$exchange->cancelOrder( $orderID ) ) { - // Cancellation failed: Order has been executed! - logg( "Order executed!" ); - Database::saveManagement( $coin, $sellAmount * -1, $rate, $exchange->getID() ); - - // Make sure the wallets are updated for pending deposit calculations. - $tradesMade = array( - $exchange->getID() => array( - $coin => -$sellAmount, - ) - ); - $exchange->refreshWallets( $tradesMade ); - - $arbitrator->getTradeMatcher()->handlePostTradeTasks( $arbitrator, $exchange, $coin, 'BTC', 'sell', - $orderID, $sellAmount ); - } - } - } - - } - - public function getBotAgeInDays() { - return floor( $this->getBotAgeInSeconds() / 24 * 3600 ); - - } - - public function getBotAgeInSeconds() { - return time() - $this->stats[ self::STAT_BOT_AGE ]; - - } - - private function doWithdraw( $source, $coin, $amount, $address, $tag ) { - - try { - return $source->withdraw( $coin, $amount, $address, $tag ); - } - catch ( Exception $ex ) { - // Perhaps the withdrawal was unsuccessful because of insufficient balance. - // This can happen if the account only has $amount balance, in which case - // we need to subtract the withdrawal fee. - $amount -= $source->getWithdrawFee( $coin, $amount ); - return $source->withdraw( $coin, $amount, $address, $tag ); - } - - return false; - - } - - public function withdraw( $source, $target, $coin, $amount ) { - - if ( !Config::isCurrency( $coin ) ) { - $limits = $source->getWithdrawLimits( $coin, 'BTC' ); - - if ( !is_null( $limits[ 'amount' ][ 'min' ] ) && - floatval( $limits[ 'amount' ][ 'min' ] ) > $amount ) { - logg( sprintf( "[%s] Withdrawal amount %s below minimum trade amount %s", - $source->getName(), $amount, $limits[ 'amount' ][ 'min' ] ) ); - return false; - } - - if ( !is_null( $limits[ 'amount' ][ 'max' ] ) && - floatval( $limits[ 'amount' ][ 'max' ] ) < $amount ) { - logg( sprintf( "[%s] Withdrawal amount %s above maximum trade amount %s", - $source->getName(), $amount, $limits[ 'amount' ][ 'max' ] ) ); - return false; - } - } - - $amount = formatBTC( $amount ); - logg( "Transfering $amount $coin " . $source->getName() . " => " . $target->getName() ); - $address = $target->getDepositAddress( $coin ); - $tag = null; - if ( is_array( $address ) ) { - $tag = $address[ 1 ]; - $address = $address[ 0 ]; - } - if ( is_null( $address ) || strlen( trim( $address ) ) == 0 ) { - logg( "Invalid deposit address for " . $target->getName() . ", received: ". $address ); - - return false; - } - - - logg( "Deposit Address: $address, Memo: " . ( is_null( $tag ) ? "null" : $tag ) ); - if ( $this->doWithdraw( $source, $coin, $amount, trim( $address ), $tag ) ) { - Database::saveWithdrawal( $coin, $amount, trim( $address ), $source->getID(), $target->getID(), - $source->getWithdrawFee( $coin, $amount ) + - $target->getDepositFee( $coin, $amount ) ); - - return true; - } - - return false; - - } - - public function getSafeDepositFee( $exchange, $tradeable, $amount ) { - - $fee = $exchange->getDepositFee( $tradeable, $amount ); - if ( is_null( $fee ) ) { - $fee = 0; - } - - return $fee; - - } - - public function getSafeWithdrawFee( $exchange, $tradeable, $amount ) { - - $fee = $exchange->getWithdrawFee( $tradeable, $amount ); - if ( !is_null( $fee ) ) { - return $fee; - } - - $txFees = [ ]; - - // Average fee from other exchanges: - foreach ( $this->exchanges as $x ) { - - if ( $x === $exchange ) { - continue; - } - - $txFee = $x->getWithdrawFee( $tradeable, $amount ); - if ( !is_null( $txFee ) ) { - $txFees[] = $txFee; - } - } - - if ( count( $txFees ) == 0 ) { - logg( "[" . $exchange->getName() . "] WARNING: Unknown transfer fee for $tradeable. Calculation may be inaccurate!" ); - return 0; - } - - return max( $txFees ); - - } - -}