Skip to content

Instantly share code, notes, and snippets.

@akkijp
Created February 20, 2016 14:59
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save akkijp/3a80bd3705c50fb59761 to your computer and use it in GitHub Desktop.
Save akkijp/3a80bd3705c50fb59761 to your computer and use it in GitHub Desktop.
株の分析関数群
#!/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