見出し画像

重み付き多項式近似バンドxnbをhighとlowを使って補正する

はじめに

ボリンジャーバンドなどの拡張である重み付き多項式近似バンドについて前回紹介しました。そのインジケータに入力する価格は1つで、closeをデフォルトで使っていました。1つの価格でbarを代表させるときにcloseを使うことは最も一般的で、barの中で最新、株取引などで出来高が多いなどの利点があります。それでも、分散や標準偏差を計算するときにはhighやlowに一時はなっていたことを反映したい気もします。極端な話としては、barの中のすべてのtickを考慮して計算することも考えられます。ただ、データの問題もありますので今回はhighとlowだけで進めます。

hl補正とdf補正のチャートでの見え方

highとlowを使うと、closeのみのときと比べて値の広がりがあります。そのためバンド幅が広くなることが期待されます。一方、前回から実装されていた、自由度(degrees of freedom)による補正をするuse_dfにもバンド幅が広くなる効果があります。そこで、この2つの補正(hl補正とdf補正と呼ぶことにします)の組み合わせで見てみます。次のように4つのチャートを並べ、左上が補正なし、下側ではhl補正を追加、右側ではdf補正を追加しました。

上下のチャートの差は分かりやすく、hl補正をした下側のチャートのバンドが上側に比べて明らかに広いです。左右のチャートの差については、df補正をした右側のチャートのバンドが心持ち広いです。

画像

参考までに、それぞれのチャートでどの補正が行われているかはインジケータのパラメータ表示の部分でも確認できます。次の例では「num src」以降のパラメータが2 high low falseとなっているので、highとlowの2つを入力とし(hl補正あり)、df補正を使わないということを示します。

画像

highとlowを使って補正したxnbの実装

ここでは今回どのような変更を行ったかをまとめます。

共通の方針

従来どおりcloseのみを使う形でも使えるように、引数として src_per_bar、src0、src1 を持ち、src_per_bar=1のときはsrc0のみを使い、src_per_bar=2のときはsrc0とsrc1を使う形に実装しました。

原理的な実装のxnb_basic()の場合

この関数は原理的な実装なので、highとlowの2つを愚直に処理する方針で作りました。

各barの時間にcloseを1つ処理していたところを、highとlowの2つを続けて処理するように変えました。次のようにsrc0について処理しているところが従来と同等で、src_per_bar=2ならsrc1についての処理も行われるようにしてあります。

        sp   += weight * src0[t]
        spt  += weight * src0[t] * t
        spt2 += weight * src0[t] * t * t
        spt3 += weight * src0[t] * t * t * t
        if src_per_bar > 1
            sp   += weight * src1[t]
            spt  += weight * src1[t] * t
            spt2 += weight * src1[t] * t * t
            spt3 += weight * src1[t] * t * t * t

highとlowの区別がないところについては、次のようにsrc_per_bar倍することで対応しています。

            s1  += src_per_bar * weight
            st  += src_per_bar * weight * t
            st2 += src_per_bar * weight * t * t     
            st3 += src_per_bar * weight * t * t * t      
            st4 += src_per_bar * weight * t * t * t * t
            st5 += src_per_bar * weight * t * t * t * t * t
            st6 += src_per_bar * weight * t * t * t * t * t * t
            sw  += src_per_bar * weight * weight

近似曲線の係数の計算などは、出来上がった集計値を扱うだけなので変えるところがありません。

汎用高速化のxnb()の場合

前回のcloseのみ入力のときには、closeが使われるのは次の内容の処理だけでした。これ以降の処理はpとp2を使って計算していました。

p = close
p2 = close*close // p2はp^2の意味

highとlowが入力のときには次の内容の処理に変えました。

p = (high + low) / 2
p2 = (high*high + low*low) / 2 // p2はp^2の意味

pとp2を使った処理は前回から変える必要はありません。関数f()が線形演算の場合には、
(f(a) + f(b)) / 2 = f( (a + b) / 2 )
などが成り立つからです。
aにa*aを、bにb*bを入れた次の式も成り立ちます。
(f(a*a) + f(b*b)) / 2 = f( (a*a + b*b) / 2 )

実際のソースでは次の形で処理しています。

export get_price_list(int src_per_bar, float src0, float src1) =>
    switch src_per_bar
        1 => [ src0,
               src0*src0 ]
        2 => [ (src0 + src1) / 2,
               (src0*src0 + src1*src1) / 2 ] 

// xnb()の中での使い方
    [p, p2] = get_price_list(src_per_bar, src0, src1)

df_factorを計算するとき、有効サンプル数をsrc_per_bar倍する調整も行っています。

    var df_factor = get_df_factor(n, s1*src_per_bar, sw*src_per_bar, use_df)

専用高速化のsnb()、lnb()、enb()の場合

専用高速化のsnb()、lnb()、enb()についてもxnb()と同じで、p、p2、df_factorの計算の変更だけで済みます。

highとlowを使う補正とは別の変更

highとlowを使う補正以外で、今回加えた変更には次のものがあります。

