Created
February 20, 2016 14:59
-
-
Save akkijp/3a80bd3705c50fb59761 to your computer and use it in GitHub Desktop.
株の分析関数群
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env ruby | |
# from https://raw.githubusercontent.com/unageanu/jiji/master/base/shared_lib/system/signal.rb | |
module Signal | |
#===一定期間のレートデータを元に値を算出するシグナルの基底クラス | |
class RangeSignal | |
include Signal | |
#====コンストラクタ | |
#range:: 集計期間 | |
def initialize( range=25 ) | |
@datas = [] # レートを記録するバッファ | |
@range = range | |
end | |
#====次のデータを受け取って指標を返します。 | |
#data:: 次のデータ | |
#戻り値:: 指標。十分なデータが蓄積されていない場合nil | |
def next_data( data ) | |
# バッファのデータを更新 | |
@datas.push data | |
@datas.shift if @datas.length > @range | |
# バッファサイズが十分でなければ、nilを返す。 | |
return nil if @datas.length != @range | |
# 算出 | |
return calculate(@datas) | |
end | |
# | |
def calculate(datas); end #:nodoc: | |
#集計期間 | |
attr_reader :range | |
end | |
#===移動平均 | |
class MovingAverage < RangeSignal | |
def calculate(datas) #:nodoc: | |
ma( datas ) | |
end | |
end | |
#===加重移動平均 | |
class WeightedMovingAverage < RangeSignal | |
def calculate(datas) #:nodoc: | |
wma( datas ) | |
end | |
end | |
#===指数移動平均 | |
class ExponentialMovingAverage < RangeSignal | |
#====コンストラクタ | |
#range:: 集計期間 | |
#smoothing_coefficient:: 平滑化係数 | |
def initialize( range=25, smoothing_coefficient=0.1 ) | |
super(range) | |
@sc = smoothing_coefficient | |
end | |
def calculate(datas) #:nodoc: | |
ema( datas, @sc ) | |
end | |
end | |
#===ボリンジャーバンド | |
class BollingerBands < RangeSignal | |
#====コンストラクタ | |
#range:: 集計期間 | |
#pivot:: ピボット | |
def initialize( range=25, pivot=[0,1,2], &block ) | |
super(range) | |
@pivot = pivot | |
@block = block | |
end | |
def calculate(datas) #:nodoc: | |
bollinger_bands( datas, @pivot, &@block ) | |
end | |
end | |
#===傾き | |
class Momentum < RangeSignal | |
def calculate(datas) #:nodoc: | |
momentum( datas ) | |
end | |
end | |
#===傾き(最小二乗法を利用) | |
class Vector < RangeSignal | |
def calculate(datas) | |
vector( datas ) | |
end | |
end | |
#===MACD | |
class MACD < RangeSignal | |
#====コンストラクタ | |
#short_range:: 短期EMAの集計期間 | |
#long_range:: 長期EMAの集計期間 | |
#signal_range:: シグナルの集計期間 | |
#smoothing_coefficient:: 平滑化係数 | |
def initialize( short_range=12, long_range=26, | |
signal_range=9, smoothing_coefficient=0.1 ) | |
raise "illegal arguments." if short_range > long_range | |
super(long_range) | |
@short_range = short_range | |
@smoothing_coefficient = smoothing_coefficient | |
@signal = ExponentialMovingAverage.new( | |
signal_range, smoothing_coefficient ) | |
end | |
def next_data( data ) #:nodoc: | |
macd = super | |
return nil unless macd | |
signal = @signal.next_data( macd ) | |
return nil unless signal | |
return { :macd=>macd, :signal=>signal } | |
end | |
def calculate(datas) #:nodoc: | |
macd( datas, @short_range, range, @smoothing_coefficient ) | |
end | |
end | |
#===RSI | |
class RSI < RangeSignal | |
#====コンストラクタ | |
#range:: 集計期間 | |
def initialize( range=14 ) | |
super(range) | |
end | |
def calculate(datas) #:nodoc: | |
rsi( datas ) | |
end | |
end | |
#===DMI | |
class DMI < RangeSignal | |
#====コンストラクタ | |
#range:: 集計期間 | |
def initialize( range=14 ) | |
super(range) | |
@dxs = [] | |
end | |
def calculate(datas) #:nodoc: | |
dmi = dmi( datas ) | |
return nil unless dmi | |
@dxs.push dmi[:dx] | |
@dxs.shift if @dxs.length > range | |
return nil if @dxs.length != range | |
dmi[:adx] = ma( @dxs ) | |
return dmi | |
end | |
end | |
#===ROC | |
class ROC < RangeSignal | |
#====コンストラクタ | |
#range:: 集計期間 | |
def initialize( range=14 ) | |
super(range) | |
end | |
def calculate(datas) #:nodoc: | |
roc( datas ) | |
end | |
end | |
module_function | |
#===移動平均値を計算します。 | |
#datas:: 値の配列。 | |
#戻り値:: 移動平均値 | |
def ma( datas ) | |
total = datas.inject {|t,s| | |
t += s; t | |
} | |
return total / datas.length | |
end | |
#===加重移動平均値を計算します。 | |
#datas:: 値の配列。 | |
#戻り値:: 加重移動平均値 | |
def wma( datas ) | |
weight = 1 | |
total = datas.inject(0.0) {|t,s| | |
t += s * weight | |
weight += 1 | |
t | |
} | |
return total / ( datas.length * (datas.length+1) /2 ) | |
end | |
#===指数移動平均値を計算します。 | |
#datas:: 値の配列。 | |
#smoothing_coefficient:: 平滑化係数 | |
#戻り値:: 加重移動平均値 | |
def ema( datas, smoothing_coefficient=0.1 ) | |
datas[1..-1].inject( datas[0] ) {|t,s| | |
t + smoothing_coefficient * (s - t) | |
} | |
end | |
# | |
#===ボリンジャーバンドを計算します。 | |
# | |
# +2σ=移動平均+標準偏差×2 | |
# +σ=移動平均+標準偏差 | |
# -σ=移動平均-標準偏差 | |
# -2σ=移動平均-標準偏差×2 | |
# 標準偏差=√((各値-値の期間中平均値)の2乗を期間分全部加えたもの)/ 期間 | |
# (√は式全体にかかる) | |
# | |
#datas:: 値の配列 | |
#pivot:: 標準偏差の倍数。初期値[0,1,2] | |
#block:: 移動平均を算出するロジック。指定がなければ移動平均を使う。 | |
#戻り値:: ボリンジャーバンドの各値の配列。例) [+2σ, +1σ, TP, -1σ, -2σ] | |
# | |
def bollinger_bands( datas, pivot=[0,1,2], &block ) | |
ma = block_given? ? yield( datas ) : ma( datas ) | |
total = datas.inject(0.0) {|t,s| | |
t+= ( s - ma ) ** 2 | |
t | |
} | |
sd = Math.sqrt(total / datas.length) | |
res = [] | |
pivot.each { |r| | |
res.unshift( ma + sd * r ) | |
res.push( ma + sd * r * -1 ) if r != 0 | |
} | |
return res | |
end | |
#===一定期間の値の傾きを計算します。 | |
#datas:: 値の配列 | |
#戻り値:: 傾き。0より大きければ上向き。小さければ下向き。 | |
def momentum( datas ) | |
(datas.last - datas.first) / datas.length | |
end | |
#===最小二乗法で、一定期間の値の傾きを計算します。 | |
#datas:: 値の配列 | |
#戻り値:: 傾き。0より大きければ上向き。小さければ下向き。 | |
def vector( datas ) | |
# 最小二乗法を使う。 | |
total = {:x=>0.0,:y=>0.0,:xx=>0.0,:xy=>0.0,:yy=>0.0} | |
datas.each_index {|i| | |
total[:x] += i | |
total[:y] += datas[i] | |
total[:xx] += i*i | |
total[:xy] += i*datas[i] | |
total[:yy] += datas[i] * datas[i] | |
} | |
n = datas.length | |
d = total[:xy] | |
c = total[:y] | |
e = total[:x] | |
b = total[:xx] | |
return (n*d - c*e) / (n*b - e*e) | |
end | |
#===MACDを計算します。 | |
#MACD = 短期(short_range日)の指数移動平均 - 長期(long_range日)の指数移動平均 | |
#datas:: 値の配列 | |
#smoothing_coefficient:: 平滑化係数 | |
#戻り値:: macd値 | |
def macd( datas, short_range, long_range, smoothing_coefficient ) | |
ema( datas[ short_range*-1 .. -1], smoothing_coefficient ) \ | |
- ema( datas[ long_range*-1 .. -1], smoothing_coefficient ) | |
end | |
#===RSIを計算します。 | |
#RSI = n日間の値上がり幅合計 / (n日間の値上がり幅合計 + n日間の値下がり幅合計) * 100 | |
#nとして、14や9を使うのが、一般的。30以下では売られすぎ70以上では買われすぎの水準。 | |
# | |
#datas:: 値の配列 | |
#戻り値:: RSI値 | |
def rsi( datas ) | |
prev = nil | |
tmp = datas.inject( [0.0,0.0] ) {|r,i| | |
r[ i > prev ? 0 : 1 ] += (i - prev).abs if prev | |
prev = i | |
r | |
} | |
(tmp[0] + tmp[1] ) == 0 ? 0.0 : tmp[0] / (tmp[0] + tmp[1]) * 100 | |
end | |
#===DMIを計算します。 | |
# | |
# 高値更新 ... 前日高値より当日高値が高かった時その差 | |
# 安値更新 ... 前日安値より当日安値が安かった時その差 | |
# DM ... 高値更新が安値更新より大きかった時高値更新の値。逆の場合は0 | |
# DM ... 安値更新が高値更新より大きかった時安値更新の値。逆の場合は0 | |
# TR ... 次の3つの中で一番大きいもの | |
# 当日高値-当日安値 | |
# 当日高値-前日終値 | |
# 前日終値-当日安値 | |
# AV(+DM) ... +DMのn日間移動平均値 | |
# AV(-DM) ... -DMのn日間移動平均値 | |
# AV(TR) ... TRのn日間移動平均値 | |
# +DI ... AV(+DM)/AV(TR) | |
# -DI ... AV(-DM)/AV(TR) | |
# DX ... (+DIと-DIの差額) / (+DIと-DIの合計) | |
# ADX ... DXのn日平均値 | |
# | |
#datas:: 値の配列(4本値を指定すること!) | |
#戻り値:: {:pdi=pdi, :mdi=mdi, :dx=dx } | |
def dmi( datas ) | |
prev = nil | |
tmp = datas.inject( [[],[],[]] ) {|r,i| | |
if prev | |
dm = _dmi( i, prev ) | |
r[0] << dm[0] # TR | |
r[1] << dm[1] # +DM | |
r[2] << dm[2] # -DM | |
end | |
prev = i | |
r | |
} | |
atr = ma( tmp[0] ) | |
pdi = ma( tmp[1] ) / atr * 100 | |
mdi = ma( tmp[2] ) / atr * 100 | |
dx = ( pdi-mdi ).abs / ( pdi+mdi ) * 100 | |
return {:pdi=>pdi, :mdi=>mdi, :dx=>dx } | |
end | |
#TR,+DM,-DMを計算します。 | |
#戻り値:: [ tr, +DM, -DM ] | |
def _dmi( rate, rate_prev ) #:nodoc: | |
pdm = rate.max > rate_prev.max ? rate.max - rate_prev.max : 0 | |
mdm = rate.min < rate_prev.min ? rate_prev.min - rate.min : 0 | |
if ( pdm > mdm ) | |
mdm = 0 | |
elsif ( pdm < mdm ) | |
pdm = 0 | |
end | |
a = rate.max - rate.min | |
b = rate.max - rate_prev.end | |
c = rate_prev.end - rate.min | |
tr = [a,b,c].max | |
return [tr, pdm, mdm] | |
end | |
#===ROCを計算します。 | |
#Rate of Change。変化率。正なら上げトレンド、負なら下げトレンド。 | |
# | |
#datas:: 値の配列 | |
#戻り値:: 値 | |
def roc( datas ) | |
(datas.first - datas.last) / datas.last * 100 | |
end | |
end | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment