VWMA와 MFI/ADX를 기반으로 한 kNN 머신러닝 양적 거래 전략


생성 날짜: 2023-12-22 14:13:27 마지막으로 수정됨: 2023-12-22 14:13:27
복사: 0 클릭수: 762
avatar of ChaoZhang ChaoZhang
1
집중하다
1623
수행원

VWMA와 MFI/ADX를 기반으로 한 kNN 머신러닝 양적 거래 전략

개요

이 전략은 실험적인 양적 거래 전략으로, 이동 평균 지표와 기계 학습의 kNN 알고리즘을 결합하여 거래 신호를 생성한다. 이 전략은 트렌드 방향을 판단하기 위해 두 개의 다른 주기의 VWMA 평균선의 교차를 사용하며, MFI와 ADX의 두 지표가 kNN 알고리즘을 통해 신호를 필터링하여 신호의 신뢰성을 높인다.

전략 원칙

이 전략의 핵심 지표는 두 가지 다른 파라미터의 VWMA 평균선, 즉 빠른 선과 느린 선이다. 빠른 선에서 느린 선을 통과하면 구매 신호가 발생하고 빠른 선 아래의 느린 선을 통과하면 판매 신호가 발생한다. 또한, 이 전략은 MFI와 ADX 두 가지 보조 지표를 도입하여 현재 시장 상황에서 kNN 분류 알고리즘을 통해 신호의 신뢰성을 판단한다.

kNN 알고리즘의 아이디어는 새로운 데이터를 역사 데이터와 비교하고, 가장 가까운 k개의 역사 데이터에 대응하는 결과를 판단하고, 이 k개의 역사 결과에 따라 다수 투표 방식으로 분류하는 것이다. 이 전략은 MFI와 ADX를 kNN 알고리즘의 두 가지 입력 파라미터로 삼아, 이 두 지표가 결합될 때 역사적 가격 움직임을 판단하고, 이를 통해 현재 신호를 필터링하여 신호 품질을 향상시킨다.

전략적 이점

  • 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)