通常は過去の値から高速に計算する一方、interval周で一度、過去の値を使わず精度を回復するために、次の実装をしました。最適なintervalの値はxnbのxやnだけでなく、入力された価格の桁数にも依存します。必要に応じて調整をお願いします。

export get_sma_sptn_list(float p, float length, int interval) =>
    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 // この関数では期間の整数部で計算した値をsp*に入れる
    var spt  = 0.0
    var spt2 = 0.0
    var spt3 = 0.0
    var spt4 = 0.0
    var loop_max = i - 1

    if bar_index % interval == 0 // interval周で一度、過去の値を使わず精度回復
        sp   := 0
        spt  := 0
        spt2 := 0
        spt3 := 0
        spt4 := 0
        for t = 0 to loop_max
            sp   += p[t]
            spt  += p[t] * t
            spt2 += p[t] * t * t
            spt3 += p[t] * t * t * t
            spt4 += p[t] * t * t * t * t
    else // 過去の値を使って高速計算
        spt4 += 4*spt3 + 6*spt2 + 4*spt + sp - i4*nz(p[i]) // 上書き管理のためspt4から更新
        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] ]

クラメルの公式を使うところを次のようにまとめました。

export solve_by_cramer(int n, float mt1, float mt2, float mt3, float mt4, float mt5, float mt6, float mpt0, float mpt1, float mpt2, float mpt3) =>
    var mt0 = 1 // wmean(t^0) = wmean(1) = 1
    switch n
        0 =>
            det   = mt0
            det_a = mpt0
            [ det_a/det, 0, 0, 0 ]
        1 =>
            det   = mt0*mt2 - mt1*mt1
            det_a = mpt0*mt2 - mt1*mpt1
            det_b = mt0*mpt1 - mpt0*mt1
            [ det_a/det, det_b/det, 0, 0 ]
        2 =>
            det   = mt0*(mt2*mt4 - mt3*mt3) - mt1*(mt1*mt4 - mt3*mt2) + mt2*(mt1*mt3 - mt2*mt2)
            det_a = mpt0*(mt2*mt4 - mt3*mt3) - mt1*(mpt1*mt4 - mt3*mpt2) + mt2*(mpt1*mt3 - mt2*mpt2)
            det_b = mt0*(mpt1*mt4 - mt3*mpt2) - mpt0*(mt1*mt4 - mt3*mt2) + mt2*(mt1*mpt2 - mpt1*mt2)
            det_c = mt0*(mt2*mpt2 - mpt1*mt3) - mt1*(mt1*mpt2 - mpt1*mt2) + mpt0*(mt1*mt3 - mt2*mt2)
            [ det_a/det, det_b/det, det_c/det, 0 ]
        3 =>
            det   = mt0*(mt2*(mt4*mt6 - mt5*mt5) - mt3*(mt3*mt6 - mt5*mt4) + mt4*(mt3*mt5 - mt4*mt4)) - mt1*(mt1*(mt4*mt6 - mt5*mt5) - mt3*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mt5 - mt4*mt3)) + mt2*(mt1*(mt3*mt6 - mt5*mt4) - mt2*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mt4 - mt3*mt3)) - mt3*(mt1*(mt3*mt5 - mt4*mt4) - mt2*(mt2*mt5 - mt4*mt3) + mt3*(mt2*mt4 - mt3*mt3))
            det_a = mpt0*(mt2*(mt4*mt6 - mt5*mt5) - mt3*(mt3*mt6 - mt5*mt4) + mt4*(mt3*mt5 - mt4*mt4)) - mt1*(mpt1*(mt4*mt6 - mt5*mt5) - mt3*(mpt2*mt6 - mt5*mpt3) + mt4*(mpt2*mt5 - mt4*mpt3)) + mt2*(mpt1*(mt3*mt6 - mt5*mt4) - mt2*(mpt2*mt6 - mt5*mpt3) + mt4*(mpt2*mt4 - mt3*mpt3)) - mt3*(mpt1*(mt3*mt5 - mt4*mt4) - mt2*(mpt2*mt5 - mt4*mpt3) + mt3*(mpt2*mt4 - mt3*mpt3))
            det_b = mt0*(mpt1*(mt4*mt6 - mt5*mt5) - mt3*(mpt2*mt6 - mt5*mpt3) + mt4*(mpt2*mt5 - mt4*mpt3)) - mpt0*(mt1*(mt4*mt6 - mt5*mt5) - mt3*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mt5 - mt4*mt3)) + mt2*(mt1*(mpt2*mt6 - mt5*mpt3) - mpt1*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mpt3 - mpt2*mt3)) - mt3*(mt1*(mpt2*mt5 - mt4*mpt3) - mpt1*(mt2*mt5 - mt4*mt3) + mt3*(mt2*mpt3 - mpt2*mt3))
            det_c = mt0*(mt2*(mpt2*mt6 - mt5*mpt3) - mpt1*(mt3*mt6 - mt5*mt4) + mt4*(mt3*mpt3 - mpt2*mt4)) - mt1*(mt1*(mpt2*mt6 - mt5*mpt3) - mpt1*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mpt3 - mpt2*mt3)) + mpt0*(mt1*(mt3*mt6 - mt5*mt4) - mt2*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mt4 - mt3*mt3)) - mt3*(mt1*(mt3*mpt3 - mpt2*mt4) - mt2*(mt2*mpt3 - mpt2*mt3) + mpt1*(mt2*mt4 - mt3*mt3))
            det_d = mt0*(mt2*(mt4*mpt3 - mpt2*mt5) - mt3*(mt3*mpt3 - mpt2*mt4) + mpt1*(mt3*mt5 - mt4*mt4)) - mt1*(mt1*(mt4*mpt3 - mpt2*mt5) - mt3*(mt2*mpt3 - mpt2*mt3) + mpt1*(mt2*mt5 - mt4*mt3)) + mt2*(mt1*(mt3*mpt3 - mpt2*mt4) - mt2*(mt2*mpt3 - mpt2*mt3) + mpt1*(mt2*mt4 - mt3*mt3)) - mpt0*(mt1*(mt3*mt5 - mt4*mt4) - mt2*(mt2*mt5 - mt4*mt3) + mt3*(mt2*mt4 - mt3*mt3))
            [ det_a/det, det_b/det, det_c/det, det_d/det ]

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

