Haskell DSLによる複数プラット
ホーム・複数言語コード生成
今井
敬吾
(有) ITプランニング
2012. 3. 23
次世代ソフトウェアの開発支援環境(公益財団法人 科学技術交流財団) @ 愛知県立大学サテライトキャンパス私について
(有)ITプランニング所属
プログラマー(C++/Java/Obj-C/Haskell/OCaml, スマートフォン/Web開発等) / SEとして働く傍ら、 論文執筆・関数型言語の (草の根的) 普及活動に従事
名古屋大学
情報科学研究科 修了(阿草・結縁研)
2012年3月 博士(情報科学) 取得見込み
並行分散計算(π計算/CCS/CSP)の関数プログラミングにおける応用を研究(していた)
(有)ITプランニングについて
•
金融業向けに株価
/FXチャートを受託開発
•
関数型プログラミング言語
を利用
・業務において
6年の使用経験をもつ
・普及活動:
セミナー講師(国立情報学研究所、(株)豆蔵)・イベント主催・ 書籍執筆など•
定理証明系
Coqなど形式手法を導入
3宣伝
•
書籍
入門OCaml・Scala実践プログラミング
•
スタート
SML#
2012/3/25(日)14:00-@名大理1-109
•
関数型プログラミング言語
SML#の勉強会
•
招待講演:大堀
淳 教授 (東北大学) ほか
•
詳しくは
http://tinyurl.com/smlnagoya 参照、参加については
今井
( [email protected] まで)
•
金融業向けに株価
/FX
チャート
を受託開発
(有)ITプランニングの製品(一部)
5
金融チャートとは
•
投資家に、相場の客観的な分析手法を提供する
ツール
•
テクニカル分析
•
売買シグナル
•
いつでも、どこでも、どんな端末でも使いたい
テクニカル分析と売買シグナル
•
テクニカル分析
•
売買シグナル
7 過去のデータから統計的手法を用いて 未来の値動きを予測する cf. ファンダメンタルズ分析HTML5
チャート テクニカル分析計算定
義書
今井宜洋
∗
平成
24
年
1
月
31
日
1
トレンド系
1.1
移動平均
移動平均はパラメータ
N
に対して次で定義される線を描画します。
M A(N, X) =
1
N
N
!
−1
i=0
R
終値(X − i)
1.2
ボリンジャーバンド
ボリンジャーバンドはパラメータ
N
に対して次で定義される
Bol(
±2, N, X), Bol(±1, N, X), Bol(0, N, X)
の計
5
本の線を描画します。パラメータの初期値は
25
です。
σ(N, X) =
"
#
#
#
#
$
N
!
−1
i=0
%
R
終値(X − i) − AV G
&
R
終値(X), · · · , R
終値(X − N + 1)
'(
2
N
Bol(K, N, X) = M A(N, X) + Kσ(N, X)
1.3
エンベロープ
M A(N ), Env(N, +
− A), Env(N, + − 2A)
の合計
5
本の線を描画します。
パラメータの初期値は
N = 25, A = 1.0
です。
Env(N, A, X) = M A(N, X)
×
)
1 +
A
100
*
∗有限会社
IT
プランニング システム開発部
1
例:移動平均 赤:短期(N=5) 黄:中期(N=20) 緑:長期(N=90) テクニカル分析で特定の条件が成立した 時、売買を促すサイン例:移動平均線の
ゴールデンクロス、デッドクロス
短期線が長期線を上に交わ ると、上昇トレンドが続き やすい→買いシグナル
反対に、短期線が長期線を 下に交わると、下降トレン ドが続きやすい→売りシグナル
投資家に、客観的な指標を提供する
f
多様な取引プラットホーム
•
Java Applet
•
Windows (C++)
•
Adobe Flash
(ActionSctipt)
•
Webブラウザ
(JavaScript/HTML5)
•
携帯
(docomo, SoftBank(Java)/
AU(BREW))
•
iPhone
(Objective-C)
•
Android (Java)
「いつでも、どこでも、どんな端末でも」の結果:
➡
6言語, 9種類
の環境で金融チャートを納品
開発
/メンテナンスコスト
の増大
•
仕様書・テスト:
「この数列は仕様通りに生成されているか?」スクリーンショット
による既存バージョンとの比較 「要求:Windows版と同じチャートを出して下さい」•
障害管理:
特定環境でバグが見つかった時
「docomo携帯版の一目均衡表の計算が一日ずれている」 「Windows版のパラボリックのドテンのタイミングが他と違う」→
残り
8環境でも
不具合調査が必要
•
(コスト圧力:値下げ要求、オフショアとの競合)
9コード再利用の試み
•
ビジネスロジック部は再利用の余地あり
(テクニカル分析や売買シグナル等)
•
…が、ソースもバイナリも直接の再利用は不可:
×
Cで共通ライブラリを作成
e.g. lib*.a/.so, *.dll, JNI (Java Native Interface)
→ NG : Java Applet, 携帯, Flash, ブラウザ
×
Javaとネイティブ両方で動作するスクリプト言語
e.g. Python / Jython, Ruby / JRuby
テクニカル分析
DSL:
Haskell/Tek
•
多用途に再利用できる
コード生成
DSL
‣
C, Javaコード生成
‣
イミディエイト評価
(ランダム実行など)
‣
TeXドキュメント生成 (未実装)
•
Haskell上の埋め込みDSL (embedded DSL)
‣
Haskellの言語機能(型推論や高階関数)の恩恵を受けられる
11テクニカル分析
DSL
Haskell/Tek
による
仕様記述とコード生成
Haskell/Tek
コード
C言語
プログラム
C コンパイラiPhone版
Java
プログラム
JavaScript
TeX仕様書
ソース
Java コンパイラAndroid
版
イミディエイト
ma n i =
sum_close (i-(n-1)) i candles
/ toFloat n
Haskell/Tekコードの例:
移動平均
13 i-(n-1)日目からi日目までの 終値(close)の和をi
日目におけるn
日移動平均とはnで割る
float accum0 = 0.0F;for (int i0 = i - iparam[0] + 1; i0 <= i; i0++) { accum0 = accum0 + i_get(candles, i0).close; }
i_put(series[0], i, accum0 / (float) iparam[0]);
生成される
Cコード:
Haskell/Tek:
HTML5
チャート テクニカル分析計算定
義書
今井宜洋
∗平成
24
年
1
月
31
日
1
トレンド系
1.1
移動平均
移動平均はパラメータ N に対して次で定義される線を描画します。 M A(N, X) = 1 N N!−1 i=0 R終値(X − i)1.2
ボリンジャーバンド
ボリンジャーバンドはパラメータN に対して次で定義される Bol(±2, N, X), Bol(±1, N, X), Bol(0, N, X)
の計 5 本の線を描画します。パラメータの初期値は 25 です。 σ(N, X) = " # # # # $ N!−1 i=0 % R終値(X − i) − AV G&R終値(X), · · · , R終値(X − N + 1)'(2 N Bol(K, N, X) = M A(N, X) + Kσ(N, X)
1.3
エンベロープ
M A(N ), Env(N, + − A), Env(N, + − 2A) の合計 5 本の線を描画します。
パラメータの初期値は N = 25, A = 1.0 です。 Env(N, A, X) = M A(N, X) × ) 1 + A 100 * ∗有限会社 IT プランニング システム開発部 1
例
2: ボリンジャーバンド
sigma_i n ma i =
sqrt_ (
sum_iter
(i - n + 1) i dist)
/ toFloat n
where
dist i = pow2_ (close_at i - ma)
bol (n,a,b) i =
let_ "ma" (
MA.ma
n i) $ \ma ->
let_ "sigma" (sigma_i n ma i) $ \sigma ->
tup5 (ma + b*sigma, ma + a*sigma, ma,
ma - a*sigma, ma - b*sigma)
Haskell/Tek:
HTML5
チャート テクニカル分析計算定
義書
今井宜洋∗ 平成24
年1
月31
日1
トレンド系
1.1
移動平均 移動平均はパラメータ N に対して次で定義される線を描画します。 M A(N, X) = 1 N N!−1 i=0 R終値(X − i)1.2
ボリンジャーバンドボリンジャーバンドはパラメータN に対して次で定義される Bol(±2, N, X), Bol(±1, N, X), Bol(0, N, X) の計 5 本の線を描画します。パラメータの初期値は 25 です。 σ(N, X) = " # # # # $ N!−1 i=0 % R終値(X − i) − AV G&R終値(X), · · · , R終値(X − N + 1)'(2 N Bol(K, N, X) = M A(N, X) + Kσ(N, X)
1.3
エンベロープM A(N ), Env(N, + − A), Env(N, + − 2A) の合計 5 本の線を描画します。 パラメータの初期値は N = 25, A = 1.0 です。 Env(N, A, X) = M A(N, X) × ) 1 + A 100 * ∗有限会社 IT プランニング システム開発部 1
HTML5
チャート テクニカル分析計算定
義書
今井宜洋
∗平成
24
年
1
月
31
日
1
トレンド系
1.1
移動平均
移動平均はパラメータ N に対して次で定義される線を描画します。 M A(N, X) = 1 N N!−1 i=0 R終値(X − i)1.2
ボリンジャーバンド
ボリンジャーバンドはパラメータN に対して次で定義される Bol(±2, N, X), Bol(±1, N, X), Bol(0, N, X)
の計 5 本の線を描画します。パラメータの初期値は 25 です。 σ(N, X) = " # # # # $ N!−1 i=0 % R終値(X − i) − AV G&R終値(X), · · · , R終値(X − N + 1)'(2 N Bol(K, N, X) = M A(N, X) + Kσ(N, X)
1.3
エンベロープ
M A(N ), Env(N, + − A), Env(N, + − 2A) の合計 5 本の線を描画します。
パラメータの初期値は N = 25, A = 1.0 です。 Env(N, A, X) = M A(N, X) × ) 1 + A 100 * ∗有限会社 IT プランニング システム開発部 1
i日目より
過去n日間に関する
分散
σ(n,i)
ボリンジャーバンド
(5つ組)
高階関数 sum_iter i j f = Σ (x is i to j) f(x)ボリンジャーバンド:
生成される
C言語コード
15
float accum0 = 0.0F;
for (int i0 = i - iparam[0] + 1; i0 <= i; i0++) { accum0 = accum0 + i_get(candles, i0).close; }
float ma = accum0 / (float) iparam[0];
float accum1 = 0.0F;
for (int i1 = i - iparam[0] + 1; i1 <= i; i1++) { float x = i_get(candles, i1).close - ma;
accum1 = accum1 + x * x; }
float sigma = sqrt(accum1 / (float) iparam[0]); i_put(series[0], i + 0, ma + fparam[1] * sigma); i_put(series[1], i + 0, ma + fparam[0] * sigma); i_put(series[2], i + 0, ma);
i_put(series[3], i + 0, ma - fparam[0] * sigma); i_put(series[4], i + 0, ma - fparam[1] * sigma);
分散
σ
移動平均ボリンジャー
バンド
Haskell/Tek=
(現状では) 性能の良いC言語マクロ
16
float accum0 = 0.0F;
for (int i0 = i - iparam[0] + 1; i0 <= i; i0++) { accum0 = accum0 + i_get(candles, i0).close; }
float ma = accum0 / (float) iparam[0];
float accum1 = 0.0F;
for (int i1 = i - iparam[0] + 1; i1 <= i; i1++) { float x = i_get(candles, i1).close - ma;
accum1 = accum1 + x * x; }
float sigma = sqrt(accum1 / (float) iparam[0]); i_put(series[0], i + 0, ma + fparam[1] * sigma); i_put(series[1], i + 0, ma + fparam[0] * sigma);
移動平均のコードが展開される
bol (n,a,b) i =
let_ "ma" (
MA.ma
n i) $ \ma ->
let_ "sigma" (sigma_i n ma i) $ \sigma ->
tup5 (ma + b*sigma, ma + a*sigma, ma,
ma - a*sigma, ma - b*sigma)
移動平均の呼び出し•
別の見方:型チェック・高階関数 (マクロ)等を備えたメタプログラ ミングライブラリ•
値呼びと名前呼びを少し注意して 区別する必要がある(やや欠点)適用可能性
•
移動平均
•
指数平滑移動平均
•
一目均衡表
•
ボリンジャーバンド
•
パラボリック
•
エンベロープ
•
RSI
•
ストキャスティクス
•
DMI
•
RCI
•
モメンタム
•
ROC
•
MACD
•
%Rオシレーター
17本手法により
14種
のテクニカル分析に対応
実装コストの減少
•
開発:
15人日 ... Haskell/Tek C言語版 (iOS向け)
7人日 ... テクニカル分析
1人日
... Haskell/Tek Java版 (Android向け)
総コード行数
•
Haskell 1758行
(うちテクニカル分析777行, C生成部 981行)
➡
生成された
C言語コード 2077行
➡
生成された
Javaコード 2257行
19なぜ
Haskellか
1. オープンソースのパーザ/プリンタが沢山あった
•
C, Java, JavaScript, ... そこそこ枯れたライブラリが多数
•
代数的データ型のおかげで構文木操作が
(JavaやCより)簡単
•
今回は
Language.C.Quote(*1)を用いた
2.
EDSL との親和性が高い
•
遅延評価、関数呼び出しの構文が「軽い」、型クラス
21(*1) language-c-quote: http://hackage.haskell.org/package/language-c-quote
実装:
Language.C.Quoteの利用
•
Haskellの言語拡張により、
Haskellの中にCのコー
ド断片
を記述可能に
•
C言語構文木の生成が手軽にできた
do
..
(bodyExp, bodyStmts) <- ....
tell [C.BlockStm
[cstm|
for
(
int
$
id
:counter=$f ; $
id
:counter<=$t ; $
id
:counter ++) {
$items:bodyStmts
$items:(makeAssigns initVars bodyExp)
}
C言語コード
EDSLの概要
•
Haskell/Tekの関数群で記述したデータが、C/Javaに変換される
(
Haskellの全体が、CやJavaに変換できるわけではない)
•
より詳細には
23Haskell/Tek
コード
C言語
プログラム
実行形式
(.exeなど)
Haskell
コンパイラ
実行
実行
Java
プログラム
-- テクニカル記述 ma n i =sum_close (i-(n-1)) i candles / toFloat n
-- テクニカル記述をCコードとして印字
main = printCCode ma
-- main = printJavaCode ma -- Java
-- main = randomRun ma -- イミディエイト実行
EDSLの実装(案1)
•
EDSLの構文木を表す型を定義し、そこから変換する
•
比較的普通の方法
•
Tek型(
中間データ構造
)を定義する必要がある
Tek型
(EDSLの構文木型)
CLanguage型
JavaLanguage型
HaskellRunner型
printCCode
printJavaCode
randomRun
Haskell/Tek
関数群
EDSLの実装(案2):
タグレス実装
•
Kiselyovら(*2)の方法 (こちらを採用)
•
中間データ型を介さず、型クラスによって実装する
25