私と私の猫の他は誰でも隠し事を持っている

野良プログラマ発、日々のアウトプット

VSCode + Remote FS で netrw っぽくリモートマシンのファイルを編集する

仕事柄、ちょこちょこっとリモートマシンのファイルを確認・編集したいことが多いのですが、そんな時は主に MacVim + netrw で ssh 越し作業してます。ローカル・リモートともに特別な設定無しで、ファイラでディレクトリを渡り歩きながらの操作が楽ちんなのでつ。

こういう作業を最近流行りの AtomVSCode 等の Electron ベースなエディタで行おうとすると、お薦めとされている拡張機能のほとんどがリモートマシンに rmate が必要だったりプロジェクト毎に諸元設定しなきゃだったりで、多機能ではあっても小回りが効かないのが不満でした。そんな中、みつけたのが Remote FS という VSCode 拡張機能。これは設定で、

"remotefs.remote": {
  "dev": {
    "scheme": "sftp",
    "host": "host",
    "username": "username",
    "rootPath": "/path/to/somewhere"
  }
}

等とリモートサーバ諸元を設定しておけば、あとはコマンドラインから

code --folder-uri sftp://dev/

と叩くだけで下記のように、エクスプローラにファイラが表示されてリモートマシンのディレクトリやファイルを直に操作できます。イイと思いません?

f:id:mariyudu:20181201165526p:plain
Remote FS

残念なのは、~/.ssh/config に登録したホスト情報が使えないことでしょうか。ていうか設定さえも面倒なので更に netrw っぽく、コマンドラインから URI を叩くだけで済むようにならないかなーなんて思います。

CloudWatch コンソールログイン通知でメールが来ない件

小ハマり系小ネタでつ。先日 AWS セキュリティ強化の一環で今さらですが、コンソールログインのメール通知を

dev.classmethod.jp

を読んで実施したのですが、ログインしてみてもさっぱりメールが来ない。CloudTrail にはちゃんとイベントが記録されているのに… と、イベントをよく見たらリージョンが us-east-1 となっている?

f:id:mariyudu:20181115153633p:plain:w500

どゆこと? と、「ConsoleLogin イベント リージョン」あたりでぐぐるとまたもクラスメソッドさんのページがヒット(助かってますぅ)。

dev.classmethod.jp

これによるとコンソールのリージョン設定やサインイン時の URL に依存するようです。当方コンソールは東京にセットしてありますが、サインイン URL を https://ap-northeast-1.console.aws.amazon.com/ec2/v2/home にしても https://s3.console.aws.amazon.com/s3/home にしても CloudTrail にはリージョンは全て us-east-1 で記録されてます。うーむ…

結局、理由はよく分からないまま、メール通知設定での SNS トピック作成および CloudWatch ルール作成の時だけリージョンをバージニア北部に変えてやりなおしたところ、無事メールが届くようになりましたとさ。ちゃんちゃん。

気象庁の各地気圧データから箱ひげ図を描いてみる

昨年末あたりから Jupyter Notebook を触り始めてすっかりハマってます。Python の豊富な DS ライブラリを使ってのデータ加工・集計・分析・可視化等を Web U/I 上でトライ&エラーできる柔軟さが素晴らしすぐる。発想にまかせて自由気ままにデータいじりするにはまだまだスキル不足なので、何かしらお題を設けてはアウトプットするルーティンワークを続けていこうかと。今回はその一環で、気象庁サイトから日本各地の1年間の気圧データ(CSV)から、月単位の箱ひげ図を作成して、各地毎の気圧変動特性を概観してみようという試み。

気圧データ取得は、気象庁過去の気象データ・ダウンロードにて、下記要領で。

  1. 「地点を選ぶ」にて、地図上から目的の都市をクリックしてひとつ選択。
  2. 「項目を選ぶ」にて、データの種類=時別値・項目=現地気圧、と選択。
  3. 「期間を選ぶ」にて、連続した期間で表示する(2017年1月1日〜2017年12月31日)と選択。
  4. CSV ファイルをダウンロード」をクリック。

今回は、札幌・仙台・東京・名古屋・大阪・松山・福岡・那覇の8地点をそれぞれデータ取得しました。それを Jupyter からアップロードして、下記コードを実行!

import pandas as pd
import matplotlib.pyplot as plt
%matplotlib inline

# データファイル
datafiles = ['kiatsu-sapporo.csv', 'kiatsu-sendai.csv', 'kiatsu-tokyo.csv', 'kiatsu-nagoya.csv', 'kiatsu-osaka.csv', 'kiatsu-matsuyama.csv', 'kiatsu-fukuoka.csv', 'kiatsu-naha.csv']

# データファイル数に合わせた大きさのグラフ領域を作成
rows = len(datafiles)
fig = plt.figure(figsize=(8,3*rows), dpi=100)
yrange = [960, 1040]