// コメント中のwsum(x)はxの重み付き総和、wmean(x)はxの重み付き平均

var pine_normalized_positive_min = math.pow(bar_index+2, -1022) // floatやintの正の正規化された最小値
stop_if_not_zero(val) =>
    if val != 0
        runtime.error(str.format("{0} is not zero", val))
    true // 値を返すことだけに意味がある
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"    

//@function 浮動小数点期間のSMA
export sma(float src, float length) =>
    var i = int(length) // lengthの整数部分(integer part)
    var f = length - i // lengthの小数部分(fractional part)
    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

//@function 浮動小数点期間のSMA(整数期間ライブラリを利用)
export 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()から計算

//@function 浮動小数点期間のLWMA
export 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

//@function 浮動小数点期間のLWMA(整数期間ライブラリを利用)
export 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)

//@function 浮動小数点期間のEMA
export 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 // この書き方でも結果は同じ

//@function 種類を切り替えられる浮動小数点期間の移動平均
export 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)

//@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

//@function ループの最大indexを求める(ループ先頭indexは0)
export get_loop_max(simple enum_ma ma, float g, float length, int cutoff) =>
    ma == enum_ma.ema ?
      int(g * cutoff) - 1 :
      math.ceil(length) - 1

//@function tにおける重みを求める
export 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)

//@function t^n の重み付き総和を求める
export 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 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
        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
        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)

//@function t^n の重み付き平均を求める
export get_mtn(simple enum_ma ma, int n, float g, float length) =>
    get_stn(ma, n, g, length) / get_stn(ma, 0, g, length)

export get_price_list(int src_per_bar, float src0, float src1) =>
    switch src_per_bar
        1 => [ src0,
               src0*src0 ]
        2 => [ (src0 + src1) / 2,
               (src0*src0 + src1*src1) / 2 ] 

//@function n=1〜4について、sma用の p*t^n の重み付き総和を求める
export get_sma_sptn_list(float p, float length, int interval) =>
    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 // この関数では期間の整数部で計算した値をsp*に入れる
    var spt  = 0.0
    var spt2 = 0.0
    var spt3 = 0.0
    var spt4 = 0.0
    var loop_max = i - 1

    if bar_index % interval == 0 // interval周で一度、過去の値を使わず精度回復
        sp   := 0
        spt  := 0
        spt2 := 0
        spt3 := 0
        spt4 := 0
        for t = 0 to loop_max
            sp   += p[t]
            spt  += p[t] * t
            spt2 += p[t] * t * t
            spt3 += p[t] * t * t * t
            spt4 += p[t] * t * t * t * t
    else // 過去の値を使って高速計算
        spt4 += 4*spt3 + 6*spt2 + 4*spt + sp - i4*nz(p[i]) // 上書き管理のためspt4から更新
        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] ]

//@function 重みの重み付き総和を求める
export 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

//@function クラメルの公式で近似曲線 a + b*t + c*t^2 + d*t^3 の係数を求める
//@param n xnbのn(多項式近似の次数)
//@param mt1 wmean(t^1) = wmean(t) = mt // wmean(x)はxの重み付き平均
//@param mt2 wmean(t^2)
//@param mt3 wmean(t^3)
//@param mt4 wmean(t^4)
//@param mt5 wmean(t^5)
//@param mt6 wmean(t^6)
//@param mpt0 wmean(p*t^0) = wmean(p) = mp
//@param mpt1 wmean(p*t^1) = wmean(p*t) = mpt
//@param mpt2 wmean(p*t^2)
//@param mpt3 wmean(p*t^3)
//@returns 近似式の係数 [a, b, c, d]
export solve_by_cramer(int n, float mt1, float mt2, float mt3, float mt4, float mt5, float mt6, float mpt0, float mpt1, float mpt2, float mpt3) =>
    var mt0 = 1 // wmean(t^0) = wmean(1) = 1
    switch n
        0 =>
            det   = mt0
            det_a = mpt0
            [ det_a/det, 0, 0, 0 ]
        1 =>
            det   = mt0*mt2 - mt1*mt1
            det_a = mpt0*mt2 - mt1*mpt1
            det_b = mt0*mpt1 - mpt0*mt1
            [ det_a/det, det_b/det, 0, 0 ]
        2 =>
            det   = mt0*(mt2*mt4 - mt3*mt3) - mt1*(mt1*mt4 - mt3*mt2) + mt2*(mt1*mt3 - mt2*mt2)
            det_a = mpt0*(mt2*mt4 - mt3*mt3) - mt1*(mpt1*mt4 - mt3*mpt2) + mt2*(mpt1*mt3 - mt2*mpt2)
            det_b = mt0*(mpt1*mt4 - mt3*mpt2) - mpt0*(mt1*mt4 - mt3*mt2) + mt2*(mt1*mpt2 - mpt1*mt2)
            det_c = mt0*(mt2*mpt2 - mpt1*mt3) - mt1*(mt1*mpt2 - mpt1*mt2) + mpt0*(mt1*mt3 - mt2*mt2)
            [ det_a/det, det_b/det, det_c/det, 0 ]
        3 =>
            det   = mt0*(mt2*(mt4*mt6 - mt5*mt5) - mt3*(mt3*mt6 - mt5*mt4) + mt4*(mt3*mt5 - mt4*mt4)) - mt1*(mt1*(mt4*mt6 - mt5*mt5) - mt3*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mt5 - mt4*mt3)) + mt2*(mt1*(mt3*mt6 - mt5*mt4) - mt2*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mt4 - mt3*mt3)) - mt3*(mt1*(mt3*mt5 - mt4*mt4) - mt2*(mt2*mt5 - mt4*mt3) + mt3*(mt2*mt4 - mt3*mt3))
            det_a = mpt0*(mt2*(mt4*mt6 - mt5*mt5) - mt3*(mt3*mt6 - mt5*mt4) + mt4*(mt3*mt5 - mt4*mt4)) - mt1*(mpt1*(mt4*mt6 - mt5*mt5) - mt3*(mpt2*mt6 - mt5*mpt3) + mt4*(mpt2*mt5 - mt4*mpt3)) + mt2*(mpt1*(mt3*mt6 - mt5*mt4) - mt2*(mpt2*mt6 - mt5*mpt3) + mt4*(mpt2*mt4 - mt3*mpt3)) - mt3*(mpt1*(mt3*mt5 - mt4*mt4) - mt2*(mpt2*mt5 - mt4*mpt3) + mt3*(mpt2*mt4 - mt3*mpt3))
            det_b = mt0*(mpt1*(mt4*mt6 - mt5*mt5) - mt3*(mpt2*mt6 - mt5*mpt3) + mt4*(mpt2*mt5 - mt4*mpt3)) - mpt0*(mt1*(mt4*mt6 - mt5*mt5) - mt3*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mt5 - mt4*mt3)) + mt2*(mt1*(mpt2*mt6 - mt5*mpt3) - mpt1*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mpt3 - mpt2*mt3)) - mt3*(mt1*(mpt2*mt5 - mt4*mpt3) - mpt1*(mt2*mt5 - mt4*mt3) + mt3*(mt2*mpt3 - mpt2*mt3))
            det_c = mt0*(mt2*(mpt2*mt6 - mt5*mpt3) - mpt1*(mt3*mt6 - mt5*mt4) + mt4*(mt3*mpt3 - mpt2*mt4)) - mt1*(mt1*(mpt2*mt6 - mt5*mpt3) - mpt1*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mpt3 - mpt2*mt3)) + mpt0*(mt1*(mt3*mt6 - mt5*mt4) - mt2*(mt2*mt6 - mt5*mt3) + mt4*(mt2*mt4 - mt3*mt3)) - mt3*(mt1*(mt3*mpt3 - mpt2*mt4) - mt2*(mt2*mpt3 - mpt2*mt3) + mpt1*(mt2*mt4 - mt3*mt3))
            det_d = mt0*(mt2*(mt4*mpt3 - mpt2*mt5) - mt3*(mt3*mpt3 - mpt2*mt4) + mpt1*(mt3*mt5 - mt4*mt4)) - mt1*(mt1*(mt4*mpt3 - mpt2*mt5) - mt3*(mt2*mpt3 - mpt2*mt3) + mpt1*(mt2*mt5 - mt4*mt3)) + mt2*(mt1*(mt3*mpt3 - mpt2*mt4) - mt2*(mt2*mpt3 - mpt2*mt3) + mpt1*(mt2*mt4 - mt3*mt3)) - mpt0*(mt1*(mt3*mt5 - mt4*mt4) - mt2*(mt2*mt5 - mt4*mt3) + mt3*(mt2*mt4 - mt3*mt3))
            [ det_a/det, det_b/det, det_c/det, det_d/det ]

