【感想】『Rubyではじめるシステムトレード』読了

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

取り組んだ動機

ふろうしょとくほしい

学習方法

史上最長のGWを利用して1日2章ぐらいのペースで進めました

とりあえず結論

  • 体系的なプログラミングを学習する本ではない
  • これからオリジナルのシストレシステムを構築しようとしている人にとっては必読レベルの良書 おすすめ度:★☆☆☆☆(後述) 難易度:★★★★☆

体系的なプログラミングを学習する本ではない

第1部準備編(1~3章)ではRubyの文法などの基本的な内容や環境構築を学習し、第2部データ編(4~9章)ではおもに東証の銘柄の過去の株価データを「スクレイピングで」取得する方法の学習、第3部(10~22章)でバックテストを行うシミュレーションソフトを作成していくという構成になっています。

一応最低限のRubyの説明はあり、この本でプログラミング始めても大丈夫!みたいな雰囲気で書かれてますが、無謀です。
すごいセンスのある人なら大丈夫なのかもしれないですが…(ちなみにこの本の著者は東北大学出身です(権威主義))。
めちゃくちゃ複雑な機能を使ってるとかいうわけではないですが、ソース内コメントも最低限で、Rubyistらしい省略記法も多用しているので、最低限シンボルとかブロックとかRuby独特の文法を理解している上で取り組まないと厳しいと思います。

加えて「仕掛け」「手仕舞い」「ストップ(損切りライン)」「フィルター(仕掛け時の追加ルール)」をすべて同じ「ルール」クラスから派生させるという素人からするとトリッキーに見える設計になっていて、そこらへんの説明もあんまりなかった気がするので、設計まで含めてプログラミング的な知見を増やすというような目的にはややそぐわないのかなぁという印象です。

またこの本は2014年に発行された書籍なのですが、ヤフーファイナンススクレイピングしていたり、Rubyのバージョンが古いこともあって、おそらくソースコードの丸写しでは正常に動かない箇所が出てくると思われます。
ちょくちょく誤植らしき箇所も散見されるので、ある程度、自分でデバッグしつつ修正していく能力が必要です。

これからオリジナルのシストレシステムを構築しようとしている人にとっては必読レベルの良書

この本の圧倒的な強みは著者が実際に使用していた(る?)であろうシステムを惜しげもなく公開しているという点につきると思います。
ある程度自力でソースコードを読み解いていく能力があり、これからオリジナルのシステムトレードのシステムを構築しようとしている人にとっては、どういう機能が必要なのか、こういう指標を実装するにはどういうアルゴリズムを組めばいいのか、など大いに刺激される知見を与えてくれます。

本格的に運用するならやはりデータベースを使用するべきでしょうし、スリッページや資金管理が考慮されていないなど、システムをそのまま流用するのは厳しい印象ですが、システム構築に関する考え方などは十分参考にできそうです。

おすすめ度★1というのは決して悪書という意味ではなく、万人向けではない、必要な人は何を言われても買うだろうという以上の意味はありません。

最後におすすめの学習法としては、まず予習の部分を軽く読み流し、各章の最後の機能テスト用のコード(掲載されていない章もあります)を1行ずつ実行しながら、必要な箇所をその都度実装していき、分かりにくい部分があればソースコードの解説を読んでいくという方法が効率的ではないかなと思います。

カロリーメイトください。

『Rubyではじめるシステムトレード』第21章~第22章 学習記録

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

序文

Rubyではじめるシステムトレード』最終日。

GW終了1時間前に読了。
いいのか悪いのか…。

進捗

  • 第3部 シミュレーション編
    • 第21章 シミュレーションする
    • 第22章 もう一つのシステム

(学習時間:6時間)

GitHub

github.com

コード実装部分(一部)

RubySystemTrade\trade_simulator\setting\breakout.rb

Simulation.setting "breakout", "0.0.0" do
    trading_system do
        entry BreakoutEntry, span: 10
        exit MinHoldExit, min_hold_days: 1
        exit StopOutExit
        exit BreakoutExit, span: 5
        stop AverageTrueRangeStop, span: 20, ratio: 2
        filter MovingAverageDirectionFilter, span: 30
    end

    data_loader TextToStock, data_dir: "data",
                             stock_list: "tosho_list.txt",
                             market_section: "東1"
    record_dir "result"
    record_every_stock true
end

実行結果

f:id:yjkym:20190506225423p:plain
あってんのかこれ 酷すぎんだろ

感想

21章で設定ファイルをRubyっぽく書くやりかた(というかRubyなんだけど)。
22章で別のルール(ブレイクアウト)を採用したシステムを作って終了。

タイトルと内容が微妙に合ってないような気がしないでもない。

とりあえずお疲れ様でした。

余力があれば後日感想とか書くかも。

カロリーメイトください。

『Rubyではじめるシステムトレード』第17章~第20章 学習記録

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

序文

Rubyではじめるシステムトレード』7~8日目。

いよいよ内容的にも佳境ということで、読み進めるにも時間がかかるようになってしまい、2日分まとめて更新。
いやこれほんとGWに集中的にこなすのに、ぴったりのボリュームで。
逆に言うとこれ以外何もできずに今年のGWは終わっちゃいそうです…。

進捗

  • 第3部 シミュレーション編
    • 第17章 売買ルールを作る
    • 第18章 TradingSystemクラス
    • 第19章 結果の出力
    • 第20章 Simulationクラス

(学習時間:2日間)

GitHub

github.com

コード実装部分(一部)

RubySystemTrade\trade_simulator\check\rule_check.rb

# coding: Windows-31J

require "./lib/simulation"
require "./lib/trading_system"
require "./lib/base"
require "./lib/text_to_stock"
require "./lib/recorder"

# 株データオブジェクトを生成する
text_to_stock = TextToStock.new(stock_list: "tosho_list.txt")
# トレードシステムを作成する
estrangement_system = TradingSystem.new ({
                    # 移動平均乖離率による仕掛け
                    # 20日移動平均から5%離れたら寄り付きで仕掛ける
                    entries: EstrangementEntry.new(span: 20, rate: 5),
                    exits: [
                        # ストップラインによる手仕舞いをする
                        StopOutExit.new,
                        # 移動平均乖離率による手仕舞い
                        # 20日移動平均から3%以内に戻ったら手仕舞いをする
                        EstrangementExit.new(span:20, rate: 3)
                    ],
                    # ATRによるストップ
                    # 仕掛け値から20日ATRのところにストップラインを設定する
                    stops: AverageTrueRangeStop.new(span: 20),
                    # 移動平均の方向によるフィルター
                    # 買いは移動平均が上昇中のときのみ、
                    # 売りは移動平均が下降中のときのみしかける
                    filters: MovingAverageDirectionFilter.new(span: 30)
                  })

# トレードを記録するオブジェクト
recorder = Recorder.new
recorder.record_dir = "result/estrangement/test_simulation"

simulation = Simulation.new(trading_system: estrangement_system,
                            data_loader: text_to_stock,
                            recorder: recorder)

recorder.create_record_folder

# simulation.simulate_a_stock(8604)

simulation.simulate_all_stocks

実行結果

f:id:yjkym:20190505224918p:plain

感想

17章で具体的な売買ルールを実装、18章でシステムの簡単なシミュレーション、19章でシミュレーションの結果を出力する機能を作り、20章ですべての機能をまとめて動かしてみる。

それほど複雑な機能を使用しているわけではないし、まったくお手上げという感じではないのだが、とにかく組み込む機能が多くて頭がついていくのが大変である。
とりあえず最低限動かして、結果を出力するところまでできて一安心。

