Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added files exchange.php and cryptopiaAPI.php
Abstract class Exchange - general exchange API class cryptopiaAPI - api wrapper for https://www.cryptopia.co.nz
- Loading branch information
1 parent
0ed3a91
commit 27a49b5
Showing
2 changed files
with
388 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,224 @@ | ||
<?php | ||
include 'exchange.php'; | ||
|
||
class Cryptopia extends Exchange{ | ||
|
||
public function __construct($priv, $pub) { | ||
$this->privateKey = $priv; | ||
$this->publicKey = $pub; | ||
|
||
$result = json_decode($this->apiCall("GetBalance", array( 'Currency'=> 'BTC' )), true); // There is a bug in the API if you send no parameters it will return Success:true Error: Market not found. | ||
// Array | ||
// ( | ||
// [Success] => 1 | ||
// [Message] => | ||
// [Data] => | ||
// [Error] => Market not found. | ||
// ) | ||
// print_r($result); | ||
if( $result['Success'] != "true" ) { | ||
throw new Exception("Can't Connect to Cryptopia, Error: " . $result['Error'] ); | ||
return false; | ||
} | ||
return true; | ||
} | ||
|
||
private function apiCall($method, array $req = array()) { | ||
$public_set = array( "GetCurrencies", "GetTradePairs", "GetMarkets", "GetMarket", "GetMarketHistory", "GetMarketOrders" ); | ||
$private_set = array( "GetBalance", "GetDepositAddress", "GetOpenOrders", "GetTradeHistory", "GetTransactions", "SubmitTrade", "CancelTrade", "SubmitTip" ); | ||
static $ch = null; | ||
$ch = curl_init(); | ||
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true); | ||
//curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/4.0 (compatible; Cryptopia.co.nz API PHP client; FreeBSD; PHP/'.phpversion().')'); | ||
if ( in_array( $method ,$public_set ) ) { | ||
$url = "https://www.cryptopia.co.nz/api/" . $method; | ||
if ($req) { foreach ($req as $r ) { $url = $url . '/' . $r; } } | ||
curl_setopt($ch, CURLOPT_URL, $url ); | ||
} elseif ( in_array( $method, $private_set ) ) { | ||
$url = "https://www.cryptopia.co.nz/Api/" . $method; | ||
$nonce = explode(' ', microtime())[1]; | ||
$post_data = json_encode( $req ); | ||
$m = md5( $post_data, true ); | ||
$requestContentBase64String = base64_encode( $m ); | ||
$signature = $this->publicKey . "POST" . strtolower( urlencode( $url ) ) . $nonce . $requestContentBase64String; | ||
$hmacsignature = base64_encode( hash_hmac("sha256", $signature, base64_decode( $this->privateKey ), true ) ); | ||
$header_value = "amx " . $this->publicKey . ":" . $hmacsignature . ":" . $nonce; | ||
$headers = array("Content-Type: application/json; charset=utf-8", "Authorization: $header_value"); | ||
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers); | ||
curl_setopt($ch, CURLOPT_URL, $url ); | ||
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode( $req ) ); | ||
} | ||
// run the query | ||
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0); | ||
curl_setopt($ch, CURLOPT_FRESH_CONNECT, TRUE); // Do Not Cache | ||
$res = curl_exec($ch); | ||
if ($res === false) throw new Exception('Could not get reply: '.curl_error($ch)); | ||
return $res; | ||
} | ||
|
||
// Some API calls require TradePairId rather than the TradePair so this should store the TradePairId | ||
public function setSymbols() { | ||
$result = json_decode($this->apiCall("GetTradePairs", array() ), true); | ||
|
||
if( $result['Success'] == "true" ) { | ||
$json = $result['Data']; | ||
} else { | ||
throw new Exception("Can't get symbols, Error: " . $result['Error'] ); | ||
} | ||
foreach($json as $pair) { | ||
// creates associative array of key: StandardSymbol (i.e. BTCUSD) value: ExchangeSymbol (i.e. btc_usd) | ||
$this->symbols[ $this->makeStandardSymbol( $pair["Label"] ) ] = $pair["Id"]; | ||
} | ||
} | ||
|
||
public function updatePrices() { | ||
$result = json_decode($this->apiCall("GetMarkets", array() ), true); | ||
if( $result['Success'] == "true" ) { | ||
$json = $result['Data']; | ||
} else { | ||
throw new Exception("Can't get markets, Error: " . $result['Error'] ); | ||
} | ||
foreach($json as $pair) { | ||
$this->prices[$pair['Label']]['high'] = $pair['High']; | ||
$this->prices[$pair['Label']]['low'] = $pair['Low']; | ||
$this->prices[$pair['Label']]['bid'] = $pair['BidPrice']; | ||
$this->prices[$pair['Label']]['ask'] = $pair['AskPrice']; | ||
$this->prices[$pair['Label']]['last'] = $pair['LastPrice']; | ||
$this->prices[$pair['Label']]['time'] = ''; // not available on Cryptopia | ||
} | ||
} | ||
|
||
// @todo add setBalance | ||
|
||
Public function getBalance() { | ||
$result = $this->apiCall("GetBalance", array('Currency'=> "") ); // "" for All currency balances | ||
$result = json_decode($result, true); | ||
if( $result['Success'] == "true" ) { | ||
// @todo ADD CODE TO REFORMAT Array to standard | ||
return $result['Data']; | ||
} else { | ||
throw new Exception("Can't get balances, Error: " . $result['Error'] ); | ||
} | ||
} | ||
|
||
Public function getCurrencyBalance( $currency ) { | ||
$result = $this->apiCall("GetBalance", array( 'Currency'=> $currency ) ); | ||
$result = json_decode($result, true); | ||
if( $result['Success'] == "true" ) { | ||
return $result['Data'][0]['Total']; | ||
} else { | ||
throw new Exception("Can't get balance, Error: " . $result['Error'] ); | ||
} | ||
} | ||
|
||
// currency pair $symbol should be in standard Format not exchange format | ||
public function activeOrders( $symbol = "") | ||
{ | ||
if($symbol == "") { | ||
$apiParams = array( 'TradePairId'=>"" ); | ||
} else { | ||
$apiParams = array( 'TradePairId'=>$this->getExchangeSymbol($symbol) ); | ||
} | ||
$myOrders = json_decode($this->apiCall("GetOpenOrders", $apiParams), true); | ||
//print_r($myOrders); | ||
// There is a bug in the API if you send no parameters it will return Success:true Error: Market not found. | ||
// Array | ||
// ( | ||
// [Success] => 1 | ||
// [Message] => | ||
// [Data] => | ||
// [Error] => Market not found. | ||
// ) | ||
|
||
$orders = array(); | ||
$price = array(); // sort by price | ||
if( $myOrders['Success'] == "true" && $myOrders['Error'] == "") { | ||
foreach ($myOrders['Data'] as $order) { | ||
$orderSymbol = $this->makeStandardSymbol($order["Market"]); // convert to standard format currency pair | ||
$orders[] = ["symbol"=>$orderSymbol, "type"=>$order["Type"], "price"=>$order["Rate"], | ||
"amount"=>$order["Remaining"], "id"=>$order["OrderId"] ]; | ||
if ($order["Type"] == "Sell") { | ||
$price[] = 0 - $order['Rate']; // lowest ask price if first | ||
} else { | ||
$price[] = $order['Rate']; | ||
} | ||
} | ||
if($orders) // If there are any orders | ||
array_multisort($price, SORT_DESC, $orders); // sort orders by price | ||
} else { | ||
throw new Exception("Can't get active orders, Error: " . $myOrders['Error'] ); | ||
} | ||
return $orders; | ||
} | ||
|
||
public function permissions() { | ||
|
||
} | ||
|
||
public function cancelOrder($id) { | ||
$result = $this->apiCall("CancelTrade", array( 'Type'=>"Trade", 'OrderId'=>$id )); | ||
$result = json_decode($result, true); | ||
if( $result['Success'] == "true" ) { | ||
echo "Orders Canceled: " . implode( ", ", $result['Data']) . "\n"; | ||
} else { | ||
throw new Exception("Can't Cancel Order # $id, Error: " . $result['Error']); | ||
} | ||
} | ||
|
||
public function cancelAll() { | ||
if(!$this->activeOrders()) return false; // "No open orders to cancel.\n" | ||
$result = $this->apiCall("CancelTrade", array( 'Type'=>"All" )); | ||
$result = json_decode($result, true); | ||
if( $result['Success'] == "true" ) { | ||
return "Orders Canceled: " . implode( ", ", $result['Data']) . "\n"; | ||
} else { | ||
throw new Exception("Can't Cancel All Orders, Error: " . $result['Error']); | ||
} | ||
} | ||
|
||
public function orderStatus($id) { | ||
|
||
} | ||
|
||
public function placeOrder($symbol, $amount, $price, $side) { | ||
$result = $this->apiCall("SubmitTrade", array( 'Type'=> $side, 'TradePairId'=> $this->getExchangeSymbol($symbol), | ||
'Rate'=> number_format((float)$price, 8, '.', ''), 'Amount'=> number_format((float)$amount, 8, '.', '') ) ); | ||
$result = json_decode($result, true); | ||
if( $result['Success'] == "true" ) { | ||
return "Order Placed. OrderId:" .$result['Data']['OrderId'] . | ||
" FilledOrders: " . implode( ", ", $result['Data']['FilledOrders']) . "\n"; | ||
} else { | ||
throw new Exception("Can't Place Order, Error: " . $result['Error'] ); //*** die instead of echo | ||
} | ||
} | ||
|
||
public function buy($symbol, $amount, $price) { | ||
return $this->placeOrder($symbol, $amount, $price, 'Buy'); | ||
} | ||
|
||
public function sell($symbol, $amount, $price) { | ||
return $this->placeOrder($symbol, $amount, $price, 'Sell'); | ||
} | ||
|
||
public function marketOrderbook($symbol) | ||
{ | ||
$mktOrders = json_decode($this->apiCall("GetMarketOrders", array('TradePairId'=>$this->getExchangeSymbol($symbol))), true); | ||
unset($orders); | ||
if( $mktOrders['Success'] == "true" && $mktOrders['Error'] == "") { | ||
//print_r($mktOrders); | ||
foreach ($mktOrders['Data'] as $orderType => $order) { | ||
foreach($order as $ordersByType) { | ||
// $standardSymbol = $this->getStandardSymbol($symbol); // @todo not yet implemented | ||
$orders[] = ["symbol"=>$symbol, "type"=>$orderType, "price"=>$ordersByType["Price"], | ||
"amount"=>$ordersByType["Volume"] ]; | ||
} | ||
} | ||
} else { | ||
throw new Exception("Can't get orderbook, Error: " . $mktOrders['Error'] ); | ||
} | ||
return $orders; | ||
} | ||
|
||
} | ||
|
||
?> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,164 @@ | ||
<?php | ||
|
||
abstract class Exchange | ||
{ | ||
// ----- Variables ----- | ||
|
||
// Each exchanges class construction function should set these and test the connection to the API | ||
private $privateKey = ''; // API Secret | ||
private $publicKey = ''; // API Key | ||
|
||
/** | ||
* Symbols is an array of all the trading pairs on a given exchange. | ||
* | ||
* i.e. array("DOT/BTC", "LTC/BTC", "DOGE/BTC", "POT/BTC", "FTC/BTC", "WSX/BTC", "DARK/BTC", ...) | ||
* It is assigned in the setSymbols() function and retrieved with the getSymbols() function. | ||
* note: these can be different for different exchanges: "LTC/BTC" "LTCBTC" "ltc_btc" | ||
* @todo Format functions or lookup tables need to be made for each exchange to standardize the format. | ||
* getStandardSymbol($symbol) and getExchangeSymbol($symbol) | ||
* | ||
* @var array of strings | ||
*/ | ||
protected $symbols = array(); //Trading Pairs | ||
|
||
/** | ||
* Prices are the current and 24hr market prices for each trading pair. | ||
* | ||
* Each trading pair has an array with keys: high, low, bid, ask, last, and time. | ||
* i.e. {"ETHBTC": {"high": 0, "low": 0, "bid": 0, "ask": 0, "last": 0, "time": 0}, | ||
* ... , | ||
* "XMRBTC": {"high": 0, "low": 0, "bid": 0, "ask": 0, "last": 0, "time": 0} | ||
* } | ||
* @todo the updatePrices() function for each exchange should use makeStandardSymbol($exchangeSymbol) to get the key for each currency pair array | ||
* note: trading pair symbol could be different but parameters these are the same across all exchanges | ||
* It is assigned in the updatePrices() function and retrieved with the getPrices() function. | ||
* | ||
* @var array of arrays | ||
*/ | ||
protected $prices = array(); | ||
|
||
/** | ||
* Balances is an array of all currencies and assets. | ||
* | ||
* Each currency has an array with keys: amount, available, etc | ||
* @todo Implement updateBalance() function to set the balance for each exchange. | ||
* Should use makeStandardSymbol($exchangeSymbol) to get the key for each currency pair array | ||
* @var array of arrays | ||
*/ | ||
protected $balances = array(); | ||
|
||
|
||
// ----- Get and Set Functions ----- | ||
|
||
public function getSymbols() { | ||
if(!$this->symbols) $this->setSymbols(); | ||
return $this->symbols; | ||
} | ||
// Can be used for currencies and currency pairs to put in only uppercase letters | ||
// note: $exchangeSymbol must be have the base currency first and quote currency second (i.e. eth_btc Not btc_eth). The quote currency is usually BTC, USD, or CNY. | ||
public function makeStandardSymbol($exchangeSymbol) { | ||
// Convert to Uppercase and remove nonletters. | ||
return preg_replace('/[^A-Z]/', '', strtoupper ( $exchangeSymbol )); | ||
} | ||
// @todo not implemented for all exchanges, setSymbols() must create associative array $this->symbols with keys as standard symbols and values as exchange symbols for this function to work. | ||
public function getExchangeSymbol($standardSymbol) { | ||
|
||
//if($standardSymbol == '') return ''; // used to get all symbols | ||
|
||
if(!$this->symbols) $this->setSymbols(); | ||
|
||
if( isset($this->symbols[$standardSymbol]) ) { | ||
return $this->symbols[$standardSymbol]; | ||
} else { | ||
throw new Exception("Can't find symbol '" . $standardSymbol ."'" ); | ||
} | ||
} | ||
|
||
public function getPrices() { | ||
return $this->prices; | ||
} | ||
|
||
// @todo the updateBalance() function for each exchange should use makeStandardSymbol($exchangeSymbol) to get the key for each currency pairs array | ||
public function getBalance() { // this function is overridden until updateBalance() is implemented | ||
// if(!isset($this.balances)) { updateBalance(); } // Not implemented for all exchanges yet | ||
return $this->balances; | ||
} | ||
Public function getCurrencyBalance($currency) { | ||
// @todo Update if not isset() | ||
if(isset($this->balances[$curency]['amount'])) { | ||
return $this->balances[$curency]['amount']; | ||
} | ||
} | ||
|
||
// @todo not implemented for all exchanges, setSymbols() must create associative array $this->symbols with keys as standard symbols and values as exchange symbols for this function to work. | ||
abstract public function setSymbols(); | ||
|
||
// abstract public function getFee($symbol); // @todo Not implemented for ct exchange yet | ||
|
||
// @todo the updatePrices() function for each exchange should use makeStandardSymbol($exchangeSymbol) to get the key for each currency pairs array | ||
abstract public function updatePrices(); | ||
|
||
// abstract public function updateBalance(); // Not implemented for all exchanges yet | ||
|
||
// @todo ??? allow passing a $symbol for all exchanges? | ||
abstract public function activeOrders(); | ||
|
||
abstract public function permissions(); | ||
|
||
abstract public function cancelOrder($id); | ||
// is function should be overridden if exchange has cancel all function call | ||
// @todo write generalized cancel all that loops through active orders, see btceAPI | ||
abstract public function cancelAll(); | ||
|
||
abstract public function orderStatus($id); | ||
|
||
// This should create a buy / sell order at the exchange for [amount] of [asset] at [price] and return the orderId | ||
// @todo should implement getExchangeSymbol($standardSymbol) for each exchange | ||
abstract public function buy($symbol, $amount, $price); // some exchanges may have additional parameters | ||
abstract public function sell($symbol, $amount, $price); // some exchanges may have additional parameters | ||
|
||
|
||
// ----- Data Calculation functions ----- | ||
|
||
// note: assumes buy offers are in descending order by price | ||
public function highestBid($offers, $depth = 0, $type = "Buy") { | ||
$total_amount = 0; | ||
$highest_bid = 0; | ||
if($offers) // if there are any offers | ||
foreach( $offers as $item ) | ||
{ | ||
if( $item["type"] == $type ) { | ||
//echo "type: " . $item["type"] . " price: " . $item['price'] . " amount: " .$item['amount'] . "\n"; | ||
$total_amount += floatval($item['amount']); | ||
|
||
if( $highest_bid == 0 && $total_amount >= $depth ) // Go $1000 deep into the bids | ||
{ | ||
$highest_bid = $item['price']; | ||
break; | ||
} | ||
} | ||
} | ||
return $highest_bid; | ||
} | ||
|
||
// note: assumes sell offers are in accenting order by price | ||
public function LowestAsk($offers, $depth = 0, $type = "Sell") { | ||
return $this->highestBid($offers, $depth, $type); | ||
} | ||
|
||
// takes an array of of offers/orders and returns the volume within a price range | ||
public function volume($offers, $lowerPriceLimit = 0, $upperPriceLimit = PHP_FLOAT_MAX) { | ||
$total_vol = 0; | ||
foreach( $offers as $item ) | ||
{ | ||
//echo "price: " . $item['Price'] . " amount: " .$item['Volume'] . "\n"; | ||
if( $item['price'] >= $lowerPriceLimit && $item['price'] <= $upperPriceLimit ) { | ||
$total_vol += floatval($item['amount']); | ||
} | ||
} | ||
return $total_vol; | ||
} | ||
|
||
} // end of class Exchange | ||
|
||
?> |