//@function xnpを求める
export get_xnp(float a, float b, float c, float d, float offset=0) =>
	t = -offset
	a + b*t + c*t*t + d*t*t*t

//@function xnpを求める(offset=0専用)
export get_xnp_offset0(float a) =>
	a

//@function xndを求める
export get_xnd(float a, float b, float c, float d, float mpt0, float mpt1, float mpt2, float mpt3, float mp2, float df_factor) =>
    raw_variance = mp2 - (a*mpt0 + b*mpt1 + c*mpt2 + d*mpt3)
    math.sqrt(math.max(pine_normalized_positive_min, raw_variance) * df_factor)

//@function degrees of freedomを調整する係数を求める
export 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 src_per_bar 価格をbarあたりいくつ使うか
//@param src0 最初に使う価格
//@param src1 次に使う価格
//@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, int src_per_bar, float src0, float src1, float g, bool use_df=true, int cutoff=20, 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  += src_per_bar * weight
            st  += src_per_bar * weight * t
            st2 += src_per_bar * weight * t * t     
            st3 += src_per_bar * weight * t * t * t      
            st4 += src_per_bar * weight * t * t * t * t
            st5 += src_per_bar * weight * t * t * t * t * t
            st6 += src_per_bar * weight * t * t * t * t * t * t
            sw  += src_per_bar * weight * weight
        sp   += weight * src0[t]
        spt  += weight * src0[t] * t
        spt2 += weight * src0[t] * t * t
        spt3 += weight * src0[t] * t * t * t
        if src_per_bar > 1
            sp   += weight * src1[t]
            spt  += weight * src1[t] * t
            spt2 += weight * src1[t] * t * t
            spt3 += weight * src1[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)
    
    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 * (src0[t]-est) * (src0[t]-est)
        if src_per_bar > 1
            sum_sqd += weight * (src1[t]-est) * (src1[t]-est)
    raw_variance = sum_sqd / s1
    var df_factor = get_df_factor(n, s1, sw, use_df)
    xnd = math.sqrt(math.max(pine_normalized_positive_min, raw_variance) * df_factor)

    [xnp, xnd]

//@function 重み付き多項式近似バンドxnbの汎用高速化実装
//@param ma xnbのx(使用する移動平均)
//@param n xnbのn(多項式近似の次数)
//@param src_per_bar 価格をbarあたりいくつ使うか
//@param src0 最初に使う価格
//@param src1 次に使う価格
//@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, int src_per_bar, float src0, float src1, float g, bool use_df=true, int cutoff=20, float offset=0) =>
    [p, p2] = get_price_list(src_per_bar, src0, src1)
    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 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, p2, 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 の係数を求める
    [a, b, c, d] = solve_by_cramer(n, mt, mt2, mt3, mt4, mt5, mt6, mp, mpt, mpt2, mpt3)

    xnp = get_xnp(a, b, c, d, offset=offset)

    var df_factor = get_df_factor(n, s1*src_per_bar, sw*src_per_bar, use_df)
    xnd = get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor)

    [xnp, xnd]

//@function 重み付き多項式近似バンドxnbのSMA専用高速化実装
//@param n xnbのn(多項式近似の次数)
//@param src_per_bar 価格をbarあたりいくつ使うか
//@param src0 最初に使う価格
//@param src1 次に使う価格
//@param g 重みの重心(単位はbar)
//@param use_df degrees of freedomを使って補正するか
//@param interval sma_sptnの精度回復を行う間隔
//@param offset xnpを計算するbarの位置(defaultの0なら最新足)
//@returns 多項式近似値xnpと残差の標準偏差xnd
export snb(int n, int src_per_bar, float src0, float src1, float g, bool use_df=true, int interval=100, float offset=0) =>
    [p, p2] = get_price_list(src_per_bar, src0, src1)
    var ma = enum_ma.sma
    var length = get_length(ma, g)
    var loop_max = get_loop_max(ma, g, length, 0)
    var s1  = length // wsum(1)
    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, p2, length) // wmean(p^2)
    var ceil_length = math.ceil(length)
    [spt, spt2, spt3, unused] = get_sma_sptn_list(p, ceil_length, interval)
    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 の係数を求める
    [a, b, c, d] = solve_by_cramer(n, mt, mt2, mt3, mt4, mt5, mt6, mp, mpt, mpt2, mpt3)

    xnp = get_xnp(a, b, c, d, offset=offset)

    var df_factor = get_df_factor(n, s1*src_per_bar, sw*src_per_bar, use_df)
    xnd = get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor)

    [xnp, xnd]

