google financeのAPIのメモ
株価データを取得するためにGoogle FinanceのAPIを触ってみたときのメモ。
このAPIはおそらくGoogle Financeの裏で使われているAPIで、公式のドキュメントが存在しない。
公式で提供しているAPIではなさそうなので、使い続けられるか怪しいかもしれない。
リクエスト
下記はトヨタの過去1年分の株価を取得する例 。
https://www.google.com/finance/getprices?p=1Y&i=86400&x=TYO&q=7203
p:データを取得する期間。年(Y)または日(d)で指定ができる
i:データの間隔。秒単位で指定する。上記では1日を(86400秒)指定している。
x:銘柄が取引される市場。TYOは東京証券取引所。
q:銘柄のコード。
またデータの取得を開始する日時をts(UNIXタイムスタンプ)で指定できる
https://www.google.com/finance/getprices?p=1Y&i=86400&x=TYO&q=7203&ts=1451574000
※2017/09追記
tsのパラメータがいつのまにか効かなくなっていた(常に現在の時刻がベースになる)
レスポンス
下記のようなレスポンスが返ってくる
EXCHANGE%3DTYO MARKET_OPEN_MINUTE=540 MARKET_CLOSE_MINUTE=900 INTERVAL=86400 COLUMNS=DATE,CLOSE,HIGH,LOW,OPEN,VOLUME DATA_SESSIONS=[MORNING,540,690],[AFTERNOON,750,900] DATA= TIMEZONE_OFFSET=540 a1455861600,5999,6086,5961,6050,14020000 3,5980,6044,5850,5906,11427200 4,5994,6070,5954,6065,10895500 5,5974,5989,5835,5900,10888100 6,5931,5967,5827,5881,15819200 7,5910,6006,5895,5960,12753100 10,5897,6062,5897,6001,18539900 11,5893,5935,5824,5853,11093200 ...
株価のデータは9行目から。どの列が何を表すのかはCOLUMNSを見れば分かる。
DATE 列は一行目がタイムスタンプになっており、2行目以降は最初のデータからiで指定した期間が何回経過したかを表している。
上記はデータの間隔が1日の例だが、dateの数値が飛んでいるのは土日などを挟んでいるため。
Date列の上限は999でそれを超えると一行目のようなタイムスタンプを挟んでまた1から数え始める。
また、終値は単にCLOSEとされているが、ちゃんと調整後終値 (株式分割を反映させた終値)が返ってくる。
サンプル
すごく雑に1年分の株価をcsvにして吐き出すものを書いた。
データ解析に向いていると聞いてpythonで書いてみたが、正直pythonよくわからずに書いているのでいいコードではないと思う。
elasticsearchでfilterとpost_filterを使ったらうまくいかなかった
termによる検索結果をさらに絞り込むためにpost_filterを使ったがうまくいかなかった。
post_filter
検索結果からさらに絞り込みを行うためのクエリで主にaggregationと組み合わせる。
このようなクエリを投げたが、 filterの結果から絞り込むのではなく、post_filterのみを適用した結果が返ってきてしまう。(2.2で確認。5以降では動くかも)
{ "filter":{ {"term":{"field1":"hoge"}}, }, "from":0, "size":100, "sort":{"createdAt":{"order":"desc"}}, "post_filter":{"not":{"term":{"field2":"huga"}}} }
queryと一緒に使うようにしたら動いた。
{ "query":{ {"term":{"field1":"hoge"}}, }, "from":0, "size":100, "sort":{"createdAt":{"order":"desc"}}, "post_filter":{"not":{"term":{"field2":"huga"}}} }
filterとqueryの違いについては公式のドキュメントで説明されている。 https://www.elastic.co/guide/en/elasticsearch/guide/current/_queries_and_filters.html
これによるとqueryはマッチした結果のスコアも一緒に知りたい時に使用するもので、filterは単にマッチするかどうか知りたい時に使うものらしい。 post_filterが使われる場合は単なる絞り込みではないと言うことなのだろうか。
python環境構築
今までデフォルトで入ってたpythonを使ってたけど、ちょっとpythonで作ってみたいものが出てきたので環境構築する。 といってもpyenv、virtualenvを入れただけ。 https://github.com/yyuu/pyenv#basic-github-checkout
pyenv
brew upgrade brew install pyenv
任意のバージョンをインストール
pyenv install 3.5.0
※コマンドラインツールがなくてエラーが出ることがあるらしい。3.5.0をインストールしようとしたら下記のエラーが出た
make: *** [Python/importlib_external.h] Trace/BPT trap: 5 make: *** Waiting for unfinished jobs.... make: *** [Python/importlib.h] Trace/BPT trap: 5
その場合以下を実行
xcode-select --install。
リハッシュしてグローバルに設定。
pyenv rehash pyenv global 3.5.0
pathの設定をする。下記はbashの例。
echo 'export PYENV_ROOT="$HOME/.pyenv"' >> ~/.bash_profile echo 'export PATH="$PYENV_ROOT/bin:$PATH"' >> ~/.bash_profile echo 'eval "$(pyenv init -)"' >> ~/.bash_profile
下記のコマンドでpyenv下のpythonが帰ってきたら完了。
which python
virtualenv
virtualenvを使うとプロジェクト毎に異なるバージョンのモジュールを使用できるようになる。
pipでインストールできる。
pip install --upgrade pip pip install virtualenv
下記のようにプロジェクト用の仮想環境を作り、activateする
virtualenv [仮想環境の名前] source [仮想環境の名前]/bin/activate
これでグローバルではなく、プロジェクト毎の環境にパッケージがインストールされるようになる。
iosアプリの画面遷移をプログラムから呼び出す
プログラムから任意のタイミングでsegueを呼び出したかったが、やり方をググっても何故か中々ヒットしなかったのでメモしておく。
ついでにViewControllerを指定して画面遷移する方法もメモしておく。
Segueを使用して遷移する場合
事前準備
StoryBoard上で、ViewController間をつなぐsegueを作っておく。 作ったsegueのidentifierはちゃんと埋めておく。
コード
以下のコードでsegueを呼び出せる。
performSegue(withIdentifier: "segueのidentifier", sender: self)
またsegueが呼ばれた際の遷移前処理はVieController内で以下のように書ける。
override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // やりたい処理 }
遷移先の画面に何か渡したい時には下記のように利用するらしい。
//segueを呼び出す処理 func callSegue( ) { let message = "hogehoge" performSegue(withIdentifier: "targetSegue", sender: message) } //次のviewのcontrollerに値を渡す override func prepare(for segue: UIStoryboardSegue, sender: Any?) { if (segue.identifier == "targetSegue") { let nextViewController : NextViewController = segue.destination as! NextViewController nextViewController.item = sender as! String } }
ViewControllerを指定して遷移する場合
プログラムから画面遷移する場合、こっちの方がよく使われるかも
事前準備
storyboard上で遷移先のViewControllerのIdentifierを設定しておく
コード
let storyboard = UIStoryboard(name: "Main", bundle: nil) let nextView = storyboard.instantiateViewController(withIdentifier: "nextViewController") let navi = UINavigationController(rootViewController: nextView) navi.modalTransitionStyle = .coverVertical self.present(navi, animated: true, completion: nil)
iosアプリの起動時の画面をコントロールする
ログイン状態など条件に応じてrootViewControllerを切り替えたかった。 ちょっと調べたところUIWindowのrootViewControllerを設定すれば良いらしい。
var window: UIWindow? let storyboard = UIStoryboard(name: "Main", bundle: nil) if (flag) { let mainViewController = storyboard.instantiateViewControllerWithIdentifier("MainViewController") let navigationController = UINavigationController(rootViewController: mainViewController) self.window!.rootViewController = navigationController } else { let loginViewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController") let navigationController = UINavigationController(rootViewController: loginViewController) self.window!.rootViewController = navigationController }
しかし、このようにすると最初のviewが表示される前に一瞬真っ暗な画面が表示されてしまった。
原因はデフォルトのrootViewControllerが設定されておらず、上記のコードの実行前に未設定のrootViewControllerを表示しようとしているためだった。
ひとまずlaunch中に表示するviewを作りデフォルトのrootViewControllerとして設定することで見かけ上は解決した。
launch前にrootViewControllerを設定する方法があればそれを使いたいが、ググっても見つからなかった。 より良い方法が見つかったら追記したい。
NSUserDefaultsによる永続化
iosアプリを停止させても消したくない情報を永続化する方法を調べた。 永続化の方法としてはNSUserDefaults、NSKeyedArchiver、プロパティリスト、CoreDataなどの方法があるらしい。
CoreDataはちょっとしたデータの保存には大変すぎる。 NSKeyedArchiverはシリアライズしたデータをファイルとして保存する方法で、保存する場所を指定する必要がある。 プロパティリストはkey、valueを指定して.pistファイルに情報を保存する方法で、アプリの設定などを扱っていることが多い。
NSUserDefaultsは保存する場所の指定などが不要で、最も手軽そうだったので今回はこれを使う。
使い方
let userDefaults = NSUserDefaults.standardUserDefaults() //データを保存する self.userDefaults.setObject("hoge", forKey: "huga") //データを取り出す let authData = self.userDefaults.objectForKey("huga") as? String
かなり手軽にデータの保存ができるが上記のコードをそのまま使うと、キーや型をベタ書きすることになり管理がし難い。 ググってみたところ、wrapperを用意して使うのが良さそう。 データがある程度複雑ならプロパティリストやCoreDataを使用するべきだと思う。
また、NSUserDefaultsは消える恐れがあるらしいく、消えてもいいデータに限ってしか使えないようだ。 今回のケースでは問題なかったけど、アプリ中断時に開いていたページの情報とか、トークンのような揮発しても問題ないなものにしか使うべきじゃないのかもしれない。
UIImageをリサイズする
IOSアプリで画像を表示する時、画像サイズに依存せずにViewいっぱいに表示したい。
その際、縦長の画像が縦に押しつぶされた状態になるなど、画像が変にリサイズされて歪んでしまうのは避けたいというコード。
一旦リサイズしさらにトリミングするという処理をしているため、無駄が多いように思える。
もっといい方法がありそうだけ、swift初心者なので思いつかなかった。。。
func fixImageSize(image: UIImage, width: CGFloat, height: CGFloat) -> UIImage{ let widthRatio = width / image.size.width let heightRatio = height / image.size.height let (resizeWidth, resizeHeight) = heightRatio > widthRatio ? (image.size.width * heightRatio, height):(width, image.size.height * widthRatio) let (x, y) = ((resizeWidth - width) / 2, (resizeHeight - height) / 2) let size: CGSize = CGSize(width: resizeWidth, height: resizeHeight) UIGraphicsBeginImageContext(size) image.drawInRect(CGRectMake(0, 0, resizeWidth, resizeHeight)) let resizeImage = UIGraphicsGetImageFromCurrentImageContext() UIGraphicsEndImageContext() let trimRect = CGRectMake(x, y, width, height) let trimRef = CGImageCreateWithImageInRect(resizeImage.CGImage, trimRect) let trimImage = UIImage(CGImage: trimRef!) return trimImage }