for (n, datafile) in enumerate(datafiles):
    # CSV を読み出してデータフレーム作成(1列目を datetime 型のインデックスにするのがミソ)
    df = pd.read_csv(datafile, skiprows=5, header=None, names=['datetime', 'pressure','dmy1','dmy2'], index_col=0, parse_dates=True)
    datas = []
    # 月毎の2次元配列に纏める
    for m in range(1, 13):
        datas.append(df[df.index.month == m].pressure)
    # 箱ひげ図プロット
    ax = fig.add_subplot(rows, 1, n+1)
    bp = ax.boxplot(datas, whis='range') # 外れ値描画なしで
    plt.title(datafile)
    plt.grid()
    plt.subplots_adjust(hspace=0.4)
    plt.ylim(yrange)

結果、作画した箱ひげ図はこんな感じ↓。matplotlib の boxplot() はデフォルトだと、四分位範囲 ✕ 1.5 外の値を外れ値にするようなのですが、今回のデータは偶然誤差の類を考慮しなくても良いかなと思い、ひげ長をレンジ全体にしました。
f:id:mariyudu:20180516205938p:plain

家族に頭痛持ちがいて低気圧の日は辛そうなので、日本のどの辺が気圧変動が穏やかなのかなー、と思ったのが動機でした。九州以北は概ね冬季の変動が大きいのに較べて沖縄は比較的穏やかそう。どこも10月にレンジが広いのは台風のせいですが、それでも東京が沖縄より振れ幅が大きいのは意外でした。

さーて次は何をやろうかな…

コンパクトな PHP テンプレートエンジン、Latte を使ってみた

Yet Another PHP Template Engine な話。とある PHP システムを作っていて、動的に与えられるテンプレート構文にデータを適用させて、断片的な HTML コードを生成させたい、という要件がありまして、

  • Twig や Blade だと大袈裟というか、かさばりそうなのでなるだけコンパクトなやつ
  • ちゃんとメンテされてて PHP7 でも安心して使えそうなの
  • コードがモダンで、PHP5.4 未満な残滓が無いと嬉しい

なテンプレートエンジンを探してたら Latte という OSS プロダクトを発見。どうやらこれ、Nette という MVC フレームワークの構成ライブラリですが、単体でも問題なく使えるようです。

Latte - amazing template engine for PHP | Nette Framework

で、上記ページを読みながら評価してみました。インストールは compser いっぱつ。

composer require latte/latte

テスト用にこんなスクリプトを書いてみたです(↓)。

<?php
require 'vendor/autoload.php';

$duos = [
	'Simon & Garfunkel' => ['Paul Simon', 'Art Garfunkel'],
	'Peter & Gordon' => ['Peter Assher', 'Gordon Waller'],
];

$template = '<dl n:foreach="$duos as $name => $duo">
	<dt>{$name}</dt>
	<dd>
		<ul>
			<li n:foreach="$duo as $member">{$member}</li>
		</ul>
	</dd>
</dl>';

$latte = new Latte\Engine;
$latte->setLoader(new Latte\Loaders\StringLoader([
	'main' => $template
]));
$latte->render('main', ['duos' => $duos]);

はい、ちゃんとレンダリングされました(↓)。

<dl>
	<dt>Simon &amp; Garfunkel</dt>
	<dd>
		<ul>
			<li>Paul Simon</li>
			<li>Art Garfunkel</li>
		</ul>
	</dd>
</dl><dl>
	<dt>Peter &amp; Gordon</dt>
	<dd>
		<ul>
			<li>Peter Assher</li>
			<li>Gordon Waller</li>
		</ul>
	</dd>
</dl>

デフォルトで HTML エンティティ変換されてるのがイイですね。テンプレートでおや?と思うのが、Vue.js テンプレートのディレクティブみたいな書き方ができること。これを Latte ではマクロと呼ぶようです。分岐やループは

<?php
$template = '{foreach $duos as $name => $duo}
	<dl>{$name}</dl>
	<dd>
		<ul>
			{foreach $duo as $member}
				<li>{$member}</li>
			{/foreach}
		</ul>
	</dd>
{/foreach}';

のように Twig っぽい書き方もできるのすが、マクロを使うことでよりコンパクトなテンプレートが書けますね。

それから Latte の特色として、コンテキスト・アウェアネス指向な件。要は、レンダリングの際に埋め込み場所に応じたセキュリティ対策が考慮されている、ということらしいです。

<?php
$latte->setLoader(new Latte\Loaders\StringLoader([
	'main' => '<p>param is {$param}</p>
<a href="{$param}">Click Here</a>'
]));
$latte->render('main', ['param' => "localtion.href='http://google.com'"]);

を実行すると、

<p>param is localtion.href='http://google.com'</p>
<a href="">Click Here</a>

