Skip to content

Instantly share code, notes, and snippets.

@amendoncabh
Created October 22, 2020 00:09
Show Gist options
  • Save amendoncabh/5f00fbe2c551d59aaf7a0d064d3ef25b to your computer and use it in GitHub Desktop.
Save amendoncabh/5f00fbe2c551d59aaf7a0d064d3ef25b to your computer and use it in GitHub Desktop.
PHP classes to get stock data from various Yahoo finance APIs
<?php
/**
* Get historic stock symbol information from Yahoo
*
* Based on https://code.google.com/p/yahoo-finance-managed/wiki/csvHistQuotesDownload
*/
class YahooHistoricStockApi
{
const QUOTE_INTERVAL_DAY = 'd';
const QUOTE_INTERVAL_WEEK = 'w';
const QUOTE_INTERVAL_MONTH = 'm';
/**
* Period of each data point
* @var string
*/
protected $quotesInterval = null;
/**
* Columns names to parse into PHP variables
* If this is null, all available columns are parsed.
* @see limitColumns method for details
* @var array
*/
protected $limitColumns = null;
/**
* Get data about a stock symbol within a period.
* $to and $from must be a format accepted by strtotime
* @param string $symbol
* @param string $from
* @param string $to
*/
public function fetchHistoricData($symbol, $from = null, $to = null)
{
$url = $this->makeUrl($symbol, $from, $to);
curl_setopt_array($ch = curl_init(), array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
)
);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status != 200) {
throw new ErrorException("Request to $url returned status $status");
}
return $this->parseStockCsv($response);
}
/**
* Convert CSV data into a PHP data
*
* CSV schema is expected to be:
* Date,Open,High,Low,Close,Volume,Adj Close
*
* @param string $csv - CSV from ichart.finance.yahoo.com
* @return array
*/
protected function parseStockCsv($csv)
{
$data = array();
$rows = explode("\n", trim($csv));
$headers = explode(',', array_shift($rows));
if (count($rows) < 1 || !count($headers)) {
throw new UnexpectedValueException('Input data not in a readable format');
}
if (is_array($this->limitColumns)) {
foreach ($headers as &$columnName) {
if (!in_array($columnName, $this->limitColumns)) {
$columnName = false;
}
}
}
foreach ($rows as $row) {
$cols = explode(',', $row);
$rowData = array();
if (count($cols) <= 1) {
continue;
}
foreach ($cols as $index => $value) {
if ($headers[$index]) {
$rowData[$headers[$index]] = $value;
}
}
$data[] = $rowData;
unset($cols);
}
return $data;
}
/**
* Create the URL for an API request
* @inheritdoc from __construct
*/
protected function makeUrl($symbol, $from = null, $to = null)
{
$url = 'http://ichart.finance.yahoo.com/table.csv?s=';
$url .= urlencode($symbol);
if ($from) {
list($fromYear, $fromMonth, $fromDay) = explode(',', date('Y,n,j', strtotime($from)));
$url .= sprintf(
'&a=%s&b=%s&c=%s',
--$fromMonth,
$fromDay,
$fromYear
);
}
if ($to) {
list($toYear, $toMonth, $toDay) = explode(',', date('Y,n,j', strtotime($to)));
$url .= sprintf(
'&d=%s&e=%s&f=%s',
--$toMonth,
$toDay,
$toYear
);
}
if ($this->quotesInterval) {
$url .= '&g=' . $this->quotesInterval;
}
return $url . '&ignore=.csv';
}
/**
* Set the column names to extract from CSV data
* If you only need one field, specify it to avoid populating arrays with other fields that won't be used.
* Setting this to null will cause all columns to be returned
*
* Default is all fields, which is equivalent to: array(
* "Date", "Open", "High", "Low", "Close", "Volume", "Adj Close"
* )
*
* @param array $columns
*/
public function limitColumns($columns)
{
$this->limitColumns = $columns;
}
public function getLimitColumns()
{
return $this->limitColumns;
}
public function setQuotesInterval($quotesInterval)
{
$this->quotesInterval = $quotesInterval;
}
public function getQuotesInterval()
{
return $this->quotesInterval;
}
}
<?php
/**
* Get real-time/20 minute delayed stock information from Yahoo
*
* Based on http://www.gummy-stuff.org/Yahoo-data.htm
*/
class YahooRealTimeStockApi
{
const AFTER_HOURS_CHANGE_RT = 'c8';
const ANNUALIZED_GAIN = 'g3';
const ASK = 'a';
const ASK_RT = 'b2';
const ASK_SIZE = 'a5';
const AVERAGE_DAILY_VOLUME = 'a2';
const BID = 'b';
const BID_RT = 'b3';
const BID_SIZE = 'b6';
const BOOK_VALUE = 'b4';
const CHANGE = 'c1';
const CHANGE_AND_PERCENT_CHANGE = 'c';
const CHANGE_FROM_200_DAY_MOVING_AVERAGE = 'm5';
const CHANGE_FROM_50_DAY_MOVING_AVERAGE = 'm7';
const CHANGE_FROM_52_WEEK_HIGH = 'k4';
const CHANGE_FROM_52_WEEK_LOW = 'j5';
const CHANGE_IN_PERCENT = 'p2';
const CHANGE_PERCENT_RT = 'k2';
const CHANGE_RT = 'c6';
const COMMISSION = 'c3';
const DAYS_HIGH = 'h';
const DAYS_LOW = 'g';
const DAYS_RANGE = 'm';
const DAYS_RANGE_RT = 'm2';
const DAYS_VALUE_CHANGE = 'w1';
const DAYS_VALUE_CHANGE_RT = 'w4';
const DIVIDEND_OR_SHARE = 'd';
const DIVIDEND_PAY_DATE = 'r1';
const DIVIDEND_YIELD = 'y';
const EARNINGS_OR_SHARE = 'e';
const EBITDA = 'j4';
const EPS_ESTIMATE_CURRENT_YEAR = 'e7';
const EPS_ESTIMATE_NEXT_QUARTER = 'e9';
const EPS_ESTIMATE_NEXT_YEAR = 'e8';
const ERROR_INDICATION = 'e1'; // for symbol changed / invalid
const EX_DIVIDEND_DATE = 'q';
const FIFTY_DAY_MOVING_AVERAGE = 'm3';
const FIFTY_TWO_WEEK_HIGH = 'k';
const FIFTY_TWO_WEEK_LOW = 'j';
const FIFTY_TWO_WEEK_RANGE = 'w';
const FLOAT_SHARES = 'f6';
const HIGH_LIMIT = 'l2';
const HOLDINGS_GAIN = 'g4';
const HOLDINGS_GAIN_PERCENT = 'g1';
const HOLDINGS_GAIN_PERCENT_RT = 'g5';
const HOLDINGS_GAIN_RT = 'g6';
const HOLDINGS_VALUE = 'v1';
const HOLDINGS_VALUE_RT = 'v7';
const LAST_TRADE_DATE = 'd1';
const LAST_TRADE_PRICE_ONLY = 'l1';
const LAST_TRADE_RT_WITH_TIME = 'k1';
const LAST_TRADE_SIZE = 'k3';
const LAST_TRADE_TIME = 't1';
const LAST_TRADE_WITH_TIME = 'l';
const LOW_LIMIT = 'l3';
const MARKET_CAPITALIZATION = 'j1';
const MARKET_CAP_RT = 'j3';
const MORE_INFO = 'i';
const NAME = 'n';
const NOTES = 'n4';
const ONE_YR_TARGET_PRICE = 't8';
const OPEN = 'o';
const ORDER_BOOK_RT = 'i5';
const PEG_RATIO = 'r5';
const PERCENT_CHANGE_FROM_200_DAY_MOVING_AVERAGE = 'm6';
const PERCENT_CHANGE_FROM_50_DAY_MOVING_AVERAGE = 'm8';
const PERCENT_CHANGE_FROM_52_WEEK_HIGH = 'k5';
const PERCENT_CHANGE_FROM_52_WEEK_LOW = 'j6';
const PREVIOUS_CLOSE = 'p';
const PRICE_OR_BOOK = 'p6';
const PRICE_OR_EPS_ESTIMATE_CURRENT_YEAR = 'r6';
const PRICE_OR_EPS_ESTIMATE_NEXT_YEAR = 'r7';
const PRICE_OR_SALES = 'p5';
const PRICE_PAID = 'p1';
const P_OR_E_RATIO = 'r';
const P_OR_E_RATIO_RT = 'r2';
const SHARES_OWNED = 's1';
const SHORT_RATIO = 's7';
const STOCK_EXCHANGE = 'x';
const SYMBOL = 's';
const TICKER_TREND = 't7';
const TRADE_DATE = 'd2';
const TRADE_LINKS = 't6';
const TWO_HUNDRED_DAY_MOVING_AVERAGE = 'm4';
const VOLUME = 'v';
/**
* Fetch the latest data for a list of symbols
* @param array $symbols
* @param array $format - array of constants from this class
* @return array
*/
public function fetch($symbols, $format) {
$url = $this->makeUrl($symbols, $format);
curl_setopt_array($ch = curl_init(), array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
)
);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status != 200) {
throw new RuntimeException("Request to $url returned status $status");
}
return $this->parseStockCsv($symbols, $format, $response);
}
/**
* Convert CSV data into PHP vals
* @param string $csv - CSV from ichart.finance.yahoo.com
* @return array
*/
protected function parseStockCsv($symbols, $format, $csv)
{
$data = array();
$rows = explode("\n", trim($csv));
$colCount = count($format);
foreach ($rows as $rowIndex => $row) {
$cols = explode(',', $row);
$symbol = $symbols[$rowIndex];
if (count($cols) != $colCount) {
throw new RuntimeException('CSV not in expected format');
continue;
}
$symbolData = array();
foreach ($cols as $colIndex => $value) {
$symbolData[$format[$colIndex]] = trim($value,'" ');
}
$data[$symbol] = $symbolData;
}
return $data;
}
/**
* Create the URL for an API request
* @inheritdoc from {fetch}
*/
protected function makeUrl($symbols, $format)
{
return sprintf(
'http://download.finance.yahoo.com/d/?s=%s&f=%s',
urlencode(implode('+', $symbols)),
urlencode(implode('', $format))
);
}
}
<?php
/**
* Get stock symbol information from Yahoo iCharts.
* This uses the same API the finance charts on finance.yahoo.com use.
*/
class YahooStockApi
{
const PERIOD_DAYS = 'd';
const PERIOD_WEEKS = 'w';
const PERIOD_MONTHS = 'm';
const PERIOD_YEARS = 'y';
const TYPE_QUOTE = 'quote';
const TYPE_CLOSE = 'close';
const TYPE_SMA = 'sma';
/**
* Get data about a stock symbol to a period limit.
* @param string $symbol
* @param string $periodCount - The number of periods to return
* @param string $periodType - type of period, one of YahooStockApi::PERIOD_*
* @param string $dataType - one of YahooStockApi::TYPE_*
*/
public function fetch($symbol, $periodCount, $periodType = self::PERIOD_DAYS, $dataType = self::TYPE_QUOTE)
{
$url = $this->makeUrl($symbol, $periodCount, $periodType, $dataType);
curl_setopt_array($ch = curl_init(), array(
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true
)
);
$response = curl_exec($ch);
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
curl_close($ch);
if ($status != 200) {
throw new ErrorException("Request to $url returned status $status");
}
return $this->parseStockCsv($response);
}
/**
* Convert CSV data into a PHP data
*
* @param string $csv - CSV from ichart.finance.yahoo.com
* @return array
*/
protected function parseStockCsv($csv)
{
$data = array(
'series' => array()
);
$rows = explode("\n", trim($csv));
if (count($rows) < 2) {
throw new UnexpectedValueException('Input data not in a readable format');
}
foreach ($rows as $row) {
// Check if row has header
$colonPos = strpos($row, ':');
if ($colonPos !== false) {
$header = substr($row, 0, $colonPos);
$row = substr($row, $colonPos+1);
} else {
$header = false;
}
$cols = explode(',', $row);
if ($header) {
$data[$header] = $cols;
} else {
$data['series'][] = $cols;
}
unset($cols);
}
return $data;
}
/**
* Create the URL for an API request
* @inheritdoc from {fetch}
*/
protected function makeUrl($symbol, $periodCount, $periodType, $dataType)
{
return sprintf(
'http://chartapi.finance.yahoo.com/instrument/1.0/%s/chartdata;type=%s;range=%s;/csv',
urlencode($symbol),
$dataType,
$periodCount . $periodType
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment