VWMA と MFI/ADX に基づく kNN 機械学習定量取引戦略


作成日: 2023-12-22 14:13:27 最終変更日: 2023-12-22 14:13:27
コピー: 0 クリック数: 762
1
フォロー
1623
フォロワー

VWMA と MFI/ADX に基づく kNN 機械学習定量取引戦略

概要

この戦略は,移動平均指数と機械学習のkNNアルゴリズムを組み合わせて取引信号を生成する実験的な量化取引戦略である.この戦略は,2つの異なる周期のVWMA平均線の交差を用い,トレンドの方向性を判断し,MFIとADXの2つの指標を組み合わせてkNNアルゴリズムで信号をフィルターすることで,信号の信頼性を向上させる.

戦略原則

この戦略の核心指標は,2つの異なるパラメータのVWMA平均線である,すなわち,快線と慢線である.快線上を通過すると買い信号が生じ,快線下を通過すると売り信号が生じます.さらに,この戦略は,MFIとADXの2つの補助指標を導入し,kNN分類アルゴリズムによって,現在の市場状況下でこの信号の信頼性を判断します.

kNNアルゴリズムの理念は,新しいデータと歴史的データとを比較し,最も近いkの歴史的データに対応した結果を判断し,このkの歴史的結果に基づいて多数決方式で分類するものである.この戦略は,MFIとADXをkNNアルゴリズムの2つの入力パラメータとして,この2つの指標の組み合わせ時の歴史的価格動きを判断し,現在の信号をフィルターし,信号の質を向上させる.

戦略的優位性

  • VWMAのトレンドフォロー能力を利用し,均線交差で買い売りポイントを生成する
  • MFIとADXの指数を使って多次元特性を抽出し,トレンドの方向性を判断する
  • kNNの機械学習アルゴリズムによる取引信号の動的最適化とフィルタリング
  • 実験的戦略,開発の余地があり,より多くのデータで検証と最適化が待っています.

リスクと対策

  • VWMA平均線が遅滞する問題
  • MFIとADXは後退しており,市場状況を誤って判断する可能性がある
  • kNNアルゴリズムのパラメータ設定 (k値選択など) は結果に大きな影響を与える
  • 実験的な戦略で,現実の世界ではうまくいかない

対策として:

  • 平均線パラメータを調整し,遅滞を減らす
  • 改善された指標アルゴリズムにより,トレンドの判断の精度が向上
  • kNNアルゴリズムのパラメータを最適化し,適合性を向上させる
  • 戦略を裏付けるために,反測と模擬実体を使用します.

最適化の方向

この戦略は,さらに改善できる可能性が大きい.

  • より多くの均線指標を追加し,均線ポートフォリオを構築
  • MACDやKDJなどの補助指標を試す
  • kNNアルゴリズムの改善,例えば異なる距離測定方法の使用
  • 他の機械学習アルゴリズムを試す SVMやランダムな森など
  • パラメータを最適化し,最適なパラメータの組み合わせを探します.

戦略の安定性や収益性をさらに高めると期待されています.

要約する

この戦略は,VWMA平均線指数とkNN機械学習アルゴリズムに基づく実験的な量化取引戦略である.傾向を追跡する能力が強いと同時に,機械学習による信号フィルタリングの特徴がある.この戦略のスペースは広大で,より多くの特徴と最適化アルゴリズムを導入することにより,よりよい効果をもたらすことが期待されている.しかし,新型戦略として,さらに検証と改善を待つ一定のリスクもある.全体的に,この戦略には大きな革新的可能性がある.

ストラテジーソースコード
/*backtest
start: 2023-11-21 00:00:00
end: 2023-12-21 00:00:00
period: 1h
basePeriod: 15m
exchanges: [{"eid":"Futures_Binance","currency":"BTC_USDT"}]
*/

// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © lastguru

//@version=4
strategy(title="VWMA with kNN Machine Learning: MFI/ADX", shorttitle="VWMA + kNN: MFI/ADX", overlay=true, default_qty_type=strategy.percent_of_equity, default_qty_value=100)

/////////
// kNN //
/////////

// Define storage arrays for: parameter 1, parameter 2, price, result (up = 1; down = -1)
var knn1 = array.new_float(1, 0)
var knn2 = array.new_float(1, 0)
var knnp = array.new_float(1, 0)
var knnr = array.new_float(1, 0)