となり、p タグ内には展開される文字列が、a タグの href 属性内ではオミットされて、スクリプトインジェクションを抑止できます。どうですか? 自分はカナーリ気に入りました、Latte。

PHP Trader 関数を使ってみる - ATR トレーリングストップの巻

忘れた頃にやってくる PHP Trader 関数シリーズ。今回は ATR (真の値幅の移動平均)を算出する trader_atr() を取り上げるのですが、ただ計算させるだけじゃつまらないので計算結果を元に損切りラインを導出して売却指示をさせてみます。所謂「ATR トレーリングストップ」ですね。

ATR トレーリングストップについては言葉はよく聞くのですが、計算ロジックまで明示された情報は少ないのですな。あれこれ調べた結果、保持中銘柄の損切りラインは以下のように算出するようです(間違ってたらすんまそん)。

  • 当該銘柄について、直近の所定日数(10日くらいが定石みたい)の真の値幅平均(以下 ATR)をとる。
  • 当該銘柄について、保持期間中の最高値を得る。
  • 最高値 - ATR × 係数をその日の損切りラインとする(係数は3〜4くらいが定石みたい)。

という訳で以下、サンプルコードです。いつものように k-db.com さんから頂いた今年の日経平均日足データを元に、ATR が算出可能になった日を買付日と想定します。んで、以後の日付毎に「終値損切りラインを割ったら翌日に売却」のルールで指示を出力する、というものです。

<?php
define('ATR_DAYS', 10); // ATR 日数
define('ATR_N', 4); // ATR 係数

// CSV ファイルから株価データを読み込んで、配列に格納
$data = file('indices_I101_1d_2017.csv', FILE_IGNORE_NEW_LINES);

//見出し行を省く
unset($data[0]);

// 各行のカンマ区切りを配列に分割
$data = array_map(function($item){ return explode(',', $item); }, $data);

// 日付昇順でソート
usort($data, function($a, $b){ return ($a[0] < $b[0]) ? -1 : 1; });

// ATR 算出
$atrs = trader_atr(
	array_column($data, 2), // 高値
	array_column($data, 3), // 安値
	array_column($data, 4), // 終値
	ATR_DAYS
);

// トレーリングストップによる売買指示を出力
$highest = 0;
$losscutLine = null;
echo "日付,最高値,終値,損切りライン,指示\n";
foreach ($atrs as $idx => $atr) {
	$price = $data[$idx];
	$highest = max($highest, $price[2]); // 最高値更新
	$losscutLine = round($highest - $atr * ATR_N, 2); // 損切りライン算出
	echo implode(",", [
		$price[0],
		$highest,
		$price[4],
		$losscutLine,
		$price[4] < $losscutLine ? '売却' : '保持'
	]) . "\n";
}

実行結果はこんな感じ↓。

日付,最高値,終値,損切りライン,指示
2017-01-19,19122.39,19072.25,18271.88,保持
2017-01-20,19176.86,19137.91,18356.74,保持
2017-01-23,19176.86,18891.03,18335.46,保持
2017-01-24,19176.86,18787.99,18366.58,保持
2017-01-25,19176.86,19057.5,18309.53,保持
2017-01-26,19405.23,19402.39,18485.54,保持
2017-01-27,19486.68,19467.4,18625.24,保持
2017-01-30,19486.68,19368.85,18642.75,保持
2017-01-31,19486.68,19041.34,18596.14,保持
2017-02-01,19486.68,19148.08,18589.35,保持
(中略)
2017-04-03,19668.01,18983.23,18884.87,保持
2017-04-04,19668.01,18810.25,18851.34,売却
2017-04-05,19668.01,18861.27,18865.81,売却
2017-04-06,19668.01,18597.06,18814.58,売却
2017-04-07,19668.01,18664.63,18792.61,売却
2017-04-10,19668.01,18797.88,18805.68,売却
(後略)

2ヶ月半ほど保持の指示が続いた後で、4月4日に売却指示が出ました。1月19日に始値(19,082.83円)で買付して、売却指示翌日の4月5日の始値(18,900.70円)で処分したとして、約1%の損失です。実際は銘柄毎に直近期間をバックテストして係数をフィッティングするようなので、お仕着せな係数ではまぁこんなもんかも知れません。

ちなみにこの trader_atr() 関数ですが、計算結果が単純移動平均では無さそうなのでソースコードを確認してみたのですが、平均値 = (前日の平均値 × (日数 - 1) + 当日の値幅) / 日数、という加重計算がされているようです。この関数では trader_ma() のように移動平均の種類が指定できる訳ではないので、その辺が不満な場合は trader_tr() 関数で真の値幅を得てから好きな方式の平均値を求めるのが良いかと。

「ジブリの呪い」を検証してみた

先日こんなツイート↓