プログラムの流れを追って、なんとなくこれで動きそうだなーというぐらいは思うのだが、厳密な検証とかはまったくできていない。
特にRubyの仕様として変数の型がパッと見で判断できないことと、メソッドがどこで定義されているのかがすぐにわからないことがつらい。
局所的な理解ではなくて、システムの全体像がしっかり把握できていないと、どういう流れで処理が行われているのかがスムーズに理解できないと感じる。

それでもプログラミングでのシステムトレードを実現するにあたって、どういう機能が必要かだとか、どういう流れで行えばいいのかとか、どういう点に注意が必要なのかといった知見を与えてくれるのは本当にありがたい。
本格的にシステムトレードに取り組もうと思えば、やはりちゃんとデータベースを利用したほうがいいだろうし、他にもいろいろ物足りない点も感じてきているので、おそらく本書のシステムをそのまま実践へ流用することは難しいだろうが、今後実践へ移るにあたっての手応えは感じている。

さぁあと1日がんばろう。

カロリーメイトください。

『Rubyではじめるシステムトレード』第15章~第16章 学習記録

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

序文

Rubyではじめるシステムトレード』6日目。

もうGW1週間なんですねぇ。
例年以上に代わり映えない毎日ですがなんとなく充実してる気もするぞ。

進捗

  • 第3部 シミュレーション編
    • 第15章 Tickモジュール
    • 第16章 Ruleとその子クラス

(学習時間:6時間)

GitHub

github.com

コード実装部分(一部)

RubySystemTrade\trade_simulator\check\rule_check.rb

#coding: Windows-31J

require "pp"
require "./lib/stock"
require "./lib/base"
require "./lib/tick"

# Entryから仕掛けクラスを作るテスト
class MyEntry < Entry
    def check_long(index)
        enter_long(index, 100, :close) if index % 2 == 0
    end

    def check_short(index)
        enter_short(index,100, :close) if index % 2 == 1
    end
end

puts '=== ENTRY ==='
stock = Stock.new(1000, :t, 100)

entry = MyEntry.new

entry.stock = stock

# indexが偶数なら買いでインする
pp entry.check_long_entry(0)
# indexが奇数なら買いでインしない
pp entry.check_long_entry(1)

# indexが偶数なら売りでインしない
pp entry.check_short_entry(0)
# indexが奇数なら売りでインする
pp entry.check_short_entry(1)

# Exitから手仕舞いクラスを作るテスト
class MyExit < Exit
    def check_long(trade, index)
        exit(trade, index, 105, :close) if index % 2 == 1
    end

    def check_short(trade, index)        
        exit(trade, index, 95, :close) if index % 2 == 0
    end
end

puts '=== EXIT ==='
my_exit = MyExit.new
my_exit.stock = stock
# 買いでインする
trade1 = entry.check_long_entry(0)
# indexが偶数なら買いを手仕舞わない
my_exit.check_exit(trade1, 0)
puts trade1.entry_price
puts trade1.exit_price
# indexが奇数なら買いを手仕舞い
my_exit.check_exit(trade1, 3)
puts trade1.entry_price
puts trade1.exit_price

# 売りでインする
trade2 = entry.check_short_entry(1)
# indexが奇数なら売りを手仕舞わない
my_exit.check_exit(trade2, 55)
puts trade2.entry_price
puts trade2.exit_price
# indexが奇数なら売りを手仕舞う
my_exit.check_exit(trade2, 2)
puts trade2.entry_price
puts trade2.exit_price

class MyStop < Stop
    def stop_price_long(position, index)
        # 5Tick下に損切りラインを置く
        Tick.down(position.entry_price, 5)
    end
    def stop_price_short(position, index)
        # 5Tick上に損切りラインを置く
        Tick.up(position.entry_price, 5)
    end
end

puts '=== STOP ==='
stop = MyStop.new
# 買いでインする
trade3 = entry.check_long_entry(0)
puts stop.get_stop(trade3, 0)

# 売りでインする
trade4 = entry.check_short_entry(1)
puts stop.get_stop(trade4, 1)