//@function 重み付き多項式近似バンドxnbのLWMA専用高速化実装
//@param n xnbのn(多項式近似の次数)
//@param src_per_bar 価格をbarあたりいくつ使うか
//@param src0 最初に使う価格
//@param src1 次に使う価格
//@param g 重みの重心(単位はbar)
//@param use_df degrees of freedomを使って補正するか
//@param interval sma_sptnの精度回復を行う間隔
//@param offset xnpを計算するbarの位置(defaultの0なら最新足)
//@returns 多項式近似値xnpと残差の標準偏差xnd
export lnb(int n, int src_per_bar, float src0, float src1, float g, bool use_df=true, int interval=100, float offset=0) =>
    [p, p2] = get_price_list(src_per_bar, src0, src1)
    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 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, p2, length) // wmean(p^2)
    var ceil_length = math.ceil(length)
    [sma_spt, sma_spt2, sma_spt3, sma_spt4] = get_sma_sptn_list(p, ceil_length, interval)
    spt  = length*sma_spt  - sma_spt2 // wsum(p*t) = wsum((length-t)*p*t) = wsum(length*p*t - p*t^2) = length*wsum(p*t) - wsum(p*t^2)
    spt2 = length*sma_spt2 - sma_spt3 // wsum(p*t^2) = wsum((length-t)*p*t^2) = wsum(length*p*t^2 - p*t^3) = length*wsum(p*t^2) - wsum(p*t^3)
    spt3 = length*sma_spt3 - sma_spt4 // wsum(p*t^3) = wsum((length-t)*p*t^3) = wsum(length*p*t^3 - p*t^4) = length*wsum(p*t^3) - wsum(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 の係数を求める
    [a, b, c, d] = solve_by_cramer(n, mt, mt2, mt3, mt4, mt5, mt6, mp, mpt, mpt2, mpt3)

    xnp = get_xnp(a, b, c, d, offset=offset)
      
    var df_factor = get_df_factor(n, s1*src_per_bar, sw*src_per_bar, use_df)
    xnd = get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor)

    [xnp, xnd]

//@function 重み付き多項式近似バンドxnbのEMA専用高速化実装
//@param n xnbのn(多項式近似の次数)
//@param src_per_bar 価格をbarあたりいくつ使うか
//@param src0 最初に使う価格
//@param src1 次に使う価格
//@param g 重みの重心(単位はbar)
//@param use_df degrees of freedomを使って補正するか
//@param offset xnpを計算するbarの位置(defaultの0なら最新足)
//@returns 多項式近似値xnpと残差の標準偏差xnd
export enb(int n, int src_per_bar, float src0, float src1, float g, bool use_df=true, float offset=0) =>
    [p, p2] = get_price_list(src_per_bar, src0, src1)
    var ma = enum_ma.ema
    var length = get_length(ma, g)
    var s1 = g + 1 // wsum(1)
    var sw = get_sw(ma, g, length) // wsum(weight)
    var g2 = g * g
    var g3 = g * g * g
    var k = g + 1
    var k2 = k * k
    var k3 = k * k * k

    mp = ema(p, length) // wmean(p)
    mmp = ema(mp, length) // wmean(wmean(p))
    mmmp = ema(mmp, length) // wmean(wmean(wmean(p)))
    mmmmp = ema(mmmp, length) // wmean(wmean(wmean(wmean(p))))
    mp2 = ema(p2, length) // wmean(p^2)
    mpt  = k*mmp - mp // wmean(p*t)
    mpt2 = 2*k2*mmmp - 3*k*mmp + mp // wmean(p*t^2)
    mpt3 = 6*k3*mmmmp - 12*k2*mmmp + 7*k*mmp - 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 => [ -mmp + 2*mp, 
               (mmp - mp) / g, 0, 0 ]
        2 => [ mmmp - 3*mmp + 3*mp,
               (-(4*g+1)*mmmp + (10*g+2)*mmp - (6*g+1)*mp) / (2*g2),
               (mmmp - 2*mmp + mp) / (2*g2), 0 ]
        3 => [ -mmmmp + 4*mmmp - 6*mmp + 4*mp,
               ((18*g2+9*g+2)*mmmmp - 6*(11*g2+5*g+1)*mmmp + 3*(28*g2+11*g+2)*mmp - 2*(18*g2+6*g+1)*mp) / (6*g3),
               (-(3*g+1)*mmmmp + (10*g+3)*mmmp - (11*g+3)*mmp + (4*g+1)*mp) / (2*g3),
               (mmmmp - 3*mmmp + 3*mmp - mp) / (6*g3) ]

    xnp = get_xnp(a, b, c, d, offset=offset)

    var df_factor = get_df_factor(n, s1*src_per_bar, sw*src_per_bar, use_df)
    xnd = get_xnd(a, b, c, d, mp, mpt, mpt2, mpt3, mp2, df_factor)

    [xnp, xnd]

//@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

考察

xndの値の確認

次のコードは特定の日時に赤線を描き、xndの値を左下のテーブルに表示するようにするものです。

target_time = input.time(timestamp("15 Sep 2025 09:00 +0900"), "Date")
is_target_bar = (time <= target_time) and (target_time < time_close)
if is_target_bar
    tbl.cell_set_text(3, 0, text=str.tostring(xnd/syminfo.mintick, "#")) // 0はextra spaceに使われている
bgcolor(is_target_bar ? color.new(color.red, 40) : na)

これを使うと、はじめにお見せしたチャートは次のようになります。赤い縦線のところのxndの値(を1000倍したもの)は、補正無し740、df補正798、hl補正1479、両方補正1534です。hl補正で大きく増え、df補正では比べると小幅に増えていて、グラフの見た目と同じです