で知ったのですが、投資家筋では有名な「ジブリの呪い」てなモノがあるそうですな。この類の事象をアノマリーと言うらしく、昨年読んだこの本(↓)でも各種アノマリーの検証例が載っており興味深かったです。

ロボット運用のプロが分析してわかった 最強の株式投資法

ロボット運用のプロが分析してわかった 最強の株式投資法

そこで、これに倣い「ジブリの呪い」を自分なりに分析してみようと思います。放映日を挟んで日経平均を信用売買して下落分の利益を狙うという方針で、何処を売買日に選んだら最も利益が見込めるのかをプログラムで集計してみます。仕様はざっくりと

  • 上記ツイート中にあるページに掲載されている放映日で、2000年以降を対象に(それ以前の日経平均データ持ってないので)。
  • 売付日は放映日とその前の営業日5日間を、買付日は放映日とその後の営業日5日間を対象に。
  • 上記売買の全組み合わせ毎にプロフィット・ファクター(総利益÷総損失、以下PF)を集計し、最大となる売買日を探す。
  • 売買は終値で。手数料とかは無視。

てな感じで。以下、得られた結果から抜粋した PF ベスト3です。

売付日 買付日 PF 勝率
0 3 1.39 53.1%
0 2 1.38 49.6%
-1 3 1.26 50.4%

つまり放映日に空売りして、その3営業日後に買い戻すのが最も良さそうです。が、PF・勝率いずれもこの数字では殆ど有意性を感じられないですね… いちおう Excel で結果の可視化もしてみます。

f:id:mariyudu:20170209214633p:plain


さて、よく調べるとこの「ジブリの呪い」、米雇用統計発表と重なると魔力を発するらしいです。という訳で、放映日を第一金曜日のものに絞って(26件ありました)再計算してみると…

売付日 買付日 PF 勝率
0 3 2.15 61.5%
-1 3 2.05 53.8%
-3 3 1.88 61.5%

f:id:mariyudu:20170209214852p:plain

ほほう、なんだかそれっぽい結果になりました。放映日か直前あたりで売り・3営業日後あたりで買うのが好成績という傾向は共通してるみたい。これならちょっと試してみようかな、という気になってしまいますw 蛇足ですが、本記事は過去のデータを集計しただけのものであり、今後を予測するものではありません。投資はあくまで自己判断・自己責任で!

PHP Trader 関数を使ってみる - ボリンジャーバンドの巻

前回の続きで、今回は trader_bbands 関数ボリンジャーバンドを算出してみます。ボリンジャーバンドは、移動平均標準偏差を重ね合わせただけの指標ですが、直観的だしトレンドやボラティリティがひと目で分かって便利なので、自分もよく使っています。

trader_bbands のコーリングシーケンスは、

trader_bbands(<株価が格納された1次元の配列>, <移動平均日数>, <+σの係数>, <-σの係数>, <移動平均の種類>)

です。株式相場では通常、25日の単純移動平均を元に計算するようです。返り値は +Nσ値・移動平均・-Nσ値が格納された2次元配列です。

それでは、前回同様、k-db.com さんから頂いた今年の日経平均日足データを元にボリンジャーバンドを算出して表示するというシナリオのサンプルプログラムです。

<?php
// CSV ファイルから株価データを読み込んで、配列に格納
$data = file('indices_I101_1d_2016.csv', FILE_IGNORE_NEW_LINES);
//見出し行を省く
unset($data[0]);
// 各行のカンマ区切りを配列に分割
$data = array_map(function($item){ return explode(',', $item); }, $data);
// 日付昇順でソート
usort($data, function($a, $b){ return ($a[0] < $b[0]) ? -1 : 1; });
// 終値だけ収集(兼実数変換)
$closePrices = array_map(function($item){ return +$item[4]; }, $data);
// 25日単純移動平均に基づくボリンジャーバンドを計算
$bbands = trader_bbands($closePrices, 25, 1, 1, TRADER_MA_TYPE_SMA);
// CSV 形式で整形・出力
echo "日付,株価,25日SMA,+1σ,-1σ\n";
foreach ($data as $n => $data) {
	echo implode(",", [
		$data[0],
		$closePrices[$n],
		@$bbands[1][$n],
		@$bbands[0][$n],
		@$bbands[2][$n],
	]) . "\n";
}

実行結果ははい、こんな感じ↓。

日付,株価,,25日SMA,+1σ,-1σ
2016-01-04,18450.98,,,
2016-01-05,18374,,,
2016-01-06,18191.32,,,
(中略)
2016-02-03,17191.25,,,
2016-02-04,17044.99,,,
2016-02-05,16819.59,,,
2016-02-08,17004.3,17296.788,17863.05,16730.525
2016-02-09,16085.44,17202.166,17765.29,16639.042
2016-02-10,15713.39,17095.742,17678.419,16513.065
(後略)