Web公開情報から株価を自動取得しFlotでチャート化
September 13, 2014 – 11:02 amYahoo Finance上に公開されている株価を自動的に抽出し、データベース上に蓄積、そして画像描画ソフトFlotを用いてチャート化する一連の手続きを実現してみた。
Webからの公開情報からのデータの抽出方法などについて、これまで本ブログ上に記載してきているものを用いている。今回は、これらの要素技術を組み合わせて、どの程度のことが可能になるか、実際に試してみた。
今回作成したPHP、JavaScriptなどのソースをメモしておいた。
株価の日足チャート表示ツール: 今回作成した株価日足チャート描画用ツールを以下に示す:
Flot Sample: Daily Stock
銘 柄: 日 付:
— Data at the Cursored Position —————————–
Label: stock_data Time: hh:mm Value:
このツール上では、データベース上に蓄積した株価データ(1分おきにYahoo Financeの株価を抽出蓄積)を銘柄、日付を指定し検索抽出。そして[DRAW LINE]ボタンを押釦することによって、指定した株価データの日足のチャートを描画表示する。2銘柄までの株価日足を表示できる。異なる日時の株価チャートをトータル5本まで重ねて表示できる。描画した株価チャートは[RESET LINE]ボタンを押釦することによりリセットできる。また、チャートのライン上にカーソルをあてるとチャート下方にそのポイントに対応する銘柄、日時そして株価が表示される。
チャート図作成に用いている各種ソースを以下に掲げる。なお、ソースの中身についての説明は割愛。
HTMLソース:
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="UTF-8"> <title>Checking Flot</title> <!--[if lt IE 9]> <script src="http://html5shiv.googlecode.com/svn/trunk/html5.js"></script> <![endif]--> <script type="text/javascript" src="./jquery.js"></script> <script type="text/javascript" src="./flot/jquery.flot.js"></script> <script type="text/javascript" src="./flot/jquery.flot.axislabels.js"></script> <link rel="stylesheet" href="http://ajax.googleapis.com/ajax/libs/jqueryui/1.10.1/themes/base/jquery.ui.all.css" /> <script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js"></script> <style> .ui-datepicker { font-size: 70%; } </style> </head> <body> <p>Flot Sample: Daily Stock</p> <div class="data_box"> <span class='stocknm'> 銘 柄: </span> <select name="sample"> <option value='1'></option> <option value='2'>三菱重工業</option> <option value='3'>三菱自動車</option> <option value='4'>三菱UFJ</option> <option value='5'>全日空</option> <option value='6'>東芝</option> <option value='7'>ソニー</option> <option value='8'>パナソニック</option> <option value='9'>味の素</option> <option value='10'>旭化成</option> <option value='11'>大日本印刷</option> <option value='12'>日立製作所</option> <option value='13'>三菱マテリアル</option> <option value='14'>シャープ</option> <option value='15'>東京電力</option> <option value='16'>トヨタ自動車</option> <option value='17'>日本郵船</option> <option value='18'>三井不動産</option> </select> <span class='seldate'> 日 付: </span> <input type='text' name='date' id='datepicker'> <span class='drawbutton'><input type="button" value="DRAW LINE " /></span> <span class='resetbutton'><input type="button" value="RESET LINE" /></span> </div> <div id="placeholder" style="width:550px;height:350px;"></div> <p>-- Data at the Cursored Position -----------------------------</p> <p>Label : <span id="label"> stock: date </span> Time : <span id="time">hh:mm</span> Value:<span id="value"></span></p> <script type="text/javascript" src="./stock_daily.js"></script> </body> </html>
JavaScript stock_daily.js ソース:
$(document).ready(function() { var date_val = ''; var istock = ''; var istep; var istep_max; var color = ['#3366CC', '#DC3912', '#FF9900', '#109618', '#990099']; $('select[name="sample"] option:first').prop('selected',true); $("#placeholder").css({'font-size':'12px','line-height':'1.1em' }); flot_initialize(); var previousPoint = null; $("#placeholder").bind("plothover", function (event, pos, item ) { if( item ) { if( previousPoint != item.dataIndex ) { previousPoint = item.dataIndex; // item.series.istep $("#label").text( item.series.dummy ); $("#time").text( timetohm( item.datapoint[0] ) ); $("#value").text( item.datapoint[1] ); } else { previousPoint = null; } } }); $(".drawbutton").click( function() { var no_is = istock_array.length; var no_dt = date_array.length; var s_flag= false; if ( no_is != no_dt ) { alert ('something wrong! array length unbalance'); return; } if ( no_is == 0 && date_val=='' ) return; if ( no_is != 0 ) { for (k_step=0; k_step<no_is; k_step++ ) { if( date_val==date_array[k_step] && istock==istock_array[k_step]) { if(date_val==str_today()) { istep = k_step+1; draw_line( date_val, istock ); } s_flag = true; } } } if( !s_flag || no_is==0 ) { istep=no_is+1; date_array.push( date_val ); istock_array.push( istock ); draw_line (date_val, istock ) } }); $(".resetbutton").click( function() { $('#datepicker').val(''); date_val = ''; $('select[name="sample"] option:first').prop('selected',true); flot_initialize(); $("#label").text(' stock: date ' ); $("#time").text('hh:mm' ); $("#value").text(' ' ); }); $('select[name="sample"]').change(function () { istock = $('select[name="sample"]').val(); label_text = $('[name="sample"] option:selected').text(); }); $('#datepicker').datepicker( { dateFormat: "yy-mm-dd", onSelect: function(dateText, inst) { date_val = dateText; } }); function flot_initialize() { options = {xaxis : { min: new Date('2011/01/01 18:00').getTime(), max: new Date('2011/01/02 00:00').getTime(), mode : "time", timeformat: "%h:%M", }, yaxes: [ { font: { color:"#545454" }, //axisLabel: "", axisLabelUseCanvas: true, axisLabelFontSizePixels: 10, axisLabelFontFamily: "Verdana, Arial, Helvetica, Tahoma, sans-serif", axisLabelPadding: 5 }, { position: "right", //axisLabel: "", axisLabelUseCanvas: true, axisLabelFontSizePixels: 12, axisLabelFontFamily: "Verdana, Arial, Helvetica, Tahoma, sans-serif", axisLabelPadding: 5 } ], grid: { hoverable: true, borderWidth: 1 }, legend: { labelBoxBorderColor: "none", margin: [0,0], position: "se" } }; mydata = [ { data: [], lines:{show:true, fill: false }, points:{show:false, fillColor: '#3366CC'}, color: "#3366CC", yaxis: 1 }, {}, {}, {}, {}, {}, {}, {}, {}, {} ]; istep_max = mydata.length /2 ; istep = 0; istock_2 = 0; istock_array = []; date_array = []; $('#datepicker').val(''); $.plot($("#placeholder"), mydata, options); } function draw_line( date, company ) { if ( istep > istep_max ) { alert('Too many lines attempt to draw!'); return; } if ( istock_array[istep-1] == istock_array[0] ) { yaxis_local = 1; } else { if ( istock_2 != 0 && istock_array[istep-1] != istock_2 ) { alert('no of stock over 2! not draw line') istock_array.pop(); date_array.pop(); return; } yaxis_local = 2; istock_2 = istock_array[istep-1]; } var is01 = 2*(istep-1); var is02 = 2*(istep-1) + 1; var color_local = color[istep-1]; var param = new Object(); param.ampm = 'AM'; param.date = date; param.company = company; $.get("/scripts/stock_daily/get_price.php", param, function( json ){ mydata[is01].data = eval("("+json+")"); if(mydata[is01].data.length <= 0 ) { alert('no data stored!'); istock_array.pop(); date_array.pop(); return; } mydata[is01].label = param.date + ':'+label_text; mydata[is01].dummy = mydata[is01].label; // mydata[is01].istep = istep; mydata[is01].color = color_local; mydata[is01].yaxis = yaxis_local; options.yaxes[yaxis_local-1].axisLabel=label_text; $.plot($("#placeholder"), mydata, options); }); param.ampm = 'PM'; $.get("/scripts/stock_daily/get_price.php", param, function( json ){ mydata[is02].data = eval("("+json+")"); var idt = mydata[is02].data.length - 1; mydata[is02].data[idt][0] = mydata[is02].data[idt-1][0]; mydata[is02].data[idt][1] = mydata[is02].data[idt-1][1]; mydata[is02].dummy = param.date + ':'+label_text; mydata[is02].color = color_local; mydata[is02].yaxis = yaxis_local; $.plot($("#placeholder"), mydata, options); }); } function timetohm( time ) { var dobj = new Date( time ) ; var utc = new Date( dobj.getTime() + dobj.getTimezoneOffset()*60000 ) var hh = utc.getHours(); var mm = utc.getMinutes(); if ( mm < 10 ) mm = '0' + mm; var x_time = hh + ':' + mm; return x_time; } function str_today() { var now = new Date(); var yy = now.getFullYear(); var mm = now.getMonth() + 1; if ( mm<10 ) mm='0'+mm; var dd = now.getDate(); if ( dd<10 ) dd='0'+dd; // return yy+'-'+mm+'-'+dd; } });
データベースからの株価取得用PHP get_price.php ソース:
<?php header("Cache-Control: no-cache, must-revalidate"); $date = $_GET["date"]; $ampm = $_GET["ampm"]; $company = $_GET["company"]; getPrice01( $date, $ampm, $company ) ; //*************************************************************************************************** //* Function getPrice01(): //* //*************************************************************************************************** function getPrice01($date,$ampm,$company){ require_once(dirname(__FILE__) . "/StockPrice.php"); require_once(dirname(__FILE__) . "/StockPriceDao.php"); $date_head = "new Date('2011/01/01 "; $date_tale = "').getTime()"; $strng = "[\n"; $stockpricedao = new StockPriceDao(); $ret_array = $stockpricedao->getStockPriceByDate( $date ); $size_of_array = sizeof( $ret_array ); for ( $i=0; $i<$size_of_array; $i++ ) { if( $ret_array[$i]->getCompany_id()==$company ) { $time = $ret_array[$i]->getTime(); if ( date('A',strtotime($time)) === $ampm ) { $time = $time . " +9 hours"; $price = $ret_array[$i]->getPrice(); $price = str_replace(",","",$price); $strng = $strng."[".$date_head.date('G:i',strtotime($time)).$date_tale.",".$price."],\n"; } } } if( strlen($strng) != 0 ) { $strng = rtrim($strng); $strng = rtrim($strng, ","); } $strng = $strng . "]"; echo $strng; $stockdao = NULL; } ?>
株価データベースSQL文ソース:
DROP TABLE IF EXISTS company; DROP TABLE IF EXISTS stock_price; CREATE TABLE company ( id INTEGER(8) PRIMARY KEY AUTO_INCREMENT, stock_id varchar(128) NOT NULL, name varchar(256) NOT NULL ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; CREATE TABLE `stock_price` ( id INTEGER(8) PRIMARY KEY AUTO_INCREMENT, time TIMESTAMP, turnover varchar(128), price varchar(128), company_id INTEGER(8) ) ENGINE=MyISAM AUTO_INCREMENT=1 DEFAULT CHARSET=utf8; ALTER TABLE stock_price ADD FOREIGN KEY (company_id) REFERENCES company(id);
Yahoo Finance からの株価抽出PHP get_realtime_price.php ソース:
<?php error_reporting(E_ALL & ~E_NOTICE); require_once(dirname(__FILE__) . "/StockPriceDao.php"); require_once(dirname(__FILE__) . "/StockPrice.php"); require_once(dirname(__FILE__) . "/Company.php"); require_once(dirname(__FILE__) . "/finance.php"); if ( (time()>=mktime(9,0,0) AND time()<mktime(11,31,0)) OR (time()>=mktime(12,30,0) ANd time()<mktime(15,1,0)) ) { $stockpricedao = new StockPriceDao(); // Get RealTimeStockPrices from Yahoo Finance for the Company registered. // $ret_array = $stockpricedao->getCompany_All(); $nr = sizeof($ret_array); for ($i=0; $i<$nr; $i++) { $stock_codes[$i] = $ret_array[$i]->getStock_id(); } $ret_array = getStockPrices($stock_codes); // set stock_value on STOCKDB $time = date("Y-m-d H:i:s"); for ($n=0; $n<$nr; $n++) { // $stock_id = $stock_codes[$n]; $stock_id = $ret_array[0][$n]; $company_id = $stockpricedao->getCompany_id($stock_id); $stock_price = new StockPrice(); $stock_price->setTime($time); $stock_price->setTurnover($ret_array[2][$n]); $stock_price->setPrice($ret_array[3][$n]); $stock_price->setCompany_id($company_id); $stockpricedao->setStockPrice($stock_price); $stock_price = null; } $stockpricedao = null; } ?>
株価自動取得のための crontab コマンド:
* * * * 1-5 /usr/bin/php /home/sample_dir/get_realtime_price.php > /dev/null 2>&1
その他PHPソース:
StockPrice.php:
<?php class StockPrice { private $id = ""; private $time = ""; private $price = ""; private $company_id = ""; function __construct() { // echo "Initialized !\n"; } function __destruct() { // echo "Destructed !\n"; } public function __toString() { return (string) ($this->id . "\t" . $this->time . "\t" . $this->turnover . "\t" . $this->price . "\t" . $this->company_id ); } public function getId() { return $this->id; } public function setId($id) { $this->id = $id; } public function getTime() { return $this->time; } public function setTime($time) { $this->time = $time; } public function getTurnover() { return $this->turnover; } public function setTurnover($turnover) { $this->turnover = $turnover; } public function getPrice() { return $this->price; } public function setPrice($price) { $this->price = $price; } public function getCompany_id() { return $this->company_id; } public function setCompany_id($company_id) { $this->company_id = $company_id; } } ?>
StockPriceDao.php:
<?php Class StockPriceDao { private $mysqli = null; //Constructor function __construct() { $this->connect(); } //Destructor function __destruct() { $this->disconnect(); } //Connect to MySQL server private function connect() { if( is_null($this->mysqli)) { $this->mysqli = new mysqli("localhost", "user_name", "password", "DB_NAME" ); $this->mysqli->query("SET NAMES utf8"); if(mysqli_connect_errno()) { die("Failed to connect to MySQL server<br></br> Reason:" . mysqli_connect_error()); } } } //Disconnect from MySQL server private function disconnect() { is_null($this->mysqli) or $this->mysqli->close(); } public function getCompany_All() { is_null($this->mysqli) and $this-connect(); // $sql = "SELECT * FROM company"; $result = $this->mysqli->query($sql); $company_array = array(); while($row = $result->fetch_array(MYSQL_ASSOC)) { $company = new Company(); $company->setId($row["id"]); $company->setStock_id($row["stock_id"]); $company->setName($row["name"]); $company_array[] = $company; } $result->close(); return $company_array; } public function getCompany_id($stock_id) { is_null($this->mysqli) and $this->connect(); // $sql = "SELECT id FROM company WHERE stock_id = '" . $stock_id . "'"; $result = $this->mysqli->query($sql); if(!$row=$result->fetch_array(MYSQLI_ASSOC)) break; //DEBUG echo " stock_id company_id = " . $stock_id . " " . $row["id"] . "\n"; return $row["id"]; } public function setStockPrice($stock_price) { is_null($this->mysqli) and $this->connect(); // $sql = "INSERT INTO stock_price values(''," . "'" . $stock_price->getTime() . "'," . "'" . $stock_price->getTurnover() . "'," . "'" . $stock_price->getPrice() . "'," . "'" . $stock_price->getCompany_id() . "')"; $this->mysqli->query($sql); } public function getStockPriceByDate( $date ) { is_null($this->mysqli) and $this->connect(); // $sql = "SELECT * FROM stock_price WHERE DATE(time)='" . $date . "'"; $result = $this->mysqli->query($sql); $stockprice_array = array(); while($row = $result->fetch_array(MYSQLI_ASSOC)) { $stockprice = new StockPrice(); $stockprice->setId($row["id"]); $stockprice->setTime($row["time"]); $stockprice->setTurnover($row["turnover"]); $stockprice->setPrice($row["price"]); $stockprice->setCompany_id($row["company_id"]); $stockprice_array[] = $stockprice; } $result->close(); return $stockprice_array; } } ?>
Company.php:
<?php class Company { private $id = ""; private $stock_id = ""; private $name = ""; function __construct() { // echo "Initialized !\n"; } function __destruct() { // echo "Destructed !\n"; } public function __toString() { return (string) ($this->id . "\t" . $this->stock_id . "\t" . $this->name ); } public function getId() { return $this->id; } public function setId($id) { $this->id = $id; } public function getStock_Id() { return $this->stock_id; } public function setStock_Id($stock_id) { $this->stock_id = $stock_id; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } } ?>
finance.php:
<?php function getStockPrices($stockcodes) { // $nr = sizeof($stockcodes); $str_codes = ""; for ( $k=0; $k<$nr; $k++ ) { // echo $stockcodes[$k] . "\n"; $str_codes = $str_codes . urlencode($stockcodes[$k]); if ( $k < $nr-1 ) $str_codes = $str_codes . "+"; } $url ="http://info.finance.yahoo.co.jp/search/?query=" . $str_codes . "&ei=UTF-8&view=l1"; // $ch = curl_init(); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $feed = curl_exec($ch); curl_close($ch); // $stock_value = array(); // 株価コード //以下に修正 $n = preg_match_all("/code=([^<]+)\"><strong>/",$feed, $cell, PREG_PATTERN_ORDER); $n = preg_match_all("/code=([^<]+)\" data-/",$feed, $cell, PREG_PATTERN_ORDER); if ( $n == $nr ) for( $i= 0; $i<$n; $i++ ) $stock_value[0][] = $cell[1][$i]; else return -1; // 日付/時間 出来高 $n = preg_match_all("/<td nowrap>\n([^<]+)<\/td>/s", $feed, $cell, PREG_PATTERN_ORDER); if ( $n == $nr*2 ) { for($i= 0;$i<$n;$i=$i+2) { $stock_value[1][] = $cell[1][$i]; $stock_value[2][] = $cell[1][$i+1]; } } else return -1; // 株価 $n = preg_match_all("/<td><strong>([^<]+)<\/strong><\/td>/s", $feed, $cell, PREG_PATTERN_ORDER); if ( $n == $nr ) for( $i= 0; $i<$n; $i++ ) $stock_value[3][] = $cell[1][$i]; else return -1; // 銘柄 $n = preg_match_all("/<strong class=\"ico.*\">([^<]+)<\/strong>/",$feed, $cell, PREG_PATTERN_ORDER); if ( $n == $nr ) for( $i= 0; $i<$n; $i++ ) $stock_value[4][] = $cell[1][$i]; else return -1; // return $stock_value; } ?>
3 Responses to “Web公開情報から株価を自動取得しFlotでチャート化”
2014年11月4日に、Yahoo Finance の仕様が変更された。
これに伴い、finance.php のなかの関数 getStockPrices()の一行を以下のように変更した:
変更前:
変更後:
By yama on Nov 5, 2014