// Store the previous trade; buffer the current one until results are in
_knnStore (p1, p2, src) =>
    var prevp1 = 0.0
    var prevp2 = 0.0
    var prevsrc = 0.0
    
    array.push(knn1, prevp1)
    array.push(knn2, prevp2)
    array.push(knnp, prevsrc)
    array.push(knnr, src >= prevsrc ? 1 : -1)
    
    prevp1 := p1
    prevp2 := p2
    prevsrc := src

// Sort two arrays (MUST be of the same size) based on the first.
// In other words, when an element in the first is moved, the element in the second moves as well.
_knnGet(arr1, arr2, k) =>
    sarr = array.copy(arr1)
    array.sort(sarr)
    ss = array.slice(sarr, 0, min(k, array.size(sarr)))
    m = array.max(ss)
    out = array.new_float(0)
    for i = 0 to array.size(arr1) - 1
        if (array.get(arr1, i) <= m)
            array.push(out, array.get(arr2, i))
    out

// Create a distance array from the two given parameters
_knnDistance(p1, p2) =>
    dist = array.new_float(0)
    n = array.size(knn1) - 1
    for i = 0 to n
        d = sqrt( pow(p1 - array.get(knn1, i), 2) + pow(p2 - array.get(knn2, i), 2) )
        array.push(dist, d)
    dist

// Make a prediction, finding k nearest neighbours
_knn(p1, p2, k) =>
    slice = _knnGet(_knnDistance(p1, p2), array.copy(knnr), k)
    knn = array.sum(slice)

////////////
// Inputs //
////////////

SRC = input(title="Source", type=input.source, defval=open)
FAST = input(title="Fast Length", type=input.integer, defval=13)
SLOW = input(title="Slow Length", type=input.integer, defval=19)
FILTER = input(title="Filter Length", type=input.integer, defval=13)
SMOOTH = input(title="Filter Smoothing", type=input.integer, defval=6)
KNN = input(title="kNN nearest neighbors (k)", type=input.integer, defval=23)
BACKGROUND = input(false,title = "Draw background")

////////
// MA //
////////
fastMA = vwma(SRC, FAST)
slowMA = vwma(SRC, SLOW)

/////////
// DMI //
/////////

// Wilder's Smoothing (Running Moving Average)
_rma(src, length) =>
    out = 0.0
    out := ((length - 1) * nz(out[1]) + src) / length

// DMI (Directional Movement Index)
_dmi (len, smooth) =>
    up = change(high)
    down = -change(low)
    plusDM = na(up) ? na : (up > down and up > 0 ? up : 0)
    minusDM = na(down) ? na : (down > up and down > 0 ? down : 0)
    trur = _rma(tr, len)
    plus = fixnan(100 * _rma(plusDM, len) / trur)
    minus = fixnan(100 * _rma(minusDM, len) / trur)
    sum = plus + minus
    adx = 100 * _rma(abs(plus - minus) / (sum == 0 ? 1 : sum), smooth)
    [plus, minus, adx]

[diplus, diminus, adx] = _dmi(FILTER, SMOOTH)

/////////
// MFI //
/////////

// common RSI function
_rsi(upper, lower) =>
    if lower == 0
        100
    if upper == 0
        0
	100.0 - (100.0 / (1.0 + upper / lower))

mfiUp = sum(volume * (change(ohlc4) <= 0 ? 0 : ohlc4), FILTER)
mfiDown = sum(volume * (change(ohlc4) >= 0 ? 0 : ohlc4), FILTER)
mfi = _rsi(mfiUp, mfiDown)

////////////
// Filter //
////////////

longCondition = crossover(fastMA, slowMA)
shortCondition = crossunder(fastMA, slowMA)

if (longCondition or shortCondition)
    _knnStore(adx, mfi, SRC)
filter = _knn(adx, mfi, KNN)

/////////////
// Actions //
/////////////

bgcolor(BACKGROUND ? filter >= 0 ? color.green : color.red : na)
plot(fastMA, color=color.red)
plot(slowMA, color=color.green)

if (longCondition and filter >= 0)
    strategy.entry("Long", strategy.long)
if (shortCondition and filter < 0)
    strategy.entry("Short", strategy.short)