class MyFilter < Filter
    def filter(index)
        case index % 4
        when 0
            :long_only
        when 1
            :short_only
        when 2
            :no_entry
        when 3
            :long_and_short
        end
    end
end

filter = MyFilter.new

puts '=== FILTER ==='
p filter.get_filter(0)
p filter.get_filter(1)
p filter.get_filter(2)
p filter.get_filter(3)

実行結果

f:id:yjkym:20190503183434p:plain:w200

感想

15章では株価の呼値単位に対応するTickクラスを実装。概ね問題なし。

16章ではトレードに使用するルールを規定するRuleクラスを作成していく。
ルールには「仕掛け(株を買うこと)」「手仕舞い(買った株を売ること)」「ストップ(損失が拡大しないように設定するルールのこと)」「フィルター(仕掛ける条件)」が含まれ、それぞれRuleの子クラスとして、Entryクラス、Exitクラス、Stopクラス、Filterクラスとして実装する。

ストップ(損切りライン)を指定しながら、利益確定ラインを作らなかったり、「仕掛け」クラスでもチェックを行うのにさらに「フィルター」クラスで二重のチェックを行ったりしているのは作者の(プログラミングではなく株取引の)思想が色濃く出ている。

そして「仕掛け」「手仕舞い」という"振る舞い"と「ストップ」「フィルター」という"条件"を同じクラスから派生させる、つまりis-aの関係で実装するのはやはりトリッキーだと思うのだが、作者の中ではしっかり抽象化できているのだろうか。羨ましい。
私ならやはりhas-aのイメージで実装していくような気がする。

驚いたのはEntryクラスからMyEntryクラスのように、すなわち親クラスから子クラスのメソッドをいきなり呼び出している部分。
親クラスに抽象メソッドを書くのを省略しているだけと言われれば納得してしまいそうだが、Rubyはこんなフレキシブルな書き方もできるのかと驚いた。
すごいなとは思うが、さすがに混乱のもとになりそうだ。
Rubyつらいなー。

カロリーメイトください。

参考サイト

www.matsui.co.jp

『Rubyではじめるシステムトレード』第13章~第14章 学習記録

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

序文

Rubyではじめるシステムトレード』5日目。

昨日今更「翔んで埼玉」見に行ったんですが、イマイチでした。
狙いすぎというか、「こういうネタがお前らおもしろいんだろ?」みたいなのが見えちゃって…。
うーん。

進捗

  • 第3部 シミュレーション編
    • 第13章 Indicatorクラス
    • 第14章 テクニカル指標を作る

(学習時間:6時間)

GitHub

github.com

コード実装部分(一部)

RubySystemTrade\trade_simulator\lib\indicator\indicator.rb

# coding: Windows-31J

require "./lib/array"

# テクニカル指標の親クラス
class Indicator
    include Enumerable

    def initialize(stock)
        @stock = stock
    end

    def each
        @indicator.each {|value| yield value}
    end

    def calculate
        @indicator = calculate_indicator
        self
    end

    # 要素が nil の時は :no_value を throw する
    def [](index)      
        # my_indicator[1..3]みたいにindexに配列っぽいのが入ってることがあることに注意
        if index.kind_of?(Numeric) && (@indicator[index].nil? || index < 0)
            throw :no_value
        else
            @indicator[index]
        end
    end

    def calculate_indicator; end

end

実行結果

割愛

感想

13章では各指標をあらわすのに使用するインターフェース的なクラスを作成し、14章では13章で作成したクラスを継承して移動平均やら移動平均乖離率やらATRといった各指標を実装していく。

各指標を表すクラスは上のコードを継承させて、空のcalculate_indicator()メソッドに実際の処理を書いていく。

Rubyではよくある書き方なのかはわからないが、"[]"みたいなのをオーバーライドするのはあんまり好きじゃない。
よっぽど必然性を感じられる実装をしないと、呼び出し側で処理がわからなくなりがちな気がする。

