Tài nguyên đang được tải lên... tải...

Động lực RSI Oscillator Polynomial Fitting Indicator Xu hướng Chiến lược giao dịch định lượng

Tác giả:ChaoZhang, Ngày: 2024-12-11 15:32:23
Tags:RSIDRSIQREMARMSEMSE

img

Chiến lược này là một hệ thống giao dịch định lượng dựa trên dao động động RSI. Bằng cách thực hiện phân tích chuỗi thời gian và phù hợp đa thức trên chỉ số RSI, nó tính toán tỷ lệ thay đổi của RSI để nắm bắt đà thị trường. Chiến lược này sử dụng các phương pháp toán học tiên tiến như phân hủy QR để xử lý tín hiệu và kết hợp với hệ thống trung bình động cho các quyết định giao dịch.

Nguyên tắc chiến lược

Cốt lõi của chiến lược là bộ dao động Delta-RSI, được thực hiện thông qua các bước sau:

  1. Đầu tiên tính toán chỉ số RSI truyền thống như dữ liệu cơ bản
  2. Sử dụng phù hợp đa thức để làm mịn RSI và giảm tiếng ồn
  3. Tính toán phái sinh thời gian của RSI để có được Delta-RSI, phản ánh tỷ lệ thay đổi của RSI
  4. So sánh Delta-RSI với đường trung bình động của nó để tạo ra tín hiệu giao dịch
  5. Sử dụng sai số bình phương gốc (RMSE) để đánh giá và lọc chất lượng phù hợp

Các tín hiệu giao dịch có thể được tạo ra theo ba cách:

  • Đi ngang đường không: dài khi Delta-RSI biến từ âm thành dương, ngắn khi biến từ dương thành âm
  • Đường truyền tín hiệu: Dài / ngắn khi Delta-RSI vượt trên / dưới đường trung bình động
  • Thay đổi hướng: dài khi Delta-RSI bắt đầu tăng trong lãnh thổ âm, ngắn khi bắt đầu giảm trong lãnh thổ tích cực

Ưu điểm chiến lược

  1. Cơ sở toán học vững chắc: Sử dụng các phương pháp toán học tiên tiến như phân hủy QR để xử lý tín hiệu
  2. Đơn giản hóa tín hiệu: Phụ hợp đa thức có thể lọc hiệu quả tiếng ồn thị trường và cải thiện chất lượng tín hiệu
  3. Độ linh hoạt cao: Cung cấp nhiều phương pháp tạo tín hiệu và lựa chọn tham số để thích nghi với các điều kiện thị trường khác nhau
  4. Rủi ro có thể kiểm soát được: Bao gồm cơ chế lọc RMSE để sàng lọc các tín hiệu đáng tin cậy hơn
  5. Hiệu quả tính toán: Các hoạt động ma trận sử dụng các thuật toán tối ưu hóa cho hiệu quả hoạt động cao

Rủi ro chiến lược

  1. Độ nhạy của các tham số: Nhiều tham số chính cần điều chỉnh cẩn thận, lựa chọn tham số kém ảnh hưởng nghiêm trọng đến hiệu suất chiến lược
  2. Sự chậm trễ: Đơn giản hóa tín hiệu giới thiệu một số sự chậm trễ, có thể bỏ lỡ các chuyển động thị trường nhanh chóng
  3. Phá vỡ sai: Có thể tạo ra tín hiệu sai trong thị trường dao động, làm tăng chi phí giao dịch
  4. Sự phức tạp tính toán: Bao gồm nhiều hoạt động ma trận, có thể có nút thắt hiệu suất trong giao dịch tần số cao
  5. Overfitting: Cần tránh quá nhiều dữ liệu lịch sử khi tối ưu hóa các thông số

Hướng dẫn tối ưu hóa chiến lược

  1. Các thông số thích nghi: Điều chỉnh động thời gian RSI và thứ tự phù hợp dựa trên biến động thị trường
  2. Nhiều khung thời gian: Kết hợp các tín hiệu từ nhiều khung thời gian để xác thực chéo
  3. Bộ lọc biến động: Thêm các chỉ số biến động như ATR để lọc tín hiệu
  4. Phân loại thị trường: Sử dụng các quy tắc tạo tín hiệu khác nhau cho các trạng thái thị trường khác nhau ( xu hướng / dao động)
  5. Tối ưu hóa stop-loss: Thêm các cơ chế stop-loss thông minh hơn, như dừng động dựa trên mức hỗ trợ / kháng cự