画像

週足から8時間足に変え、他の条件はそのまま作ったチャートは次のとおりです。8時間足では補正無し1216、df補正1220、hl補正1256、両方補正1259です。補正の効果はあるものの、グラフの見た目のとおり小さな補正でした。

画像

barの数で換算したg

それぞれのチャートにある灰色の縦線は、g(ここでは1Mなので1か月)の間隔の区切りです。区切りの間隔は、週足では5本以下、8時間足では60本以上です。今回は概算で週足でg=1M=5b、8時間足でg=1M=60bと書くことにします。

8時間足のほうがdf補正が弱い理由

週足ではg=5bなので、重心までにあるbarは5本だけです。8時間足ではg=60bなので、重心までにあるbarは60本です。df補正は有効サンプル数が少ないときに強く補正するものです。そのため重心までにbarが5本しかない週足では補正が強く、8時間足では補正が弱くなります。

8時間足のほうがhl補正が弱い理由

8時間足のすべてのhighがその週のhighと等しく、lowについても同様の場合、8時間足と週足でhl補正の強さが(ほぼ)同じになります。(8時間足のhighが週足のhighであり続けlowも同様のとき、その8時間足の並びが週足のbarに見えてきますよね。)実際には8時間足のhighはその週のhighよりも小さいことが多く、lowも同様なので、hl補正は8時間足のほうが弱くなります。

hl補正は意外と都合がよい

見方を変えると、週足でhl補正をすることは、週足のhighとlowがその週のあいだ継続するとして扱うことです。そのため、hl補正後のバンド幅の期待値は正しい値よりも大きいです。hl補正は期待値的には過剰ですが、次のように考えると都合がよいです。

8時間足(g=60b)の場合は、有効サンプル数が多いのでxndの推定誤差が小さいです。xndをそのまま信用して使えばよいため、上で見たように8時間足でのhl補正がごく小幅なことが都合がよいです。

週足(g=5b)の場合は、有効サンプル数が少ないのでxndの推定誤差が大きいです。+2σ超えなどで作った売買シグナルは、xndが大きいと出にくく、xndが小さいと出やすいです。誤差のため余計なシグナルが出るのは問題です。上で見たように週足でhl補正幅が大きいことは、シグナルが出にくくなるので都合がよいです。

もちろん、有効サンプル数が少なくて誤差が大きいのであれば、より短い時間足を使って有効サンプル数を増やすか、取引を休むのがよいです。ただ、どこまで有効サンプル数を増やせばよいことにするかという問題があるので、補正はオンのままでもサンプル数が多くなるにつれて実質オフになっていくhl補正の特徴は都合がよいです。

おわりに

xnbの関数自体は上に無料公開しましたが、オプション設定やプロットなどの記述も含めた今回のインジケータ全体は有料パートに置きました。有料といってもリポストするだけで無料で読めるようになります。よろしければ応援も兼ねて最後までお楽しみください。(有料パート分を含むソースコードは、動作や結果を保証するものではない点をご了承ください。)

インジケータの設定画面は次のようになっていて、前回からsrcの数、src0、src1が追加されています。灰色の丸に白でiと書かれたところを押すと説明が出てくるようになっています。

画像

本文中のチャートを表示することも末尾のソースで可能です。ソース中にある「本文中のチャートを表示するには、上の2行をコメントアウトし、次の5行を復活させる」という指示に従ってください。

// 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
indicator("xnb 202601", overlay=true)

import dig_dig_11/main/11 as dd11
import dig_dig_11/libxnb/2 as xnb
// dd11.profile(position=position.top_right)

enum enum_impl
    basic = "basic" // 原理実装版
    generic = "generic" // maに依存しない汎用高速化版
    specific = "specific" // ma別の専用高速化版

var ma = input.enum(xnb.enum_ma.ema, title="​moving average", tooltip="使用する移動平均(xnbのx)")
var n = input.int(1 , title="​polynomial degree", minval=0, maxval=3, tooltip="多項式近似の次数(xnbのn)")
var impl = input.enum(enum_impl.specific, title="​implementation",
  tooltip="basic: 原理実装版\n"
  + "generic: maに依存しない汎用高速化版\n"
  + "specific: ma別の専用高速化版\n")
var cutoff = 20

var g_value = input.float(0, title="​g: value", minval=0, inline="g")
var g_unit = input.string("b", title="​unit", options=["b", "s", "m", "h", "D", "W", "M", "Y"], inline="g")
var g_shift = input.int(0, title="​shift", inline="g",
  tooltip="value\n"
  + "  0: テーブルで設定\n"
  + "  他: 次のunitとともに解釈\n"
  + "unit\n"
  + "  b: bars\n  s: seconds\n  m: minutes\n  h: hours\n  D: days\n  W: weeks\n  M: months\n  Y: years\n"
  + "shift\n"
  + "  規定テーブルをずらす量")

var src_per_bar = input.int(2, title="​num src", inline="src", minval=1, maxval=2)
src0 = input.source(high, title="​src0", inline="src")
src1 = input.source(low, title="​src1", inline="src", tooltip="使う価格の数と対象")

