重み付き多項式近似バンドxnbの紹介
はじめに
今回はxnb(重み付き多項式近似バンド)を紹介します。xnbでは価格の重み付き多項式近似曲線を求め、その近似値を中心に残差の標準偏差をプロットします。xnbの例として、均等の重みで0次式で近似する場合がボリンジャーバンド(Bollinger Bands)です。指数減衰重みで0次式で近似する場合が指数ボリンジャーバンド(EBB: Exponentially weighted Bollinger Bands)です。
以下ではxnbについて説明し、TradingViewのPineによる実装を示します。実装には、(1)方式を理解するための原理実装版、(2)重み付けの種類によらない高速化版、(3)重み付け固有の性質を使った高速化版がありますので用途に応じて使い分けてください。
xnbとは
xnbはxとnで決まるbandです。xで指定される重みでn次多項式近似曲線を求め、その近似値(xnp)と残差の標準偏差(xnd)を使い、次をプロットしたものです。
中心線: xnp
±1σ: xnp±xnd
±2σ: xnp±xnd×2
xとして今回の実装では次の3つが使えます。(他の重みも適用可能)
s: SMA(単純移動平均)と同じ重み
l: LWMA(線形加重移動平均)と同じ重み
e: EMA(指数移動平均)と同じ重み
nとして今回の実装では0〜3が使えます。(4以上も拡張可能)
0: 0次式
1: 1次式
2: 2次式
3: 3次式
xnb、xnp、xndのそれぞれについて、xやnを具体的な値に置き換えた名前も使います。例えば、x=sのxnbを「snb」、n=2のxnpを「x2p」、x=eかつn=1のxndを「e1d」などと書きます。
ここで実際のxnbのチャートを見ておきます。左から順にx=s,l,e、上からn=0,1,2,3のチャートになっています。移動平均の方式の差が分かりやすいように、重みの重心(g)を1週間(1W)に統一してあります。
原理実装版のxnb_basic()
まずはxnbの定義をそのまま原理的に実装した次のxnb_basic()を見てみます。実行効率より単純さを優先しています。解釈の明確さ、バグの入りにくさ、拡張性を狙っています。
行列演算で
行列演算の前では、行列演算で使う重み付き総和を求めています。SMAとLWMAの場合はすべての重みについてループして加算しています。EMAの場合は重みが無限にあるのでcutoffで指定されたところまでを加算しています。
行列演算のあとでは、xnpとxndを求めています。xndについては前半と同じだけ回るループで残差の2乗和を求めて計算します。
export xnb_basic(simple enum_ma ma, int n, float p, float g, bool use_df, int cutoff, float offset=0) =>
var length = get_length(ma, g) // ta.sma/wma/ema()互換のlength
var loop_max = get_loop_max(ma, g, length, cutoff)
var s1 = 0.0 // wsum(1) (wsum(x)はxの重み付き総和)
var st = 0.0 // wsum(t)
var st2 = 0.0 // wsum(t^2)
var st3 = 0.0 // wsum(t^3)
var st4 = 0.0 // wsum(t^4)
var st5 = 0.0 // wsum(t^5)
var st6 = 0.0 // wsum(t^6)
var sw = 0.0 // wsum(weight)
sp = 0.0 // wsum(p)
spt = 0.0 // wsum(p*t)
spt2 = 0.0 // wsum(p*t^2)
spt3 = 0.0 // wsum(p*t^3)
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
if bar_index == 0 // bar_indexで変わらないものは最初だけ計算
s1 += weight
st += weight * t
st2 += weight * t * t
st3 += weight * t * t * t
st4 += weight * t * t * t * t
st5 += weight * t * t * t * t * t
st6 += weight * t * t * t * t * t * t
sw += weight * weight
sp += weight * p[t]
spt += weight * p[t] * t
spt2 += weight * p[t] * t * t
spt3 += weight * p[t] * t * t * t
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
mat = matrix.new<float>(n+1, n+1)
vec = matrix.new<float>(n+1, 1)
mat_data = array.from(s1, st, st2, st3, st4, st5, st6)
vec_data = array.from(sp, spt, spt2, spt3)
for row = 0 to n
vec.set(row, 0, vec_data.get(row))
for column = 0 to n
mat.set(row, column, mat_data.get(row+column))
coefs = mat.inv().mult(vec) // mat^(-1) * vec
a = coefs.get(0, 0)
b = n > 0 ? coefs.get(1, 0) : 0
c = n > 1 ? coefs.get(2, 0) : 0
d = n > 2 ? coefs.get(3, 0) : 0
xnp = get_xnp(a, b, c, d, offset=offset)
var df_factor = get_df_factor(n, s1, sw, use_df)
sum_sqd = 0.0
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
est = a + b*t + c*t*t + d*t*t*t
sum_sqd += weight * (p[t]-est) * (p[t]-est)
xnd = math.sqrt(sum_sqd / s1 * df_factor)
[xnp, xnd]xnpについては、次のget_xnp()で近似曲線上の値を求めています。offset=0で使っているので最新のbarにおける値です。offset=1として次のbarにおける値にしたりするのも面白そうです。
get_xnp(float a, float b, float c, float d, float offset=0) =>
a + b*(-offset) + c*(-offset)*(-offset) + d*(-offset)*(-offset)*(-offset)use_dfがtrueのときには、次のget_df_factor()を使って自由度を有効サンプル数で調整するようにしてあります。
// degrees of freedomを調整する係数を求める
get_df_factor(int n, float s1, float sw, bool use_df) =>
neff = s1*s1 / sw // 有効サンプル数
df_factor = use_df ? 1 / (1 - (n+1)/neff) : 1汎用高速化版のxnb()
次のxnb()は、移動平均の種類によらない汎用的な高速化を行ったものです。重み付き総和の代わりに重み付き平均を使い、TradingView内蔵のta.sma()などを裏で使っています。行列演算は展開して普通の式にしてあります。
export xnb(simple enum_ma ma, int n, float p, float g, bool use_df, int cutoff, float offset=0) =>
var length = get_length(ma, g)
var loop_max = get_loop_max(ma, g, length, cutoff)
var s1 = get_stn(ma, 0, g, length) // wsum(t^0) = wsum(1) (wsum(x)はxの重み付き総和)
var m1 = get_mtn(ma, 0, g, length) // wmean(t^0) = wmean(1) = 1(wmean(x)はxの重み付き平均)
var mt = get_mtn(ma, 1, g, length) // wmean(t^1) = wmean(t)
var mt2 = get_mtn(ma, 2, g, length) // wmean(t^2)
var mt3 = get_mtn(ma, 3, g, length) // wmean(t^3)
var mt4 = get_mtn(ma, 4, g, length) // wmean(t^4)
var mt5 = get_mtn(ma, 5, g, length) // wmean(t^5)
var mt6 = get_mtn(ma, 6, g, length) // wmean(t^6)
var sw = get_sw(ma, g, length) // wsum(weight)
mp = ma(ma, p, length) // wmean(p)
mp2 = ma(ma, p*p, length) // wmean(p^2)
spt = 0.0 // wsum(p*t)
spt2 = 0.0 // wsum(p*t^2)
spt3 = 0.0 // wsum(p*t^3)
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
spt += weight * p[t] * t
spt2 += weight * p[t] * t * t
spt3 += weight * p[t] * t * t * t
mpt = spt / s1 // wmean(p*t) emaのspt*は途中打ち切りなので注意
mpt2 = spt2 / s1 // wmean(p*t^2)
mpt3 = spt3 / s1 // wmean(p*t^3)
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
det = switch n
0 => 1
1 => mt2*m1 - mt*mt
2 => m1*(mt2*mt4 - mt3*mt3) - mt*(mt*mt4 - mt2*mt3) + mt2*(mt*mt3 - mt2*mt2)
3 => m1*mt2*mt4*mt6 - m1*mt2*mt5*mt5 - m1*mt3*mt3*mt6 + 2*m1*mt3*mt4*mt5 - m1*mt4*mt4*mt4 - mt*mt*mt4*mt6 + mt*mt*mt5*mt5 + 2*mt*mt2*mt3*mt6 - 2*mt*mt2*mt4*mt5 - 2*mt*mt3*mt3*mt5 + 2*mt*mt3*mt4*mt4 - mt2*mt2*mt2*mt6 + 2*mt2*mt2*mt3*mt5 + mt2*mt2*mt4*mt4 - 3*mt2*mt3*mt3*mt4 + mt3*mt3*mt3*mt3
[a, b, c, d] = switch n
0 => [ mp / det, 0, 0, 0 ]
1 => [ (mp*mt2 - mt*mpt) / det, (mpt*m1 - mp*mt) / det, 0, 0]
2 => [ (mp*(mt2*mt4 - mt3*mt3) - mt*(mpt*mt4 - mpt2*mt3) + mt2*(mpt*mt3 - mpt2*mt2)) / det,
(m1*(mpt*mt4 - mpt2*mt3) - mp*(mt*mt4 - mt2*mt3) + mt2*(mt*mpt2 - mt2*mpt)) / det,
(m1*(mt2*mpt2 - mt3*mpt) - mt*(mt*mpt2 - mt2*mpt) + mp*(mt*mt3 - mt2*mt2)) / det, 0 ]
3 => [ ( -mp*(-mt2*mt4*mt6 + mt2*mt5*mt5 + mt3*mt3*mt6 - 2*mt3*mt4*mt5 + mt4*mt4*mt4) - mpt*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) + mpt2*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) + mpt3*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) ) / det,
( -mp*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) - mpt*(-m1*mt4*mt6 + m1*mt5*mt5 + mt2*mt2*mt6 - 2*mt2*mt3*mt5 + mt3*mt3*mt4) - mpt2*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) + mpt3*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) ) / det,
( mp*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) - mpt*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) - mpt2*(-m1*mt2*mt6 + m1*mt4*mt4 + mt*mt*mt6 - 2*mt*mt3*mt4 + mt2*mt3*mt3) - mpt3*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) ) / det,
( mp*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) + mpt*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) - mpt2*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) - mpt3*(-m1*mt2*mt4 + m1*mt3*mt3 + mt*mt*mt4 - 2*mt*mt2*mt3 + mt2*mt2*mt2) ) / det ]
var df_factor = get_df_factor(n, s1, sw, use_df)
[ get_xnp(a, b, c, d, offset=offset),
get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor) ]xndを求めるためにxnd_basic()では残差平方をループで処理していました。xnb()では次のget_xnd()を使い、平均値を1回処理するだけで実現しています。無相関のため消える項が多いためここまで簡単になります。
pine_normalized_positive_min という小さい数以上に制限しているのは、
計算誤差で負の数になることやその後の0除算を防止するためです。
get_xnd(float a, float b, float c, float d, float mp, float mpt, float mpt2, float mpt3, float mp2, float df_factor) =>
math.sqrt(math.max(pine_normalized_positive_min, mp2 - (a*mp + b*mpt + c*mpt2 + d*mpt3)) * df_factor)専用高速化版のsnb()、lnb()、enb()
次のsnb()、lnb()、enb()はそれぞれSMA、LWMA、EMA専用に高速化したものです。
snb()とlnb()はこのままではそれほど速くないですが、「sptn計算1」の部分の代わりに「sptn計算2」を使うと高速になります。(条件によっては誤差が大きく累積するので適宜リセットが必要になります。)
enb()は指数重みの性質を使うことでループ処理を排除して高速化しています。
export snb(int n, float p, float g, bool use_df, float offset=0) =>
var ma = enum_ma.sma
var length = get_length(ma, g)
var loop_max = get_loop_max(ma, g, length, 0)
var s1 = length
var m1 = 1.0
var mt = get_mtn(ma, 1, g, length) // wmean(t^1) = wmean(t)
var mt2 = get_mtn(ma, 2, g, length) // wmean(t^2)
var mt3 = get_mtn(ma, 3, g, length) // wmean(t^3)
var mt4 = get_mtn(ma, 4, g, length) // wmean(t^4)
var mt5 = get_mtn(ma, 5, g, length) // wmean(t^5)
var mt6 = get_mtn(ma, 6, g, length) // wmean(t^6)
var sw = get_sw(ma, g, length) // wsum(weight)
mp = ma(ma, p, length) // wmean(p)
mp2 = ma(ma, p*p, length) // wmean(p^2)
// sptn計算1
spt = 0.0 // wsum(p*t)
spt2 = 0.0 // wsum(p*t^2)
spt3 = 0.0 // wsum(p*t^3)
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
spt += weight * p[t] * t
spt2 += weight * p[t] * t * t
spt3 += weight * p[t] * t * t * t
// sptn計算2(高速だが誤差が累積するので必要ならリセットしながら使う)
// [spt, spt2, spt3, unused] = get_sma_sptn(p, length)
mpt = spt / s1
mpt2 = spt2 / s1
mpt3 = spt3 / s1
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
det = switch n
0 => 1
1 => mt2*m1 - mt*mt
2 => m1*(mt2*mt4 - mt3*mt3) - mt*(mt*mt4 - mt2*mt3) + mt2*(mt*mt3 - mt2*mt2)
3 => m1*mt2*mt4*mt6 - m1*mt2*mt5*mt5 - m1*mt3*mt3*mt6 + 2*m1*mt3*mt4*mt5 - m1*mt4*mt4*mt4 - mt*mt*mt4*mt6 + mt*mt*mt5*mt5 + 2*mt*mt2*mt3*mt6 - 2*mt*mt2*mt4*mt5 - 2*mt*mt3*mt3*mt5 + 2*mt*mt3*mt4*mt4 - mt2*mt2*mt2*mt6 + 2*mt2*mt2*mt3*mt5 + mt2*mt2*mt4*mt4 - 3*mt2*mt3*mt3*mt4 + mt3*mt3*mt3*mt3
[a, b, c, d] = switch n
0 => [ mp / det, 0, 0, 0 ]
1 => [ (mp*mt2 - mt*mpt) / det, (mpt*m1 - mp*mt) / det, 0, 0]
2 => [ (mp*(mt2*mt4 - mt3*mt3) - mt*(mpt*mt4 - mpt2*mt3) + mt2*(mpt*mt3 - mpt2*mt2)) / det,
(m1*(mpt*mt4 - mpt2*mt3) - mp*(mt*mt4 - mt2*mt3) + mt2*(mt*mpt2 - mt2*mpt)) / det,
(m1*(mt2*mpt2 - mt3*mpt) - mt*(mt*mpt2 - mt2*mpt) + mp*(mt*mt3 - mt2*mt2)) / det, 0 ]
3 => [ ( -mp*(-mt2*mt4*mt6 + mt2*mt5*mt5 + mt3*mt3*mt6 - 2*mt3*mt4*mt5 + mt4*mt4*mt4) - mpt*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) + mpt2*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) + mpt3*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) ) / det,
( -mp*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) - mpt*(-m1*mt4*mt6 + m1*mt5*mt5 + mt2*mt2*mt6 - 2*mt2*mt3*mt5 + mt3*mt3*mt4) - mpt2*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) + mpt3*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) ) / det,
( mp*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) - mpt*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) - mpt2*(-m1*mt2*mt6 + m1*mt4*mt4 + mt*mt*mt6 - 2*mt*mt3*mt4 + mt2*mt3*mt3) - mpt3*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) ) / det,
( mp*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) + mpt*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) - mpt2*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) - mpt3*(-m1*mt2*mt4 + m1*mt3*mt3 + mt*mt*mt4 - 2*mt*mt2*mt3 + mt2*mt2*mt2) ) / det ]
var df_factor = get_df_factor(n, s1, sw, use_df)
[ get_xnp(a, b, c, d, offset=offset),
get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor) ]
export lnb(int n, float p, float g, bool use_df, float offset=0) =>
var ma = enum_ma.lma
var length = get_length(ma, g)
var loop_max = get_loop_max(ma, g, length, 0)
var s1 = get_stn(ma, 0, g, length) // wsum(t^0) = wsum(1)
var m1 = 1.0
var mt = get_mtn(ma, 1, g, length) // wmean(t^1) = wmean(t)
var mt2 = get_mtn(ma, 2, g, length) // wmean(t^2)
var mt3 = get_mtn(ma, 3, g, length) // wmean(t^3)
var mt4 = get_mtn(ma, 4, g, length) // wmean(t^4)
var mt5 = get_mtn(ma, 5, g, length) // wmean(t^5)
var mt6 = get_mtn(ma, 6, g, length) // wmean(t^6)
var sw = get_sw(ma, g, length) // wsum(weight)
mp = ma(ma, p, length) // wmean(p)
mp2 = ma(ma, p*p, length) // wmean(p^2)
// sptn計算1
spt = 0.0 // wsum(p*t)
spt2 = 0.0 // wsum(p*t^2)
spt3 = 0.0 // wsum(p*t^3)
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
spt += weight * p[t] * t
spt2 += weight * p[t] * t * t
spt3 += weight * p[t] * t * t * t
// sptn計算2(高速だが誤差が累積するので必要ならリセットしながら使う)
// [sma_spt, sma_spt2, sma_spt3, sma_spt4] = get_sma_sptn(p, math.ceil(length))
// spt = length*sma_spt - sma_spt2 // wsum(p[t]*t) = sum((length-t)*p[t]*t) = sum(length*p[t]*t - p[t]*t^2) = length*sum(p*t) - sum(p*t^2)
// spt2 = length*sma_spt2 - sma_spt3
// spt3 = length*sma_spt3 - sma_spt4 // wsum(p[t]*t^3) = sum((length-t)*p[t]*t^3) = length*sum(p*t^3) - sum(p*t^4)
mpt = spt / s1 // wmean(p*t)
mpt2 = spt2 / s1 // wmean(p*t^2)
mpt3 = spt3 / s1 // wmean(p*t^3)
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
det = switch n
0 => 1
1 => mt2*m1 - mt*mt
2 => m1*(mt2*mt4 - mt3*mt3) - mt*(mt*mt4 - mt2*mt3) + mt2*(mt*mt3 - mt2*mt2)
3 => m1*mt2*mt4*mt6 - m1*mt2*mt5*mt5 - m1*mt3*mt3*mt6 + 2*m1*mt3*mt4*mt5 - m1*mt4*mt4*mt4 - mt*mt*mt4*mt6 + mt*mt*mt5*mt5 + 2*mt*mt2*mt3*mt6 - 2*mt*mt2*mt4*mt5 - 2*mt*mt3*mt3*mt5 + 2*mt*mt3*mt4*mt4 - mt2*mt2*mt2*mt6 + 2*mt2*mt2*mt3*mt5 + mt2*mt2*mt4*mt4 - 3*mt2*mt3*mt3*mt4 + mt3*mt3*mt3*mt3
[a, b, c, d] = switch n
0 => [ mp / det, 0, 0, 0 ]
1 => [ (mp*mt2 - mt*mpt) / det, (mpt*m1 - mp*mt) / det, 0, 0]
2 => [ (mp*(mt2*mt4 - mt3*mt3) - mt*(mpt*mt4 - mpt2*mt3) + mt2*(mpt*mt3 - mpt2*mt2)) / det,
(m1*(mpt*mt4 - mpt2*mt3) - mp*(mt*mt4 - mt2*mt3) + mt2*(mt*mpt2 - mt2*mpt)) / det,
(m1*(mt2*mpt2 - mt3*mpt) - mt*(mt*mpt2 - mt2*mpt) + mp*(mt*mt3 - mt2*mt2)) / det, 0 ]
3 => [ ( -mp*(-mt2*mt4*mt6 + mt2*mt5*mt5 + mt3*mt3*mt6 - 2*mt3*mt4*mt5 + mt4*mt4*mt4) - mpt*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) + mpt2*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) + mpt3*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) ) / det,
( -mp*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) - mpt*(-m1*mt4*mt6 + m1*mt5*mt5 + mt2*mt2*mt6 - 2*mt2*mt3*mt5 + mt3*mt3*mt4) - mpt2*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) + mpt3*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) ) / det,
( mp*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) - mpt*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) - mpt2*(-m1*mt2*mt6 + m1*mt4*mt4 + mt*mt*mt6 - 2*mt*mt3*mt4 + mt2*mt3*mt3) - mpt3*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) ) / det,
( mp*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) + mpt*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) - mpt2*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) - mpt3*(-m1*mt2*mt4 + m1*mt3*mt3 + mt*mt*mt4 - 2*mt*mt2*mt3 + mt2*mt2*mt2) ) / det ]
var df_factor = get_df_factor(n, s1, sw, use_df)
[ get_xnp(a, b, c, d, offset=offset),
get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor) ]
export enb(int n, float p, float g, bool use_df=true, float offset=0) =>
var ma = enum_ma.ema
var length = get_length(ma, g)
var s1 = g + 1
var sw = get_sw(ma, g, length) // wsum(weight)
var g2 = g * g
var g3 = g * g * g
var g4 = g * g * g * g
var h = g + 1
var h2 = h * h
var h3 = h * h * h
mp = ema(p, length) // wmean(p)
mmwp = ema(mp, length) // wmean(wmean(p))
mmmwp = ema(mmwp, length) // wmean(wmean(wmean(p)))
mmmmwp = ema(mmmwp, length) // wmean(wmean(wmean(wmean(p))))
mp2 = ema(p*p, length) // wmean(p^2)
mpt = h*mmwp - mp // wmean(p*t)
mpt2 = 2*h2*mmmwp - 3*h*mmwp + mp // wmean(p*t^2)
mpt3 = 6*h3*mmmmwp - 12*h2*mmmwp + 7*h*mmwp - mp // wmean(p*t^3)
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
[a, b, c, d] = switch n
0 => [ mp, 0, 0, 0 ]
1 => [ 2*mp - mmwp,
(mmwp - mp)/g, 0, 0]
2 => [ (mpt2 - 3*(2*g+1)*mpt + 2*(3*g2+3*g+1)*mp) / (2*h2),
(-(4*g+1)*mpt2 + (2*g+1)*(10*g+1)*mpt - 6*g2*(2*g+1)*mp) / (4*g2*h2),
(mpt2 - (4*g+1)*mpt + 2*g2*mp) / (4*g2*h2), 0 ]
3 => [ (6*(2*g+1)*(2*g2+2*g+1)*mp - (36*g2+36*g+11)*mpt + 6*(2*g+1)*mpt2 - mpt3) / (h3*6),
(-6*(36*g2+36*g+11)*g3*mp + (504*g4+612*g3+270*g2+45*g+4)*mpt - 6*(33*g3+30*g2+9*g+1)*mpt2 + (18*g2+9*g+2)*mpt3) / (36*h3*g3),
(12*(2*g+1)*g3*mp - 2*(33*g3+30*g2+9*g+1)*mpt + 3*(2*g+1)*(5*g+1)*mpt2 - (3*g+1)*mpt3) / (12*h3*g3),
(-6*g3*mp + (18*g2+9*g+2)*mpt - 3*(3*g+1)*mpt2 + mpt3) / (36*h3*g3)]
var df_factor = get_df_factor(n, s1, sw, use_df)
[ get_xnp(a, b, c, d, offset=offset),
get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor) ]売買シグナル計算例
ボリンジャーバンドなどで各自が使っている方式が有効だと思いますが、例として単純な売買シグナルを置いておきます。
次のコードの例では、xnpの傾きを重みの重心gとxndで規格化したslopeという変数を作り、適宜なめらかにしたあとでslope2pos3()を使って -2、0、+2 をとるposを計算しています。slope2pos3()は +entryを上回ってから+exitを下回るまで買いに、-entryを下回ってから-exitを上回るまで売りになるものです。
export slope2pos3(float slope, float entry, float exit) =>
var pos = 0
pos :=
dd11.is_p(slope - entry) ? +2 : // +entryより上なら買い
dd11.is_n(slope + entry) ? -2 : // -entryより下なら売り
pos > 0 and dd11.is_n(slope - exit) ? 0 : // +exitより下なら買い手仕舞い
pos < 0 and dd11.is_p(slope + exit) ? 0 : // -exitより上なら売り手仕舞い
pos
export pos2col(int pos, bool not_enough=false, color color_bull0=na, color color_bull1=na, color color_bear0=na, color color_bear1=na, color color_neutral=na) =>
var col_bull0 = nz(color_bull0, dd11.red(tune_s=-75, tune_v=+90)) // 弱買
var col_bull1 = nz(color_bull1, dd11.red(tune_s=-50, tune_v=+70)) // 買
var col_bear0 = nz(color_bear0, dd11.blue(tune_s=-75, tune_v=+90)) // 弱売
var col_bear1 = nz(color_bear1, dd11.blue(tune_s=-50, tune_v=+70)) // 売
var col_neutral = nz(color_neutral, dd11.gray(85)) // 中立
not_enough ? na :
pos > +1 ? col_bull1 :
pos < -1 ? col_bear1 :
pos > 0 ? col_bull0 :
pos < 0 ? col_bear0 :
col_neutral
slope = (xnp - nz(xnp[1], xnp)) * g / xnd / (n+1) * 2
var g_for_slope = math.max(1, g / g_div) // 1より小さい値でも動く
[e0b_slope, unused] = xnb.enb(0, slope, g_for_slope, use_df)
pos = xnb.slope2pos3(e0b_slope, entry, exit)このシグナルで色分けしたxnbのチャートは次のようになります。
USDJPY g=1W(1週間)
USDJPY g=1D(1日)
EURUSD g=1W
EURUSD g=1D
xnbライブラリの全体ソース
ここまでで出てこなかった関数も含むライブラリの全体は次のとおりです。
// This Pine Script® code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © dig_dig_11
//@version=6
//@description 重み付き多項式近似バンド(xnb)のライブラリ
library("libxnb", overlay=true)
import dig_dig_11/main/11 as dd11
stop_if_not_zero(val) =>
if val != 0
runtime.error(str.format("{0} is not zero", val))
true // 値を返すことだけに意味がある
var pine_normalized_positive_min = math.pow(bar_index+2, -1022) // floatやintの正の正規化された最小値
var checked = stop_if_not_zero(bar_index) // bar_indexが0でなければエラー終了させる
//@enum 移動平均の列挙型
//@field sma SMA(単純移動平均)
//@field lwma LWMA(線形加重移動平均)
//@field ema EMA(指数移動平均)
export enum enum_ma
sma = "sma"
lma = "lwma"
ema = "ema"
sma(float src, float length) =>
var i = int(length) // lengthの整数部分
var f = length - i // lengthの小数部分
var loop_max = i - 1 // 整数部の回数だけループする
sum = 0.0
for t = 0 to loop_max
sum += src[t] // i本のbarの価格を加える
sum += src[i] * f // さらに1つ前のbarの価格をfで調整して加える
sum / length
sma_pbl(float src, float length) => // pblはpowered by libraryの略。有用な整数期間のlibraryを活かせます
var i = int(length)
var f = length - i
(ta.sma(src, i)*i + src[i]*f) / length // 直近i本の価格合計はta.sma()から計算
lma(float src, float length) =>
var i = int(length)
var loop_max = math.ceil(length) - 1 // 整数部の回数だけループするが、端数があれば1追加
sum = 0.0
sum_weight = 0.0
weight = length
for t = 0 to loop_max
sum += src[t] * weight
sum_weight += weight
weight -= 1
sum / sum_weight
lma_pbl(float src, float length) =>
var i = int(length)
var f = length - i
var weight_lma = (i+1) * i / 2
var weight_sma = (i+1) * f
(ta.wma(src, i)*weight_lma + ta.sma(src, i+1)*weight_sma) / (weight_lma + weight_sma)
ema(float src, float length) =>
var alpha = 2 / (length + 1)
var ema = src
if na(src)
na
else
ema := nz(ema, src) // 最初のbarがnaでなければこの行は不要
ema += alpha * (src - ema)
// ema := ema*(1-alpha) + src*alpha // この書き方でも結果は同じ
ma(simple enum_ma ma, float src, float length) => // warning回避でsimpleをつけた
switch ma
// enum_ma.sma => sma(src, length)
enum_ma.sma => sma_pbl(src, length)
// enum_ma.lma => lma(src, length)
enum_ma.lma => lma_pbl(src, length)
enum_ma.ema => ema(src, length)
=> na
//@function 重心gをta.sma()、ta.wma()、ta.ema()で使われるlengthに換算する
//@param ma xnbのx(使用する移動平均)
//@param g 重みの重心(単位はbar)
//@returns 重心gをta.sma()、ta.wma()、ta.ema()で使われるlengthに換算した値
export get_length(simple enum_ma ma, float g) =>
switch ma
enum_ma.sma =>
i = math.floor(2*g + 1)
i * (i + 1) / (2*(i - g))
// 2*g + 1 // 近似計算の場合
enum_ma.lma =>
i = math.floor(3*g + 1)
i * (3*g - 2*i - 1) / (3*(2*g - i))
// 3*g + 1 // 近似計算の場合
enum_ma.ema =>
2*g + 1
=>
na
get_loop_max(simple enum_ma ma, float g, float length, int cutoff) =>
ma == enum_ma.ema ?
int(g * cutoff) :
math.ceil(length) - 1 // 整数部の回数だけループするが、端数があれば1追加
get_weight(simple enum_ma ma, int t, float g, float length) =>
var i = int(length)
var f = length - i
switch ma
enum_ma.sma => t < i ? 1 : f
enum_ma.lma => length - t
enum_ma.ema => math.pow(g/(g+1), t)
=> na
// t^n の重み付き総和を求める wsum(t^n)
get_stn(simple enum_ma ma, int n, float g, float length) =>
var i = int(length)
var f = length - i
var g2 = g * g
var g3 = g * g * g
var g4 = g * g * g * g
var g5 = g * g * g * g * g
var g6 = g * g * g * g * g * g
var i2 = i * i
var i3 = i * i * i
var i4 = i * i * i * i
var i5 = i * i * i * i * i
var i6 = i * i * i * i * i * i
switch ma
enum_ma.sma =>
switch n
0 => f + i
1 => f*i + i*(i-1)/2
2 => f*i2 + i*(i-1)*(2*i-1)/6
3 => f*i3 + i2*(i-1)*(i-1)/4
4 => f*i4 + i*(i-1)*(2*i-1)*(3*i2-3*i-1)/30
5 => f*i5 + i2*(i-1)*(i-1)*(2*i2-2*i-1)/12
6 => f*i6 + i*(i-1)*(2*i-1)*(3*i4-6*i3+3*i+1)/42
=> na
enum_ma.lma =>
switch n
0 => (i+1)*(i+2*f)/2
1 => i*(i+1)*(i + 3*f-1)/6
2 => i*(i+1)*(i2 + (4*f-1)*i + 2*f)/12
3 => i*(i+1)*(3*i3 + (15*f-3)*i2 + (15*f-2)*i + 2)/60
4 => i*(i+1)*(2*i4 + (12*f-2)*i3 + (18*f-3)*i2 + (2*f+3)*i - 2*f)/60
5 => i*(i+1)*(2*i5 + (14*f-2)*i4 + (28*f-5)*i3 + (7*f+5)*i2 + (2-7*f)*i - 2)/84
6 => i*(i+1)*(3*i6 + (24*f-3)*i5 + (60*f-11)*i4 + (24*f+11)*i3 + (24*f+10)*i2 + (-4*f-10)*i + 4*f-10)/168
=> na
enum_ma.ema =>
switch n
0 => g+1
1 => g*(g+1)
2 => g*(g+1)*(2*g+1)
3 => g*(g+1)*(6*g2 + 6*g + 1)
4 => g*(g+1)*(24*g3 + 36*g2 + 14*g + 1)
5 => g*(g+1)*(120*g4 + 240*g3 + 150*g2 + 30*g + 1)
6 => g*(g+1)*(720*g5 + 1800*g4 + 1560*g3 + 540*g2 + 62*g + 1)
=> na
=> na
// t^n の重み付き平均を求める
get_mtn(simple enum_ma ma, int n, float g, float length) =>
get_stn(ma, n, g, length) / get_stn(ma, 0, g, length)
// n=1〜4について、sma用の p*t^n の重み付き総和を求める
get_sma_sptn(float p, float length) =>
var i = int(length)
var f = length - i
var i2 = i * i
var i3 = i * i * i
var i4 = i * i * i * i
var sp = 0.0
var spt = 0.0
var spt2 = 0.0
var spt3 = 0.0
var spt4 = 0.0
spt4 += 4*spt3 + 6*spt2 + 4*spt + sp - i4*nz(p[i]) // 1つ前のbarの値を使って計算している
spt3 += 3*spt2 + 3*spt + sp - i3*nz(p[i])
spt2 += 2*spt + sp - i2*nz(p[i])
spt += sp - i*nz(p[i])
sp += p - nz(p[i])
[ spt + f*i *p[i],
spt2 + f*i2*p[i],
spt3 + f*i3*p[i],
spt4 + f*i4*p[i] ]
// 重みの重み付き総和を求める
get_sw(simple enum_ma ma, float g, float length) =>
var i = int(length)
var f = length - i
switch ma
enum_ma.sma => i + f*f
enum_ma.lma => (i+1)*(i*(2*i+1)/6 + i*f + f*f)
enum_ma.ema => (g+1)*(g+1)/(2*g+1)
=> na
// xnpを求める
get_xnp(float a, float b, float c, float d, float offset=0) =>
a + b*(-offset) + c*(-offset)*(-offset) + d*(-offset)*(-offset)*(-offset)
// offset=0決め打ち版を作ってもいい
// xndを求める
get_xnd(float a, float b, float c, float d, float mp, float mpt, float mpt2, float mpt3, float mp2, float df_factor) =>
math.sqrt(math.max(pine_normalized_positive_min, mp2 - (a*mp + b*mpt + c*mpt2 + d*mpt3)) * df_factor)
// degrees of freedomを調整する係数を求める
get_df_factor(int n, float s1, float sw, bool use_df) =>
neff = s1*s1 / sw // 有効サンプル数
df_factor = use_df ? 1 / (1 - (n+1)/neff) : 1
// var a_str = array.from(
// "s1", str.tostring(s1, "0.0"),
// "sw", str.tostring(sw, "0.0"),
// "neff", str.tostring(neff, "0.0"),
// "df_factor", str.tostring(df_factor, " 0.000"))
// var tbl = dd11.get_table(a_str, columns=2, rows=4, position=position.bottom_right)
// df_factor
//@function 重み付き多項式近似バンドxnbの原理的な実装
//@param ma xnbのx(使用する移動平均)
//@param n xnbのn(多項式近似の次数)
//@param p 価格
//@param g 重みの重心(単位はbar)
//@param use_df degrees of freedomを使って補正するか
//@param cutoff EMAの場合にループによる集計をg×cutoffまでで打ち切る
//@param offset xnpを計算するbarの位置(defaultの0は現在を表す)
//@returns 多項式近似値xnpと残差の標準偏差xnd
export xnb_basic(simple enum_ma ma, int n, float p, float g, bool use_df, int cutoff, float offset=0) =>
var length = get_length(ma, g) // ta.sma/wma/ema()互換のlength
var loop_max = get_loop_max(ma, g, length, cutoff)
var s1 = 0.0 // wsum(1) (wsum(x)はxの重み付き総和)
var st = 0.0 // wsum(t)
var st2 = 0.0 // wsum(t^2)
var st3 = 0.0 // wsum(t^3)
var st4 = 0.0 // wsum(t^4)
var st5 = 0.0 // wsum(t^5)
var st6 = 0.0 // wsum(t^6)
var sw = 0.0 // wsum(weight)
sp = 0.0 // wsum(p)
spt = 0.0 // wsum(p*t)
spt2 = 0.0 // wsum(p*t^2)
spt3 = 0.0 // wsum(p*t^3)
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
if bar_index == 0 // bar_indexで変わらないものは最初だけ計算
s1 += weight
st += weight * t
st2 += weight * t * t
st3 += weight * t * t * t
st4 += weight * t * t * t * t
st5 += weight * t * t * t * t * t
st6 += weight * t * t * t * t * t * t
sw += weight * weight
sp += weight * p[t]
spt += weight * p[t] * t
spt2 += weight * p[t] * t * t
spt3 += weight * p[t] * t * t * t
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
mat = matrix.new<float>(n+1, n+1)
vec = matrix.new<float>(n+1, 1)
mat_data = array.from(s1, st, st2, st3, st4, st5, st6)
vec_data = array.from(sp, spt, spt2, spt3)
for row = 0 to n
vec.set(row, 0, vec_data.get(row))
for column = 0 to n
mat.set(row, column, mat_data.get(row+column))
coefs = mat.inv().mult(vec) // mat^(-1) * vec
a = coefs.get(0, 0)
b = n > 0 ? coefs.get(1, 0) : 0
c = n > 1 ? coefs.get(2, 0) : 0
d = n > 2 ? coefs.get(3, 0) : 0
xnp = get_xnp(a, b, c, d, offset=offset)
var df_factor = get_df_factor(n, s1, sw, use_df)
sum_sqd = 0.0
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
est = a + b*t + c*t*t + d*t*t*t
sum_sqd += weight * (p[t]-est) * (p[t]-est)
xnd = math.sqrt(sum_sqd / s1 * df_factor)
[xnp, xnd]
//@function 重み付き多項式近似バンドxnbの汎用高速化実装
//@param ma xnbのx(使用する移動平均)
//@param n xnbのn(多項式近似の次数)
//@param p 価格
//@param g 重みの重心(単位はbar)
//@param use_df degrees of freedomを使って補正するか
//@param cutoff EMAの場合にループによる集計をg×cutoffまでで打ち切る
//@param offset xnpを計算するbarの位置(defaultの0は現在を表す)
//@returns 多項式近似値xnpと残差の標準偏差xnd
export xnb(simple enum_ma ma, int n, float p, float g, bool use_df, int cutoff, float offset=0) =>
var length = get_length(ma, g)
var loop_max = get_loop_max(ma, g, length, cutoff)
var s1 = get_stn(ma, 0, g, length) // wsum(t^0) = wsum(1) (wsum(x)はxの重み付き総和)
var m1 = get_mtn(ma, 0, g, length) // wmean(t^0) = wmean(1) = 1(wmean(x)はxの重み付き平均)
var mt = get_mtn(ma, 1, g, length) // wmean(t^1) = wmean(t)
var mt2 = get_mtn(ma, 2, g, length) // wmean(t^2)
var mt3 = get_mtn(ma, 3, g, length) // wmean(t^3)
var mt4 = get_mtn(ma, 4, g, length) // wmean(t^4)
var mt5 = get_mtn(ma, 5, g, length) // wmean(t^5)
var mt6 = get_mtn(ma, 6, g, length) // wmean(t^6)
var sw = get_sw(ma, g, length) // wsum(weight)
mp = ma(ma, p, length) // wmean(p)
mp2 = ma(ma, p*p, length) // wmean(p^2)
spt = 0.0 // wsum(p*t)
spt2 = 0.0 // wsum(p*t^2)
spt3 = 0.0 // wsum(p*t^3)
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
spt += weight * p[t] * t
spt2 += weight * p[t] * t * t
spt3 += weight * p[t] * t * t * t
mpt = spt / s1 // wmean(p*t) emaのspt*は途中打ち切りなので注意
mpt2 = spt2 / s1 // wmean(p*t^2)
mpt3 = spt3 / s1 // wmean(p*t^3)
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
det = switch n
0 => 1
1 => mt2*m1 - mt*mt
2 => m1*(mt2*mt4 - mt3*mt3) - mt*(mt*mt4 - mt2*mt3) + mt2*(mt*mt3 - mt2*mt2)
3 => m1*mt2*mt4*mt6 - m1*mt2*mt5*mt5 - m1*mt3*mt3*mt6 + 2*m1*mt3*mt4*mt5 - m1*mt4*mt4*mt4 - mt*mt*mt4*mt6 + mt*mt*mt5*mt5 + 2*mt*mt2*mt3*mt6 - 2*mt*mt2*mt4*mt5 - 2*mt*mt3*mt3*mt5 + 2*mt*mt3*mt4*mt4 - mt2*mt2*mt2*mt6 + 2*mt2*mt2*mt3*mt5 + mt2*mt2*mt4*mt4 - 3*mt2*mt3*mt3*mt4 + mt3*mt3*mt3*mt3
[a, b, c, d] = switch n
0 => [ mp / det, 0, 0, 0 ]
1 => [ (mp*mt2 - mt*mpt) / det, (mpt*m1 - mp*mt) / det, 0, 0]
2 => [ (mp*(mt2*mt4 - mt3*mt3) - mt*(mpt*mt4 - mpt2*mt3) + mt2*(mpt*mt3 - mpt2*mt2)) / det,
(m1*(mpt*mt4 - mpt2*mt3) - mp*(mt*mt4 - mt2*mt3) + mt2*(mt*mpt2 - mt2*mpt)) / det,
(m1*(mt2*mpt2 - mt3*mpt) - mt*(mt*mpt2 - mt2*mpt) + mp*(mt*mt3 - mt2*mt2)) / det, 0 ]
3 => [ ( -mp*(-mt2*mt4*mt6 + mt2*mt5*mt5 + mt3*mt3*mt6 - 2*mt3*mt4*mt5 + mt4*mt4*mt4) - mpt*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) + mpt2*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) + mpt3*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) ) / det,
( -mp*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) - mpt*(-m1*mt4*mt6 + m1*mt5*mt5 + mt2*mt2*mt6 - 2*mt2*mt3*mt5 + mt3*mt3*mt4) - mpt2*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) + mpt3*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) ) / det,
( mp*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) - mpt*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) - mpt2*(-m1*mt2*mt6 + m1*mt4*mt4 + mt*mt*mt6 - 2*mt*mt3*mt4 + mt2*mt3*mt3) - mpt3*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) ) / det,
( mp*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) + mpt*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) - mpt2*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) - mpt3*(-m1*mt2*mt4 + m1*mt3*mt3 + mt*mt*mt4 - 2*mt*mt2*mt3 + mt2*mt2*mt2) ) / det ]
var df_factor = get_df_factor(n, s1, sw, use_df)
[ get_xnp(a, b, c, d, offset=offset),
get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor) ]
//@function 重み付き多項式近似バンドxnbのSMA専用高速化実装
//@param n xnbのn(多項式近似の次数)
//@param p 価格
//@param g 重みの重心(単位はbar)
//@param use_df degrees of freedomを使って補正するか
//@param cutoff EMAの場合にループによる集計をg×cutoffまでで打ち切る
//@param offset xnpを計算するbarの位置(defaultの0は現在を表す)
//@returns 多項式近似値xnpと残差の標準偏差xnd
export snb(int n, float p, float g, bool use_df, float offset=0) =>
var ma = enum_ma.sma
var length = get_length(ma, g)
var loop_max = get_loop_max(ma, g, length, 0)
var s1 = length
var m1 = 1.0
var mt = get_mtn(ma, 1, g, length) // wmean(t^1) = wmean(t)
var mt2 = get_mtn(ma, 2, g, length) // wmean(t^2)
var mt3 = get_mtn(ma, 3, g, length) // wmean(t^3)
var mt4 = get_mtn(ma, 4, g, length) // wmean(t^4)
var mt5 = get_mtn(ma, 5, g, length) // wmean(t^5)
var mt6 = get_mtn(ma, 6, g, length) // wmean(t^6)
var sw = get_sw(ma, g, length) // wsum(weight)
mp = ma(ma, p, length) // wmean(p)
mp2 = ma(ma, p*p, length) // wmean(p^2)
// sptn計算1
spt = 0.0 // wsum(p*t)
spt2 = 0.0 // wsum(p*t^2)
spt3 = 0.0 // wsum(p*t^3)
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
spt += weight * p[t] * t
spt2 += weight * p[t] * t * t
spt3 += weight * p[t] * t * t * t
// sptn計算2(高速だが誤差が累積するので必要ならリセットしながら使う)
// [spt, spt2, spt3, unused] = get_sma_sptn(p, length)
mpt = spt / s1
mpt2 = spt2 / s1
mpt3 = spt3 / s1
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
det = switch n
0 => 1
1 => mt2*m1 - mt*mt
2 => m1*(mt2*mt4 - mt3*mt3) - mt*(mt*mt4 - mt2*mt3) + mt2*(mt*mt3 - mt2*mt2)
3 => m1*mt2*mt4*mt6 - m1*mt2*mt5*mt5 - m1*mt3*mt3*mt6 + 2*m1*mt3*mt4*mt5 - m1*mt4*mt4*mt4 - mt*mt*mt4*mt6 + mt*mt*mt5*mt5 + 2*mt*mt2*mt3*mt6 - 2*mt*mt2*mt4*mt5 - 2*mt*mt3*mt3*mt5 + 2*mt*mt3*mt4*mt4 - mt2*mt2*mt2*mt6 + 2*mt2*mt2*mt3*mt5 + mt2*mt2*mt4*mt4 - 3*mt2*mt3*mt3*mt4 + mt3*mt3*mt3*mt3
[a, b, c, d] = switch n
0 => [ mp / det, 0, 0, 0 ]
1 => [ (mp*mt2 - mt*mpt) / det, (mpt*m1 - mp*mt) / det, 0, 0]
2 => [ (mp*(mt2*mt4 - mt3*mt3) - mt*(mpt*mt4 - mpt2*mt3) + mt2*(mpt*mt3 - mpt2*mt2)) / det,
(m1*(mpt*mt4 - mpt2*mt3) - mp*(mt*mt4 - mt2*mt3) + mt2*(mt*mpt2 - mt2*mpt)) / det,
(m1*(mt2*mpt2 - mt3*mpt) - mt*(mt*mpt2 - mt2*mpt) + mp*(mt*mt3 - mt2*mt2)) / det, 0 ]
3 => [ ( -mp*(-mt2*mt4*mt6 + mt2*mt5*mt5 + mt3*mt3*mt6 - 2*mt3*mt4*mt5 + mt4*mt4*mt4) - mpt*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) + mpt2*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) + mpt3*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) ) / det,
( -mp*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) - mpt*(-m1*mt4*mt6 + m1*mt5*mt5 + mt2*mt2*mt6 - 2*mt2*mt3*mt5 + mt3*mt3*mt4) - mpt2*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) + mpt3*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) ) / det,
( mp*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) - mpt*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) - mpt2*(-m1*mt2*mt6 + m1*mt4*mt4 + mt*mt*mt6 - 2*mt*mt3*mt4 + mt2*mt3*mt3) - mpt3*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) ) / det,
( mp*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) + mpt*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) - mpt2*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) - mpt3*(-m1*mt2*mt4 + m1*mt3*mt3 + mt*mt*mt4 - 2*mt*mt2*mt3 + mt2*mt2*mt2) ) / det ]
var df_factor = get_df_factor(n, s1, sw, use_df)
[ get_xnp(a, b, c, d, offset=offset),
get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor) ]
//@function 重み付き多項式近似バンドxnbのLWMA専用高速化実装
//@param n xnbのn(多項式近似の次数)
//@param p 価格
//@param g 重みの重心(単位はbar)
//@param use_df degrees of freedomを使って補正するか
//@param cutoff EMAの場合にループによる集計をg×cutoffまでで打ち切る
//@param offset xnpを計算するbarの位置(defaultの0は現在を表す)
//@returns 多項式近似値xnpと残差の標準偏差xnd
export lnb(int n, float p, float g, bool use_df, float offset=0) =>
var ma = enum_ma.lma
var length = get_length(ma, g)
var loop_max = get_loop_max(ma, g, length, 0)
var s1 = get_stn(ma, 0, g, length) // wsum(t^0) = wsum(1)
var m1 = 1.0
var mt = get_mtn(ma, 1, g, length) // wmean(t^1) = wmean(t)
var mt2 = get_mtn(ma, 2, g, length) // wmean(t^2)
var mt3 = get_mtn(ma, 3, g, length) // wmean(t^3)
var mt4 = get_mtn(ma, 4, g, length) // wmean(t^4)
var mt5 = get_mtn(ma, 5, g, length) // wmean(t^5)
var mt6 = get_mtn(ma, 6, g, length) // wmean(t^6)
var sw = get_sw(ma, g, length) // wsum(weight)
mp = ma(ma, p, length) // wmean(p)
mp2 = ma(ma, p*p, length) // wmean(p^2)
// sptn計算1
spt = 0.0 // wsum(p*t)
spt2 = 0.0 // wsum(p*t^2)
spt3 = 0.0 // wsum(p*t^3)
for t = 0 to loop_max
weight = get_weight(ma, t, g, length)
spt += weight * p[t] * t
spt2 += weight * p[t] * t * t
spt3 += weight * p[t] * t * t * t
// sptn計算2(高速だが誤差が累積するので必要ならリセットしながら使う)
// [sma_spt, sma_spt2, sma_spt3, sma_spt4] = get_sma_sptn(p, math.ceil(length))
// spt = length*sma_spt - sma_spt2 // wsum(p[t]*t) = sum((length-t)*p[t]*t) = sum(length*p[t]*t - p[t]*t^2) = length*sum(p*t) - sum(p*t^2)
// spt2 = length*sma_spt2 - sma_spt3
// spt3 = length*sma_spt3 - sma_spt4 // wsum(p[t]*t^3) = sum((length-t)*p[t]*t^3) = length*sum(p*t^3) - sum(p*t^4)
mpt = spt / s1 // wmean(p*t)
mpt2 = spt2 / s1 // wmean(p*t^2)
mpt3 = spt3 / s1 // wmean(p*t^3)
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
det = switch n
0 => 1
1 => mt2*m1 - mt*mt
2 => m1*(mt2*mt4 - mt3*mt3) - mt*(mt*mt4 - mt2*mt3) + mt2*(mt*mt3 - mt2*mt2)
3 => m1*mt2*mt4*mt6 - m1*mt2*mt5*mt5 - m1*mt3*mt3*mt6 + 2*m1*mt3*mt4*mt5 - m1*mt4*mt4*mt4 - mt*mt*mt4*mt6 + mt*mt*mt5*mt5 + 2*mt*mt2*mt3*mt6 - 2*mt*mt2*mt4*mt5 - 2*mt*mt3*mt3*mt5 + 2*mt*mt3*mt4*mt4 - mt2*mt2*mt2*mt6 + 2*mt2*mt2*mt3*mt5 + mt2*mt2*mt4*mt4 - 3*mt2*mt3*mt3*mt4 + mt3*mt3*mt3*mt3
[a, b, c, d] = switch n
0 => [ mp / det, 0, 0, 0 ]
1 => [ (mp*mt2 - mt*mpt) / det, (mpt*m1 - mp*mt) / det, 0, 0]
2 => [ (mp*(mt2*mt4 - mt3*mt3) - mt*(mpt*mt4 - mpt2*mt3) + mt2*(mpt*mt3 - mpt2*mt2)) / det,
(m1*(mpt*mt4 - mpt2*mt3) - mp*(mt*mt4 - mt2*mt3) + mt2*(mt*mpt2 - mt2*mpt)) / det,
(m1*(mt2*mpt2 - mt3*mpt) - mt*(mt*mpt2 - mt2*mpt) + mp*(mt*mt3 - mt2*mt2)) / det, 0 ]
3 => [ ( -mp*(-mt2*mt4*mt6 + mt2*mt5*mt5 + mt3*mt3*mt6 - 2*mt3*mt4*mt5 + mt4*mt4*mt4) - mpt*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) + mpt2*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) + mpt3*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) ) / det,
( -mp*(mt*mt4*mt6 - mt*mt5*mt5 - mt2*mt3*mt6 + mt2*mt4*mt5 + mt3*mt3*mt5 - mt3*mt4*mt4) - mpt*(-m1*mt4*mt6 + m1*mt5*mt5 + mt2*mt2*mt6 - 2*mt2*mt3*mt5 + mt3*mt3*mt4) - mpt2*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) + mpt3*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) ) / det,
( mp*(mt*mt3*mt6 - mt*mt4*mt5 - mt2*mt2*mt6 + mt2*mt3*mt5 + mt2*mt4*mt4 - mt3*mt3*mt4) - mpt*(m1*mt3*mt6 - m1*mt4*mt5 - mt*mt2*mt6 + mt*mt3*mt5 + mt2*mt3*mt4 - mt3*mt3*mt3) - mpt2*(-m1*mt2*mt6 + m1*mt4*mt4 + mt*mt*mt6 - 2*mt*mt3*mt4 + mt2*mt3*mt3) - mpt3*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) ) / det,
( mp*(-mt*mt3*mt5 + mt*mt4*mt4 + mt2*mt2*mt5 - 2*mt2*mt3*mt4 + mt3*mt3*mt3) + mpt*(m1*mt3*mt5 - m1*mt4*mt4 - mt*mt2*mt5 + mt*mt3*mt4 + mt2*mt2*mt4 - mt2*mt3*mt3) - mpt2*(m1*mt2*mt5 - m1*mt3*mt4 - mt*mt*mt5 + mt*mt2*mt4 + mt*mt3*mt3 - mt2*mt2*mt3) - mpt3*(-m1*mt2*mt4 + m1*mt3*mt3 + mt*mt*mt4 - 2*mt*mt2*mt3 + mt2*mt2*mt2) ) / det ]
var df_factor = get_df_factor(n, s1, sw, use_df)
[ get_xnp(a, b, c, d, offset=offset),
get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor) ]
//@function 重み付き多項式近似バンドxnbのEMA専用高速化実装
//@param n xnbのn(多項式近似の次数)
//@param p 価格
//@param g 重みの重心(単位はbar)
//@param use_df degrees of freedomを使って補正するか
//@param cutoff EMAの場合にループによる集計をg×cutoffまでで打ち切る
//@param offset xnpを計算するbarの位置(defaultの0は現在を表す)
//@returns 多項式近似値xnpと残差の標準偏差xnd
export enb(int n, float p, float g, bool use_df=true, float offset=0) =>
var ma = enum_ma.ema
var length = get_length(ma, g)
var s1 = g + 1
var sw = get_sw(ma, g, length) // wsum(weight)
var g2 = g * g
var g3 = g * g * g
var g4 = g * g * g * g
var h = g + 1
var h2 = h * h
var h3 = h * h * h
mp = ema(p, length) // wmean(p)
mmwp = ema(mp, length) // wmean(wmean(p))
mmmwp = ema(mmwp, length) // wmean(wmean(wmean(p)))
mmmmwp = ema(mmmwp, length) // wmean(wmean(wmean(wmean(p))))
mp2 = ema(p*p, length) // wmean(p^2)
mpt = h*mmwp - mp // wmean(p*t)
mpt2 = 2*h2*mmmwp - 3*h*mmwp + mp // wmean(p*t^2)
mpt3 = 6*h3*mmmmwp - 12*h2*mmmwp + 7*h*mmwp - mp // wmean(p*t^3)
// 近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
[a, b, c, d] = switch n
0 => [ mp, 0, 0, 0 ]
1 => [ 2*mp - mmwp,
(mmwp - mp)/g, 0, 0]
2 => [ (mpt2 - 3*(2*g+1)*mpt + 2*(3*g2+3*g+1)*mp) / (2*h2),
(-(4*g+1)*mpt2 + (2*g+1)*(10*g+1)*mpt - 6*g2*(2*g+1)*mp) / (4*g2*h2),
(mpt2 - (4*g+1)*mpt + 2*g2*mp) / (4*g2*h2), 0 ]
3 => [ (6*(2*g+1)*(2*g2+2*g+1)*mp - (36*g2+36*g+11)*mpt + 6*(2*g+1)*mpt2 - mpt3) / (h3*6),
(-6*(36*g2+36*g+11)*g3*mp + (504*g4+612*g3+270*g2+45*g+4)*mpt - 6*(33*g3+30*g2+9*g+1)*mpt2 + (18*g2+9*g+2)*mpt3) / (36*h3*g3),
(12*(2*g+1)*g3*mp - 2*(33*g3+30*g2+9*g+1)*mpt + 3*(2*g+1)*(5*g+1)*mpt2 - (3*g+1)*mpt3) / (12*h3*g3),
(-6*g3*mp + (18*g2+9*g+2)*mpt - 3*(3*g+1)*mpt2 + mpt3) / (36*h3*g3)]
var df_factor = get_df_factor(n, s1, sw, use_df)
[ get_xnp(a, b, c, d, offset=offset),
get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor) ]
//@function 傾きslopeを0,±1,±2の5種類のポジションに変換する
//@param slope 傾き
//@param th2 slopeの絶対値が大きければ通常のポジションを取るしきい値
//@param th1 slopeの絶対値が大きければ小さなポジションを取るしきい値
//@returns +2買い、+1弱い買い、0中立、-1弱い売り、-2売り
export slope2pos5(float slope, float th2, float th1) =>
( dd11.is_p(slope - th2) ? +2 :
dd11.is_n(slope + th2) ? -2 :
dd11.is_p(slope - th1) ? +1 :
dd11.is_n(slope + th1) ? -1 :
0 )
//@function 傾きslopeを0,±2の3種類のポジションに変換する
//@param slope 傾き
//@param entry slopeの絶対値が超えたら参入するしきい値
//@param exit slopeの絶対値が割ったら手仕舞いするしきい値
//@returns +2買い、0中立、-2売り
export slope2pos3(float slope, float entry, float exit) =>
var pos = 0
pos :=
dd11.is_p(slope - entry) ? +2 : // +entryより上なら買い
dd11.is_n(slope + entry) ? -2 : // -entryより下なら売り
pos > 0 and dd11.is_n(slope - exit) ? 0 : // +exitより下なら買い手仕舞い
pos < 0 and dd11.is_p(slope + exit) ? 0 : // -exitより上なら売り手仕舞い
pos
//@function ポジションで塗り分ける
//@param pos ポジション
//@param not_enough 経過時間が短いので色分けしない
//@param color_bull0 弱買の色
//@param color_bull1 買の色
//@param color_bear0 弱売の色
//@param color_bear1 売の色
//@returns 決定した色
export pos2col(int pos, bool not_enough=false, color color_bull0=na, color color_bull1=na, color color_bear0=na, color color_bear1=na, color color_neutral=na) =>
var col_bull0 = nz(color_bull0, dd11.red(tune_s=-75, tune_v=+90)) // 弱買
var col_bull1 = nz(color_bull1, dd11.red(tune_s=-50, tune_v=+70)) // 買
var col_bear0 = nz(color_bear0, dd11.blue(tune_s=-75, tune_v=+90)) // 弱売
var col_bear1 = nz(color_bear1, dd11.blue(tune_s=-50, tune_v=+70)) // 売
var col_neutral = nz(color_neutral, dd11.gray(85)) // 中立
not_enough ? na :
pos > +1 ? col_bull1 :
pos < -1 ? col_bear1 :
pos > 0 ? col_bull0 :
pos < 0 ? col_bear0 :
col_neutral
おわりに
xnbについての解説は、ひとまずここまでとなります。実際にTradingViewのインジケータとして動かすには、オプション設定やプロットなどの記述も必要です。そこで、今回使用したインジケータの全文を、末尾の有料パートにまとめました。興味のある方はぜひ活用してみてください。(有料パート分を含むソースコードは、動作や結果を保証するものではない点をご了承ください。)
ここから先は有料ですが、リポストするだけで無料で読めるようになります。よろしければ応援も兼ねて最後までお楽しみください。
ここから先は
この記事が気に入ったらチップで応援してみませんか?


購入者のコメント