This article is not really a debate over what is the correct tool (language) to consume a streaming web service but just one example of how TradeKing’s streaming quotes api can be handled. There are a couple caveats to the way I implemented it (in a rush before seeing the movie ‘Flight’). The first – you need PECL’s OAuth. You can implement this easily without it, however I just took the example from TradeKing’s profile web service and expanded. The key is generating the hash signature (HMAC_SHA1 in this case) and for this test, it was easier for me to just use the OAuth library I already had installed. The second caveat is how I handled decoding HTTP/1.1 Chunked Encoding. I just quickly pounded away at it. My understanding is that there are a couple available libraries (one in PECL) that handle the process.
TradeKing kindly hands you either JSON or XML back via “Chunked transfer encoding“. Before each chunk you have a hexadecimal number containing the number of characters in the chunk followed by CRLF (commonly seen as “\r\n”) then the chunked message and another CRLF.
Looks like this:
1a {"status":"connected"}
My example connects to https://stream.tradeking.com/v1/market/quotes.json, then sends the proper headers and grabs whatever comes over the socket – echoing out usable chunks.
If you wanted to clean this script up and use it as a CLI process to feed something like Redis or a message queue, you would need to build the ‘chunk handling’ function to parse the JSON message and store it/take action on it. Then have another process handle the triggering of something else (like a trade).
You can check out the source is below or at Github.
<!--?php //Provide your consumer keys / tokens try { // Setup an OAuth consumer $oauth = new OAuth($consumer_key,$consumer_secret,OAUTH_SIG_METHOD_HMACSHA1,OAUTH_AUTH_TYPE_AUTHORIZATION); $oauth--->setToken($access_token,$access_secret); $noonce=date('U') ; $oauth->setNonce($noonce); $signature= $oauth->generateSignature("GET", "https://stream.tradeking.com/v1/market/quotes.json?symbols=". $symbols);//https://stream.tradeking.com/v1/market/quotes.xml?symbols=AAPL,QQQ,MSFT"); $fp = fsockopen("ssl://stream.tradeking.com",443, $errno, $errstr, 30); if (!$fp) { echo "$errstr ($errno) \n"; } else { $out = "GET /v1/market/quotes.json?symbols=". $symbols ." HTTP/1.1\r\n"; $out.="User-Agent: ThomasLoughlin.com/1.0\r\n"; $out .= "Host: stream.tradeking.com\r\n"; $out.="Accept: */*\r\n"; $out.="Authorization: OAuth "; $out .="oauth_consumer_key=".'"' .$consumer_key .'"' .","; $out .="oauth_nonce=".'"' .$noonce .'"' .","; $out .="oauth_signature=".'"' .urlencode($signature) .'"' .","; $out .="oauth_signature_method=".'"' ."HMAC-SHA1" .'"' .","; $out .="oauth_timestamp=".'"' .date('U') .'"' .","; $out .="oauth_token=".'"' .$access_token .'"' .","; $out .="oauth_version=".'"' ."1.0" .'"' ."\r\n"; $out .= "Connection: Close\r\n\r\n"; fwrite($fp, $out); echo $out; sleep(2); //Chill for a couple seconds because no one wants to be rushed $temp_unfinished =""; //This will hold the unfinished chunks since we are reading 512 chars at a time $start=false; while (!feof($fp)) { $temp = fgets($fp, 512); //read in whatever is ready $temp=$temp_unfinished . $temp; //add the extra that was not parsed previously. $temp_unfinished=""; //clear the var just to be careful - not needed but I did this fast if($start==false) { /* The intent of this was to trash the header information and get started with the first chunk (more effective when I was using a bigger buffer */ $data=explode("\r\n\r\n", $temp, 2); if(count($data)==2) { $start=true; $temp=$data[1]; } } ////////////////////// // Since we sent HTTP/1.1, we have to handle the chunking ////////////////////// $chunks=explode("\r\n",$temp); $count=count($chunks); for($i=0;$ilastResponse . "\n"; } ?>
It will output something like:
php check_price.php GET /v1/market/quotes.json?symbols=AAPL,QQQ,MSFT,VXX HTTP/1.1 User-Agent: ThomasLoughlin.com/1.0 Host: stream.tradeking.com Accept: */* Authorization: OAuth oauth_consumer_key="23423ewrfrqwfqwerqtretertqr",oauth_nonce="1351900465",oauth_signature="dsafasdfERWEEFdfsdrewrwer%3D",oauth_signature_method="HMAC-SHA1",oauth_timestamp="1351900465",oauth_token="245624562rtewegwergwergwerg",oauth_version="1.0" Connection: Close This chunk can be handled:{"status":"connected"} ------------------ This chunk can be handled:{"trade":{"cvol":"44079982","datetime":"2012-11-02T19:54:03-04:00","exch":"Pacific","last":"65.04","symbol":"QQQ","tcond":"57,16,10","timestamp":"1351900443","vl":"100","vwap":"65.9048"}} ------------------ This chunk can be handled:{"quote":{"ask":"29.49","asksz":"6","bid":"29.48","bidsz":"10","datetime":"2012-11-02T19:54:49-04:00","exch":"Pacific","qcond":"Regular, two-sided open quote automated","symbol":"MSFT","timestamp":"1351900489"}}