Tóm lại

Đây là một chiến lược giao dịch định lượng hoàn chỉnh với nền tảng lý thuyết vững chắc. Thông qua phân tích các đặc điểm năng động của RSI kết hợp với các phương pháp toán học hiện đại để xử lý tín hiệu, nó có thể nắm bắt hiệu quả xu hướng thị trường. Mặc dù có một số vấn đề về độ nhạy của tham số và độ phức tạp tính toán, chiến lược có giá trị thực tế tốt thông qua việc lựa chọn tham số và cải tiến tối ưu hóa đúng cách. Khi áp dụng giao dịch trực tiếp, nên chú ý đến kiểm soát rủi ro, đặt kích thước vị trí hợp lý và liên tục theo dõi hiệu suất chiến lược.


/*backtest
start: 2024-11-10 00:00:00
end: 2024-12-09 08:00:00
period: 4h
basePeriod: 4h
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/
// © tbiktag
//
// Delta-RSI Oscillator Strategy
//
// A strategy that uses Delta-RSI Oscillator (© tbiktag) as a stand-alone indicator:
// https://www.tradingview.com/script/OXQVFTQD-Delta-RSI-Oscillator/
//
// Delta-RSI is a smoothed time derivative of the RSI, plotted as a histogram 
// and serving as a momentum indicator. 
// 
// Input parameters:
// RSI Length: The timeframe of the RSI that serves as an input to D-RSI.
// Length: The length of the lookback frame used for local regression.
// Polynomial Order: The order of the local polynomial function used to interpolate the RSI.
// Signal Length: The length of a EMA of the D-RSI series that is used as a signal line.
// Trade signals are generated based on three optional conditions:
// - Zero-crossing: bullish when D-RSI crosses zero from negative to positive values (bearish otherwise)
// - Signal Line Crossing: bullish when D-RSI crosses from below to above the signal line (bearish otherwise)
// - Direction Change: bullish when D-RSI was negative and starts ascending (bearish otherwise)
//
// Since D-RSI oscillator is based on polynomial fitting of the RSI curve, there is also an option
// to filter trade signal by means of the root mean-square error of the fit (normalized by the sample average).
// 
//@version=5
strategy(title='Delta-RSI Oscillator Strategy-QuangVersion', shorttitle='D-RSI-Q', overlay=true)

// ---Subroutines---
matrix_get(_A, _i, _j, _nrows) =>
    // Get the value of the element of an implied 2d matrix
    //input: 
    // _A :: array: pseudo 2d matrix _A = [[column_0],[column_1],...,[column_(n-1)]]
    // _i :: integer: row number
    // _j :: integer: column number
    // _nrows :: integer: number of rows in the implied 2d matrix
    array.get(_A, _i + _nrows * _j)

matrix_set(_A, _value, _i, _j, _nrows) =>
    // Set a value to the element of an implied 2d matrix
    //input: 
    // _A :: array, changed on output: pseudo 2d matrix _A = [[column_0],[column_1],...,[column_(n-1)]]
    // _value :: float: the new value to be set
    // _i :: integer: row number
    // _j :: integer: column number
    // _nrows :: integer: number of rows in the implied 2d matrix
    array.set(_A, _i + _nrows * _j, _value)

transpose(_A, _nrows, _ncolumns) =>
    // Transpose an implied 2d matrix
    // input:
    // _A :: array: pseudo 2d matrix _A = [[column_0],[column_1],...,[column_(n-1)]]
    // _nrows :: integer: number of rows in _A
    // _ncolumns :: integer: number of columns in _A
    // output:
    // _AT :: array: pseudo 2d matrix with implied dimensions: _ncolums x _nrows
    var _AT = array.new_float(_nrows * _ncolumns, 0)
    for i = 0 to _nrows - 1 by 1
        for j = 0 to _ncolumns - 1 by 1
            matrix_set(_AT, matrix_get(_A, i, j, _nrows), j, i, _ncolumns)
    _AT

multiply(_A, _B, _nrowsA, _ncolumnsA, _ncolumnsB) =>
    // Calculate scalar product of two matrices
    // input: 
    // _A :: array: pseudo 2d matrix
    // _B :: array: pseudo 2d matrix
    // _nrowsA :: integer: number of rows in _A
    // _ncolumnsA :: integer: number of columns in _A
    // _ncolumnsB :: integer: number of columns in _B
    // output:
    // _C:: array: pseudo 2d matrix with implied dimensions _nrowsA x _ncolumnsB
    var _C = array.new_float(_nrowsA * _ncolumnsB, 0)
    int _nrowsB = _ncolumnsA
    float elementC = 0.0
    for i = 0 to _nrowsA - 1 by 1
        for j = 0 to _ncolumnsB - 1 by 1
            elementC := 0
            for k = 0 to _ncolumnsA - 1 by 1
                elementC += matrix_get(_A, i, k, _nrowsA) * matrix_get(_B, k, j, _nrowsB)
                elementC
            matrix_set(_C, elementC, i, j, _nrowsA)
    _C

vnorm(_X, _n) =>
    //Square norm of vector _X with size _n
    float _norm = 0.0
    for i = 0 to _n - 1 by 1
        _norm += math.pow(array.get(_X, i), 2)
        _norm
    math.sqrt(_norm)

qr_diag(_A, _nrows, _ncolumns) =>
    //QR Decomposition with Modified Gram-Schmidt Algorithm (Column-Oriented)
    // input:
    // _A :: array: pseudo 2d matrix _A = [[column_0],[column_1],...,[column_(n-1)]]
    // _nrows :: integer: number of rows in _A
    // _ncolumns :: integer: number of columns in _A
    // output:
    // _Q: unitary matrix, implied dimenstions _nrows x _ncolumns
    // _R: upper triangular matrix, implied dimansions _ncolumns x _ncolumns
    var _Q = array.new_float(_nrows * _ncolumns, 0)
    var _R = array.new_float(_ncolumns * _ncolumns, 0)
    var _a = array.new_float(_nrows, 0)
    var _q = array.new_float(_nrows, 0)
    float _r = 0.0
    float _aux = 0.0
    //get first column of _A and its norm:
    for i = 0 to _nrows - 1 by 1
        array.set(_a, i, matrix_get(_A, i, 0, _nrows))
    _r := vnorm(_a, _nrows)
    //assign first diagonal element of R and first column of Q
    matrix_set(_R, _r, 0, 0, _ncolumns)
    for i = 0 to _nrows - 1 by 1
        matrix_set(_Q, array.get(_a, i) / _r, i, 0, _nrows)
    if _ncolumns != 1
        //repeat for the rest of the columns
        for k = 1 to _ncolumns - 1 by 1
            for i = 0 to _nrows - 1 by 1
                array.set(_a, i, matrix_get(_A, i, k, _nrows))
            for j = 0 to k - 1 by 1
                //get R_jk as scalar product of Q_j column and A_k column:
                _r := 0
                for i = 0 to _nrows - 1 by 1
                    _r += matrix_get(_Q, i, j, _nrows) * array.get(_a, i)
                    _r
                matrix_set(_R, _r, j, k, _ncolumns)
                //update vector _a
                for i = 0 to _nrows - 1 by 1
                    _aux := array.get(_a, i) - _r * matrix_get(_Q, i, j, _nrows)
                    array.set(_a, i, _aux)
            //get diagonal R_kk and Q_k column
            _r := vnorm(_a, _nrows)
            matrix_set(_R, _r, k, k, _ncolumns)
            for i = 0 to _nrows - 1 by 1
                matrix_set(_Q, array.get(_a, i) / _r, i, k, _nrows)
    [_Q, _R]

pinv(_A, _nrows, _ncolumns) =>
    //Pseudoinverse of matrix _A calculated using QR decomposition
    // Input: 
    // _A:: array: implied as a (_nrows x _ncolumns) matrix _A = [[column_0],[column_1],...,[column_(_ncolumns-1)]]
    // Output: 
    // _Ainv:: array implied as a (_ncolumns x _nrows) matrix _A = [[row_0],[row_1],...,[row_(_nrows-1)]]
    // ----
    // First find the QR factorization of A: A = QR,
    // where R is upper triangular matrix.
    // Then _Ainv = R^-1*Q^T.
    // ----
    [_Q, _R] = qr_diag(_A, _nrows, _ncolumns)
    _QT = transpose(_Q, _nrows, _ncolumns)
    // Calculate Rinv:
    var _Rinv = array.new_float(_ncolumns * _ncolumns, 0)
    float _r = 0.0
    matrix_set(_Rinv, 1 / matrix_get(_R, 0, 0, _ncolumns), 0, 0, _ncolumns)
    if _ncolumns != 1
        for j = 1 to _ncolumns - 1 by 1
            for i = 0 to j - 1 by 1
                _r := 0.0
                for k = i to j - 1 by 1
                    _r += matrix_get(_Rinv, i, k, _ncolumns) * matrix_get(_R, k, j, _ncolumns)
                    _r
                matrix_set(_Rinv, _r, i, j, _ncolumns)
            for k = 0 to j - 1 by 1
                matrix_set(_Rinv, -matrix_get(_Rinv, k, j, _ncolumns) / matrix_get(_R, j, j, _ncolumns), k, j, _ncolumns)
            matrix_set(_Rinv, 1 / matrix_get(_R, j, j, _ncolumns), j, j, _ncolumns)
    //
    _Ainv = multiply(_Rinv, _QT, _ncolumns, _ncolumns, _nrows)
    _Ainv

norm_rmse(_x, _xhat) =>
    // Root Mean Square Error normalized to the sample mean
    // _x.   :: array float, original data
    // _xhat :: array float, model estimate
    // output
    // _nrmse:: float
    float _nrmse = 0.0
    if array.size(_x) != array.size(_xhat)
        _nrmse := na
        _nrmse
    else
        int _N = array.size(_x)
        float _mse = 0.0
        for i = 0 to _N - 1 by 1
            _mse += math.pow(array.get(_x, i) - array.get(_xhat, i), 2) / _N
            _mse
        _xmean = array.sum(_x) / _N
        _nrmse := math.sqrt(_mse) / _xmean
        _nrmse
    _nrmse


diff(_src, _window, _degree) =>
    // Polynomial differentiator
    // input:
    // _src:: input series
    // _window:: integer: wigth of the moving lookback window
    // _degree:: integer: degree of fitting polynomial
    // output:
    // _diff :: series: time derivative
    // _nrmse:: float: normalized root mean square error
    //
    // Vandermonde matrix with implied dimensions (window x degree+1)
    // Linear form: J = [ [z]^0, [z]^1, ... [z]^degree], with z = [ (1-window)/2 to (window-1)/2 ] 
    var _J = array.new_float(_window * (_degree + 1), 0)
    for i = 0 to _window - 1 by 1
        for j = 0 to _degree by 1
            matrix_set(_J, math.pow(i, j), i, j, _window)
    // Vector of raw datapoints:
    var _Y_raw = array.new_float(_window, na)
    for j = 0 to _window - 1 by 1
        array.set(_Y_raw, j, _src[_window - 1 - j])
    // Calculate polynomial coefficients which minimize the loss function
    _C = pinv(_J, _window, _degree + 1)
    _a_coef = multiply(_C, _Y_raw, _degree + 1, _window, 1)
    // For first derivative, approximate the last point (i.e. z=window-1) by 
    float _diff = 0.0
    for i = 1 to _degree by 1
        _diff += i * array.get(_a_coef, i) * math.pow(_window - 1, i - 1)
        _diff
    // Calculates data estimate (needed for rmse)
    _Y_hat = multiply(_J, _a_coef, _window, _degree + 1, 1)
    float _nrmse = norm_rmse(_Y_raw, _Y_hat)
    [_diff, _nrmse]

/// --- main ---
degree = input.int(title='Polynomial Order', group='Model Parameters:', inline='linepar1', defval=2, minval=1)
rsi_l = input.int(title='RSI Length', group='Model Parameters:', inline='linepar1', defval=21, minval=1, tooltip='The period length of RSI that is used as input.')
window = input.int(title='Length ( > Order)', group='Model Parameters:', inline='linepar2', defval=21, minval=2)
signalLength = input.int(title='Signal Length', group='Model Parameters:', inline='linepar2', defval=9, tooltip='The signal line is a EMA of the D-RSI time series.')
islong = input.bool(title='Buy', group='Show Signals:', inline='lineent', defval=true)
isshort = input.bool(title='Sell', group='Show Signals:', inline='lineent', defval=true)
showendlabels = input.bool(title='Exit', group='Show Signals:', inline='lineent', defval=true)
buycond = input.string(title='Buy', group='Entry and Exit Conditions:', inline='linecond', defval='Zero-Crossing', options=['Zero-Crossing', 'Signal Line Crossing', 'Direction Change'])
sellcond = input.string(title='Sell', group='Entry and Exit Conditions:', inline='linecond', defval='Zero-Crossing', options=['Zero-Crossing', 'Signal Line Crossing', 'Direction Change'])
endcond = input.string(title='Exit', group='Entry and Exit Conditions:', inline='linecond', defval='Zero-Crossing', options=['Zero-Crossing', 'Signal Line Crossing', 'Direction Change'])
usenrmse = input.bool(title='', group='Filter by Means of Root-Mean-Square Error of RSI Fitting:', inline='linermse', defval=false)
rmse_thrs = input.float(title='RSI fitting Error Threshold, %', group='Filter by Means of Root-Mean-Square Error of RSI Fitting:', inline='linermse', defval=10, minval=0.0) / 100


src = ta.rsi(close, rsi_l)
[drsi, nrmse] = diff(src, window, degree)
signalline = ta.ema(drsi, signalLength)

// Conditions and filters
filter_rmse = usenrmse ? nrmse < rmse_thrs : true
dirchangeup = drsi > drsi[1] and drsi[1] < drsi[2] and drsi[1] < 0.0
dirchangedw = drsi < drsi[1] and drsi[1] > drsi[2] and drsi[1] > 0.0
crossup = ta.crossover(drsi, 0.0)
crossdw = ta.crossunder(drsi, 0.0)
crosssignalup = ta.crossover(drsi, signalline)
crosssignaldw = ta.crossunder(drsi, signalline)

//Signals
golong = (buycond == 'Direction Change' ? dirchangeup : buycond == 'Zero-Crossing' ? crossup : crosssignalup) and filter_rmse
goshort = (sellcond == 'Direction Change' ? dirchangedw : sellcond == 'Zero-Crossing' ? crossdw : crosssignaldw) and filter_rmse
endlong = (endcond == 'Direction Change' ? dirchangedw : endcond == 'Zero-Crossing' ? crossdw : crosssignaldw) and filter_rmse
endshort = (endcond == 'Direction Change' ? dirchangeup : endcond == 'Zero-Crossing' ? crossup : crosssignalup) and filter_rmse
plotshape(golong and islong ? low : na, location=location.belowbar, style=shape.labelup, color=color.new(#2E7C13, 0), size=size.small, title='Buy')
plotshape(goshort and isshort ? high : na, location=location.abovebar, style=shape.labeldown, color=color.new(#BF217C, 0), size=size.small, title='Sell')
plotshape(showendlabels and endlong and islong ? high : na, location=location.abovebar, style=shape.xcross, color=color.new(#2E7C13, 0), size=size.tiny, title='Exit Long')
plotshape(showendlabels and endshort and isshort ? low : na, location=location.belowbar, style=shape.xcross, color=color.new(#BF217C, 0), size=size.tiny, title='Exit Short')

alertcondition(golong, title='Long Signal', message='D-RSI: Long Signal')
alertcondition(goshort, title='Short Signal', message='D-RSI: Short Signal')
alertcondition(endlong, title='Exit Long Signal', message='D-RSI: Exit Long')
alertcondition(endshort, title='Exit Short Signal', message='D-RSI: Exit Short')

strategy.entry('long', strategy.long, when=golong and islong)
strategy.entry('short', strategy.short, when=goshort and isshort)
strategy.close('long', when=endlong and islong)
strategy.close('short', when=endshort and isshort)



Có liên quan

Thêm nữa