var use_df = input.bool(true, title="​use df", display=display.all, tooltip="degrees of freedomを使って補正するか")

var show_position = input.bool(not true, title="​show position:", inline="​show_position")
var entry = input.float(1.5, title="​entry", inline="​show_position", display=display.none) // とりあえず1.5〜2
var exit = input.float(0.5, title="​exit", inline="​show_position", display=display.none, tooltip="entryを超えてexitを割るまでポジションを持つ") // とりあえず0.5〜1
var g_div = 16 // ポジション計算に使うgを1/g_divにする
var exclude = 5 // 最初のexclude*gで、塗りつぶしをしない

var info_position = input.string(position.bottom_left, title="​info table: position",
  options=[position.bottom_left, position.bottom_right, position.top_right], inline="info", display=display.none)
var extra_space = input.bool(true, title="​extra space", inline="info",
  tooltip="TradingViewのロゴが邪魔なときにチェックしてください")

var vline_width = input.int(1, title="​vline width", display=display.none,
  tooltip="g毎に描かれる縦線の太さ")

// gを計算
var g = dd11.get_g(g_value, g_unit, g_shift)

[xnp, xnd] = switch impl
    enum_impl.basic =>
        xnb.xnb_basic(ma, n, src_per_bar, src0, src1, g, use_df=use_df, cutoff=cutoff)
    enum_impl.generic =>
        xnb.xnb(ma, n, src_per_bar, src0, src1, g, use_df=use_df, cutoff=cutoff)
    enum_impl.specific =>
        switch ma
            xnb.enum_ma.sma => xnb.snb(n, src_per_bar, src0, src1, g, use_df=use_df)
            xnb.enum_ma.lma => xnb.lnb(n, src_per_bar, src0, src1, g, use_df=use_df)
            xnb.enum_ma.ema => xnb.enb(n, src_per_bar, src0, src1, g, use_df=use_df)

var color_band = color.gray
var col0 = show_position? dd11.white() : color_band
var col1 = show_position? na : color_band
var col2 = color_band
var lw = 1

// plot3p = plot(xnp + xnd*3, color=col2, linewidth=lw, title="+3σ")
plot2p = plot(xnp + xnd*2, color=col2, linewidth=lw, title="+2σ")
plot1p = plot(xnp + xnd*1, color=col1, linewidth=lw, title="+1σ")
plot0  = plot(xnp + xnd*0, color=col0, linewidth=lw, title="0σ")
plot1m = plot(xnp - xnd*1, color=col1, linewidth=lw, title="-1σ")
plot2m = plot(xnp - xnd*2, color=col2, linewidth=lw, title="-2σ")
// plot3m = plot(xnp - xnd*3, color=col2, linewidth=lw, title="-3σ")

// 塗りつぶし描画
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, src_per_bar, slope, slope, g_for_slope, use_df=use_df)
pos = xnb.slope2pos3(e0b_slope, entry, exit)
fill(plot1p, plot1m, xnb.pos2col(pos, not_enough=bar_index<g*exclude or not show_position))

// 情報テーブル出力
var a_str = array.from(str.format("{0}{1}b({2})", str.substring(str.tostring(ma), 0, 1), n, str.substring(str.tostring(impl), 0, 1)),
  dd11.second2readable(g*timeframe.in_seconds()))
var a_str_tt = array.from(str.format("g={0,number,#.#}bars length={1,number,#.#}bars", g, xnb.get_length(ma, g)))
var tbl = dd11.get_table(a_str, a_str_tt, columns=3, position=info_position, extra_space=extra_space)
if barstate.islast
    tbl.cell_set_text(3, 0, text=str.tostring(xnd/syminfo.mintick, "#")) // 0はextra spaceに使われている
// 本文中のチャートを表示するには、上の2行をコメントアウトし、次の5行を復活させる
// target_time = input.time(timestamp("15 Sep 2025 09:00 +0900"), "Date")
// is_target_bar = (time <= target_time) and (target_time < time_close)
// if is_target_bar
//     tbl.cell_set_text(3, 0, text=str.tostring(xnd/syminfo.mintick, "#")) // 0はextra spaceに使われている
// bgcolor(is_target_bar ? color.new(color.red, 40) : na)

// 背景色表示
var gray = dd11.black(95)
bgcolor(dd11.get_bg_color(g, vline_width=vline_width, color_vline=gray), title="​colored session")

// dd11.profile(position=position.middle_right)


ここから先は

0字

メンバー特典記事、メンバー限定掲示板が、他のnoteメーンバーシップと同様にあります。 このメンバ…

スタンダードプラン

¥500 / 月
初月無料

プレミアムプラン

¥1,500 / 月

この記事が気に入ったらチップで応援してみませんか?

購入者のコメント

コメントするには、 ログイン または 会員登録 をお願いします。
重み付き多項式近似バンドxnbをhighとlowを使って補正する|ここほれ!わんわん
word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word word

mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1
mmMwWLliI0fiflO&1