Das kann jetzt mittlerweile echt nurnoch ein API Server Überlast Szenario sein. So unregelmäßig und an unterschiedlichen Stellen das kommt. Und wenn ich noch weitere identische Calls nur mit nem Delay absetze werden diese mit gleicher OrderID korrekt beantwortet.
Ich schreibe später mal ein Script das permanent kauft, Status abfragt und Cancelt um das nachzuweisen. Bekommen die BTer Jungs ein nettes Ticket von mir
Von mir bekommen die von Bter auch ein Ticket. Vor allem der BTC/CNY Markt ist dort zeitweise vermurkst. Zeitweise ein falsches Orderbuch, bis zu 5% daneben. Zwar in sich konsistent ( bid kleiner als ask , Sortierung ist ebenfalls OK, also keine Möglichkeit den Fehler durch triviale Checks zu erkennen), bleibt ein paar Sekunden so falsch, und nach einigen Sekunden ist der Spuk vorbei.
Kann mir vorerst helfen, indem ich Arbitragen von mehr als 1% Gewinn ignoriere. Kommt mir so vor, als ob ein Orderbuch angezeigt wird, das einige Stunden alt ist.
Scheint wohl irgendein Caching und/oder Publizierungsproblem auf Seiten von BTer zu sein, dass mir wirklich in die Suppe spuckt... Vielleicht kann/mag jemand das verifizieren.
Abblauf: 1) Setzte eine Order in einen beliebigen Markt
2) Hol dir den Status der Order via "private/getorder" API
3) Cancel die Order
Ergebnis:1) Wenn man innerhalb der ersten 10 Sekunden nach Erstellung der Order den Status abrufen möchte, bekommt man ein API Response Error "Invalid Order ID".
Nach den 10 Sekunden ist die Order ID auf einmal bekannt und kann abgefragt werden.
2) Wenn man nicht innerhalb der ersten Minute nach der Erstellung der Order wieder den Status abfragt, "vergisst" die API ebenfalls wieder diese Order ID und bei der dann gestellten Anfrage bekommt man erstmalig wieder ein Invalid Order ID zurück.
3) Man kann den Umstand jetzt auf mehreren Ebenen begegnen. Ich habe mich für die Variante entschieden, dass nach einer Order nach 15 Sekunden ein Pseudo-Call abgesetzt wird, um den Cache für weitere Abfragen vorzubereiten.
Die Frage die sich natürlich daraus ergibt, wenn die Latenz bereits beim einstellen einer Order so massiv ist, wie wird es dann wohl bei anderen Schnittstellen sein...
Wer es testen mag, einfach den API Key ($apiKey) und Secret ($apiSecret) von eurem Account verwenden und mit dem auskommentierten sleep Timer experimentieren.
date_default_timezone_set('Europe/Berlin');
while(true)
{
$pair = "WDC_BTC";
$buyrice = 0.00001000;
$buyAmount = 50;
logging("/////////////////////////////");
logging("BUY ORDER");
$buyOrderID = buyOrder($pair, $buyrice, $buyAmount);
var_dump($buyOrderID);
// Change this parameter to find the sweet spot of the API response / caching time
//sleep(15);
logging("/////////////////////////////");
logging("INFO ORDER");
$info = statusOrder($buyOrderID["order_id"]);
var_dump($info);
logging("/////////////////////////////");
logging("CANCEL ORDER");
$cancel = cancelOrder($buyOrderID["order_id"]);
var_dump($cancel);
logging("/////////////////////////////");
logging("FINISHED");
logging("/////////////////////////////");
sleep(20);
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function query($path, array $req = array())
{
$api_url = "https://bter.com/api/1/";
$apiKey = "";
$apiSecret = "";
// Prevent API from overload
usleep(300000);
// generate a nonce to avoid problems with 32bits systems
$mt = explode(' ', microtime());
$req['nonce'] = $mt[1].substr($mt[0], 2, 6);
// generate the POST data string
$post_data = http_build_query($req, '', '&');
$sign = hash_hmac('sha512', $post_data, $apiSecret);
// generate the extra headers
$headers = array(
'KEY: ' . $apiKey,
'SIGN: ' . $sign,
);
//!!! please set Content-Type to application/x-www-form-urlencoded if it's not the default value
// curl handle (initialize if required)
static $ch = null;
if (is_null($ch)) {
$ch = curl_init();
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_USERAGENT,
'Mozilla/4.0 (compatible; Bter PHP bot; '.php_uname('a').'; PHP/'.phpversion().')'
);
}
curl_setopt($ch, CURLOPT_URL, $api_url . $path);
curl_setopt($ch, CURLOPT_POSTFIELDS, $post_data);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, FALSE);
// run the query
// run the query
$error = null;
for ($i = 0; $i < 5; $i++)
{
try
{
$res = curl_exec($ch);
}
catch (Exception $ex)
{
$error = "[BTER] Private API CALL error: " . print_r($ex->getMessage(), true);
logging($error, true);
sleep(1);
continue;
}
if ($res === false)
{
$error = "[BTER] Could not get reply: " . curl_error($ch);
logging($error);
continue;
}
$dec = json_decode($res, true);
//Debugging only
// var_dump($dec);
if (!$dec || $dec["result"] != "true" || (key_exists("msg", $dec) && $dec["msg"] != "Success"))
{
$error = "[BTER] Private API RESPONSE error: " . print_r($dec, true);
$info = " Method: " . print_r($path, true) . " / Parameter: " . print_r($req, true);
logging($error . "\n" . $info, true);
sleep(5);
continue;
}
else
return $dec;
}
}
function buyOrder($pair, $buyrice, $buyAmount)
{
return query('private/placeorder', array
(
'pair' => strtolower($pair),
'type' => 'buy',
'rate' => $buyrice,
'amount' => $buyAmount
)
);
}
function cancelOrder($orderID)
{
return query('private/cancelorder', array('order_id' => $orderID));
}
function statusOrder($orderID)
{
return query('private/getorder', array('order_id' => $orderID));
}
function logging($message)
{
echo date("H:i:s") . ": " . $message . "\n";
}