あとArrayクラスを拡張して、新しいメソッドを追加しているのだけど、これもなんとなく気に食わない。
呼び出し側から標準クラスを呼び出しているのか、拡張したクラスを呼び出しているのか判別できないだろう。
これはまだSuperArrayなりMyArrayなりのArrayを継承したクラスを新しく作ったほうが分かりやすい気がする。

ともあれ全653ページ中の428ページまで終了。
今のところ順調だが、無事GW中に読破できるだろうか。

カロリーメイトください。

参考サイト

ref.xaio.jp

104m.jp

『Rubyではじめるシステムトレード』第10章~第12章 学習記録

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

序文

Rubyではじめるシステムトレード』4日目。

毎月1日映画の日。ということで映画館から上映までの待ち時間で更新しております。

進捗

  • 第3部 シミュレーション編
    • 第10章 シミュレーションソフトの設計図
    • 第11章 トレードクラス
    • 第12章 Arrayクラスの拡張

(学習時間:6時間)

GitHub

github.com

コード実装部分(一部)

RubySystemTrade\trade_simulator\lib\array.rb

# coding: Windows-31J

class Array
    def average
        sum.to_f / self.size
    end

    # 元の配列と同じ要素数の配列を返す
    # 配列の各要素を span 個ずつ取り出した配列に対して、順番に
    # ブロックで実装された処理を実行する
    # 呼び出し方:array.map_indicator(span){|span_array|...}
    def map_indicator(span)
        indicator_array = Array.new(self.size)
        self.each_cons(span).with_index do |span_array, index|
            next if span_array.include?(nil)
            indicator_array[index + span - 1] = yield span_array
        end
        indicator_array
    end

    # 移動平均
    def moving_average(span)
        map_indicator(span) do |vals|
            vals.average
        end
    end

    # 区間高値
    def highs(span)
        map_indicator(span){|vals|vals.max}
    end

    # 区間安値
    def lows(span)
        map_indicator(span){|vals|vals.min}
    end
end

実行結果

f:id:yjkym:20190501210723p:plain

感想

9章はパンローリング社が提供しているらしいPan Active Market Databaseなるアプリケーションを使って株価を取得する方法の説明。

露骨に宣伝フェーズの匂いがしたのでスルーしようかと思っていたのだけど、無料でできるというので一応試用版というのだけ試してみた。
結果、ダメでした。

アプリケーションが古すぎるのか、むりやりデフォルトと違う場所にインストールしようとしたからなのかは定かでないですが、DLLが参照できないみたいなエラーが出てた気がします。

10章はこれから作るシミュレーションソフトの概要の説明。
これはまたわからなくなったら戻ってこようということで流し読み。

11章は取引そのものに対応するTradeクラスの作成。
そのTradeが終了したかどうかを返すclosed?メソッドを冗長な書き方してるのになんとなく親近感を抱く。

    # 手仕舞い済みかどうか
    def closed?
        if @exit_date && @exit_price
            true
        else
            false
        end
    end

↑はこれ↓でいけるはず。
わかんないけどほとんどのRubyist?はこれでいくんじゃないかなー。

    # 手仕舞い済みかどうか
    def closed?
        @exit_date && @exit_price
    end

12章はRuby標準のArrayクラスを拡張していく。
配列の一部を取り出して、その取り出した配列に対して、順次ブロック内の処理をさせていくmap_indicatorメソッドが鬼門。
なんとなく分かるんだけど、説明しろって言われると難しいなー。

あ、そろそろ「翔んで埼玉」上演の時刻です。

カロリーメイトください。

『Rubyではじめるシステムトレード』第7章~第8章 学習記録

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

Rubyではじめるシステムトレード (現代の錬金術師シリーズ)

序文

Rubyではじめるシステムトレード』3日目。

平成最後のディナーは日本ハムの業務用レトルトカレー
f:id:yjkym:20190430224227j:plain:w150

と勘違いしてまとめ買いしたトップバリュのカレー(30%OFF)でした。
f:id:yjkym:20190430224243j:plain:w150
わりとうまかった。

進捗

  • 第2部 データ編
    • 第7章 ネットから株価データをダウンロードする
    • 第8章 ダウンロードした株価データから株オブジェクトを作る

(学習時間:6時間)

GitHub

github.com

コード実装部分(一部)

RubySystemTrade\trade_simulator\lib\text_to_stock.rb

# coding: Windows-31J

require "./lib/stock_list_loader"
require "Date"

class TextToStock
    attr_writer :from, :to

    def initialize(params)
        @data_dir = params[:data_dir] || "data"
        @stock_list = params[:stock_list] || raise {"銘柄リストを指定してください"}       
        @market_section = params[:market_section]
        @list_loader = StockListLoader.new("#{@data_dir}/#{@stock_list}")
    end

    def generate_stock(code)
        index = @list_loader.codes.index(code)
        stock = Stock.new(code,
                          market(index),
                          @list_loader.units[index])
        add_prices_from_data_file(stock)
        stock
    end

    # 銘柄リストにある銘柄について
    # データディレクトリ内にある株価データから
    # 順番に株オブジェクトを返すイテレータ
    def each_stock
        @list_loader.filter_by_market_section(*@market_section)
                    .codes.each do |code|
                        if File.exist?("#{@data_dir}/#{code}.csv")
                            yield generate_stock(code)
                        end
                    end
    end

    private
    def market(index)
        section = @list_loader.market_sections[index]
        case section            
        when /[12]|JQ/
            :t
        end
    end

    def add_prices_from_data_file(stock)
        lines = File.readlines("#{@data_dir}/#{stock.code}.csv")
        fi = from_index(lines)
        ti = to_index(lines)
        return if fi.nil? || ti.nil?
        lines[fi..ti].each do |line|
            data = line.split(",")
            date = data[0]
            prices_and_volume = data[1..5].map {|d| d.to_i}
            stock.add_price(date, *prices_and_volume)
        end
    end

    def from_index(lines)
        return 0 unless @from
        @formatted_from ||= Date.parse(@from).to_s.gsub("-","/")
        # Array#index ブロック内の評価が最初に真になった要素のインデックスを返す
        lines.index{|line|
            line[0..9] >= @formatted_from
        }
    end

    def to_index(lines)
        return lines.size unless @to
        @formatted_to ||= Date.parse(@to).to_s.gsub("-","/")
        lines.rindex{|line|
            line[0..9] <= @formatted_to
        }
    end
end

実行結果

割愛

感想

第7章はやはりヤフーファイナンススクレイピングして個別の株価データを取得してくる。
あんまりスクレイピングはやりたくないので、別の方法で株価データを取得する。

スクレイピングよりはまっとうなやり方なのだけど、スクレイピングのように全自動でということがなかなか難しい。
ここらへんは実際に運用するときにまた方法を考えなければいけなさそうだ。

第8章では取得した株価データをオブジェクトに変換していく。

そんなに難しくなさそうに見えるのだが、コードを個別指定して株価オブジェクトを取得したり、期間を指定した株価オブジェクトを取得できるようにしたり、ちょっとしたAPIを作っているような感じ。

文法もギリギリ理解できそうな感じではあるのだが、yieldを駆使したイテレータ(each_stockメソッド)を自作していたり、なかなか歯ごたえがある。

each_stockメソッドの書き方…中間変数とか使わずにどんどんチェーンして処理を書いていくのとか、すごくプログラミング素養のある人っぽい書き方ですねー…。
つらい…。

あと最後の方のline[0..9]ってなんのこっちゃって思ったんですが、ある日の株価データがline変数に

2014/04/10,1638.0,1642.0,1604.0,1604.0,176400

みたいな形式で入ってるんで、つまり日付の部分を取り出してるんですねー。

…いやー…つらい。笑

カロリーメイトいらない。

参考サイト

qiita.com

[http://chartnavi.com/brand/market/%E6%9D%B1%E8%A8%BC1%E9%83%A8/1/:embed:cite]