SuperTrend V.1 -- Hệ thống đường xu hướng siêu

Tác giả:Lydia., Tạo: 2022-12-01 11:36:33, Cập nhật: 2023-09-11 20:04:38

img

I. Nguồn gốc của câu chuyện

Ông Ran, người bạn tốt của tôi, đã quan sát chỉ số này trong một thời gian dài và đề nghị nó cho tôi trước ngày đầu năm mới để thảo luận liệu nó có thể được chuyển thành định lượng hay không. Thật là đáng tiếc khi người trì hoãn đã trì hoãn đến bây giờ để giúp anh ấy thực hiện một mong muốn như vậy. Người ta ước tính rằng một ngày nào đó tôi sẽ viết một phiên dịch cho ngôn ngữ Pine. Mọi thứ đều có thể là Python. Vâng, không có nhiều chuyện vớ vẩn, hãy giới thiệu dòng siêu xu hướng huyền thoại.

II. Việc giới thiệu hệ thống

Trong thế hệ mới của hệ thống giao dịch thông minh trong CMC Markets, chúng ta có thể chọn Super Trend Line từ các chỉ số kỹ thuật để sử dụng, Chúng ta có thể điều chỉnh màu sắc và độ dày của tín hiệu tăng và giảm theo sở thích của mình. Vậy chỉ số siêu xu hướng là gì? Trước khi hiểu công thức chỉ số siêu xu hướng, cần phải hiểu ATR, bởi vì siêu xu hướng sử dụng giá trị ATR để tính giá trị chỉ số. Các thuật toán chính cũng được mô tả trong hình sau:

img

Hãy xem một chút. Nó chủ yếu mô tả kênh nơi HL2 (giá trung bình k-line) cộng với n lần ATR. Nhưng bài viết rất đơn giản. Không có thuật toán chi tiết. Quả thật, nó thực sự ở đó.

img

Nhìn vào biểu đồ, nó phù hợp với xu hướng.

III. Học mã nguồn

Mã không quá dài, nên hãy thử dịch nó.!(っ•̀ω•́)っ))!

img

Mã pin hoàn chỉnh là như trên.

VI. Chuyển đổi mã

Ở đây chúng ta tạo ra một chiến lược mới trên FMZ, đặt tên nó là SuperTrend

img

Tiếp theo, chúng ta sẽ thiết lập hai tham số, Factor và Pd

img

Để đơn giản hóa tốt hơn các hoạt động mã và tạo điều kiện dễ hiểu, chúng ta cần sử dụng pythons gói mở rộng dữ liệu tiên tiến Pandas (https://pandas.pydata.org/) FMZ hỗ trợ thư viện này bây giờ.

  1. Chúng ta cần nhập thư viện Panda và thư viện thời gian.
  2. Trong chức năng chính, thiết lập việc sử dụng các hợp đồng hàng quý (chủ yếu cho okex)
  3. Thiết lập một chu kỳ doTicker để phát hiện mỗi 15 phút một lần. Chạy mã trên một khoảng thời gian 15 phút Sau đó chúng ta viết chiến lược chính trong doTicker ().
import pandas as pd
import time

def main():
    exchange.SetContractType("quarter")
    preTime = 0
    Log(exchange.GetAccount())
    while True:
        records = exchange.GetRecords(PERIOD_M15)
        if records and records[-2].Time > preTime:
            preTime = records[-2].Time
            doTicker(records[:-1])
        Sleep(1000 *60)
  1. Chúng tôi cần lấy OHCLV của K-line, vì vậy chúng tôi sử dụng GetRecords()
  2. Chúng tôi nhập dữ liệu được lấy vào panda M15 = pd.DataFrame ((records)
  3. Chúng ta cần sửa đổi nhãn tiêu đề của bảng. M15.columns = [time,open,high,low,close,volume,OpenInterest] Trên thực tế, nó là để thay đổi các chữ cái đầu tiên của open, high, low, và close thành chữ cái nhỏ, để viết mã sau này của chúng tôi không cần phải thay đổi từ chữ cái lớn sang chữ cái nhỏ.
def doTicker(records):
    M15 = pd.DataFrame(records)
    M15.columns = ['time','open','high','low','close','volume','OpenInterest']  
  1. Thêm một cột vào bộ dữ liệu hl2 hl2=(high+low)/2
#HL2
M15['hl2']=(M15['high']+M15['low'])/2
  1. Sau đó hãy tính ATR Bởi vì tính toán ATR cần nhập một chiều dài biến, có giá trị là Pd

Sau đó, chúng ta tham khảo hướng dẫn của MyLanguage, và các bước thuật toán của giá trị trung bình của kích thước biến động thực ATR là như sau: TR: MAX ((MAX ((((HIGH-LOW),ABS ((REF ((CLOSE,1) -HIGH)),ABS ((REF ((CLOSE,1) -LOW)); ATR: RMA(TR,N)

Giá trị TR là tối đa của ba sự khác biệt sau:

  1. Sự biến động giữa giá cao nhất và giá thấp nhất trong ngày giao dịch hiện tại HIGH-LOW
  2. Sự biến động giữa giá đóng cửa của ngày giao dịch trước và giá cao nhất của ngày giao dịch hiện tại REF (CLOSE, 1) - HIGH
  3. Sự biến động giữa giá đóng cửa ngày giao dịch trước và giá thấp nhất của ngày giao dịch hiện tại REF (CLOSE, 1) - LOW Vì vậy, TR: MAX ((MAX ((((HIGH-LOW),ABS ((REF ((CLOSE,1)-HIGH)),ABS ((REF ((CLOSE,1)-LOW));

Trong tính toán Python

M15['prev_close']=M15['close'].shift(1)

Chúng ta cần thiết lập một prev_close để lấy dữ liệu của đóng trong dòng trước đó, đó là, di chuyển gần bên phải bởi một lưới để hình thành một tham số mới

ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]

Tiếp theo, chúng ta xác định một biến trung gian ghi lại một mảng 3 giá trị tương phản cho TR. (HIGH-LOW) (high-prev_close) (low-prev_close)

M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)

Chúng tôi xác định một cột mới trong bộ dữ liệu và đặt tên nó là TR. Giá trị của TR là giá trị tuyệt đối lớn nhất của biến trung gian, sử dụng các hàm abs () và max ()

    alpha = (1.0 / length) if length > 0 else 0.5
    M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()

Cuối cùng, chúng ta cần tính toán giá trị của ATR, ATR: RMA (TR, N). N là biến mà chúng ta nhập. tham số mặc định của ATR là 14.

===

Sau đó thuật toán ewm được sử dụng để tính toán ema Quá trình tính toán ATR hoàn chỉnh là như sau:

    #ATR(PD)
    length=Pd
    M15['prev_close']=M15['close'].shift(1)
    ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
    M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
    alpha = (1.0 / length) if length > 0 else 0.5
    M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()

9 Bắt đầu tính Up và Dn

    M15['Up']=M15['hl2']-(Factor*M15['atr'])
    M15['Dn']=M15['hl2']+(Factor*M15['atr'])

Up=hl2 - ((Factor * atr) Dn=hl2 + ((Factor * atr) Không đơn giản sao?

Đây là phần mã chính của dòng 15-21 từ TV

TrendUp=close[1]>TrendUp[1]? max(Up,TrendUp[1]) : Up
TrendDown=close[1]<TrendDown[1]? min(Dn,TrendDown[1]) : Dn

Trend = close > TrendDown[1] ? 1: close< TrendUp[1]? -1: nz(Trend[1],1)
Tsl = Trend==1? TrendUp: TrendDown

linecolor = Trend == 1 ? green : red

Điểm chính của đoạn này là thể hiện rằng, Nếu nó ở giai đoạn tăng, (dòng dưới) TrendUp=max (Up, TrendUp [1]) Nếu nó ở giai đoạn giảm, (dòng trên) TrendDown=min (Dn, TrendDown [1]) Điều đó có nghĩa là trong một xu hướng, giá trị ATR đã sử dụng một công nghệ tương tự như chiến lược Bandit Bollinger. Hãy tiếp tục thu hẹp bên kia kênh.

Ở đây, mỗi tính toán của TrendUp và TrendDown đòi hỏi tự lặp. Đó là, mỗi bước nên được tính theo bước trước đó. Do đó, bộ dữ liệu nên được lặp lại trong vòng lặp.

Đầu tiên, chúng ta tạo các trường mới TrendUp, TrendDown, Trend, linecolor cho bộ dữ liệu. Sau đó chúng ta sử dụng fillna ngữ pháp (0) để điền dữ liệu với giá trị không trong kết quả được tính toán trước đó với 0

    M15['TrendUp']=0.0
    M15['TrendDown']=0.0
    M15['Trend']=1
    M15['Tsl']=0.0
    M15['linecolor']='Homily'
    M15 = M15.fillna(0)

Cho phép vòng lặp for Sử dụng Python hoạt động ba trong vòng lặp

    for x in range(len(M15)):

Tính toán TrendUp TrendUp = MAX(Up,TrendUp[-1]) nếu gần[-1]>TrendUp[-1] nếu không Up Nó gần như có nghĩa là nếu trước đây đóng>TrendUp trước đó là đúng, giá trị tối đa giữa Up và TrendUp trước đó sẽ được lấy; nếu không, giá trị Up sẽ được lấy và chuyển sang TrendUp hiện tại

        M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]

Tương tự, tính toán TrendDown TrendDown=min(Dn,TrendDown[-1]) nếu gần[-1]

        M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]

Sau đây là biểu tượng để tính toán hướng điều khiển. Tôi đơn giản hóa mã giả Xu hướng = 1 nếu (kết thúc > Xu hướngDown[-1]) khác (x) x = -1 nếu (close< TrendUp[-1]) khác Trend[-1]

Điều này có nghĩa là nếu giá đóng> xu hướng trước đó, lấy giá trị của 1 (bullish). Nếu giá đóng cửa thấp hơn so với TrendUp trước đó, lấy giá trị -1 (giảm). Nếu không, lấy xu hướng trước (có nghĩa là không thay đổi) Để dịch thành ngôn ngữ hình ảnh là một sự đột phá của cờ chuyển đổi đường trên cho tăng; và một sự đột phá của cờ chuyển đổi đường dưới cho giảm.

        M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]

Tính toán Tsl và Linecolor Tsl= RendUp nếu (Trend==1) khác TrendDown Tsl là giá trị được sử dụng để đại diện cho SuperTrend trên hình ảnh. Nó có nghĩa là để đánh dấu theo dõi xuống trên hình ảnh khi chúng ta đang trong tăng, và đánh dấu theo dõi trên hình ảnh khi chúng ta đang trong giảm. linecolor= green nếu (Trend==1) khác red Ý nghĩa của linecolor là để đánh dấu đường màu xanh lá cây nếu chúng ta đang trong tăng, và đánh dấu màu trống nếu chúng ta đang trong giảm (chủ yếu cho mục đích hiển thị Tradeview)

        M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
        M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else  'red'

23-30 dòng mã tiếp theo chủ yếu là các bản vẽ phác thảo, không được giải thích ở đây.

Cuối cùng, có 2 dòng mã để điều khiển tín hiệu mua và bán. Trong Tradeview, nó có nghĩa là tín hiệu được đưa ra sau khi đảo ngược cờ Chuyển câu lệnh có điều kiện thành python. Nếu dấu hiệu xu hướng cuối cùng thay đổi từ -1 thành 1, điều đó có nghĩa là ngưỡng kháng cự trên đã được vượt quá và mở vị trí mua. Nếu dấu hiệu xu hướng cuối cùng thay đổi từ 1 sang -1, điều đó có nghĩa là hỗ trợ giảm đã được vượt quá và mở vị trí mua.

    if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
        Log('SuperTrend V.1 Alert Long',"Create Order Buy)
    if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
        Log('SuperTrend V.1 Alert Long',"Create Order Sell)

Mã đầy đủ là như sau:

    M15['TrendUp']=0.0
    M15['TrendDown']=0.0
    M15['Trend']=1
    M15['Tsl']=0.0
    M15['linecolor']='Homily'
    M15 = M15.fillna(0)
    
    for x in range(len(M15)):
        M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
        M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
        M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] )
        M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
        M15['linecolor'].values[x]= 'green' if ( M15['Trend'].values[x]==1) else  'red'
        
    if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):
        Log('SuperTrend V.1 Alert Long',"Create Order Buy)
        Log('Tsl=',Tsl)
    if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
        Log('SuperTrend V.1 Alert Long',"Create Order Sell)
        Log('Tsl=',Tsl)

img img

V. Mã đầy đủ

Tôi đã điều chỉnh cấu trúc mã tổng thể. Và tôi hợp nhất các hướng dẫn lệnh liên quan đến đi dài và đi ngắn vào chiến lược. Đây là mã đầy đủ:

'''backtest
start: 2019-05-01 00:00:00
end: 2020-04-21 00:00:00
period: 15m
exchanges: [{"eid":"Futures_OKCoin","currency":"BTC_USD"}]
'''

import pandas as pd
import time

def main():
    exchange.SetContractType("quarter")
    preTime = 0
    Log(exchange.GetAccount())
    while True:
        records = exchange.GetRecords(PERIOD_M15)
        if records and records[-2].Time > preTime:
            preTime = records[-2].Time
            doTicker(records[:-1])
        Sleep(1000 *60)

       
def doTicker(records):
    #Log('onTick',exchange.GetTicker())
    M15 = pd.DataFrame(records)

    #Factor=3
    #Pd=7
    
    M15.columns = ['time','open','high','low','close','volume','OpenInterest']  
    
    #HL2
    M15['hl2']=(M15['high']+M15['low'])/2

    #ATR(PD)
    length=Pd
    M15['prev_close']=M15['close'].shift(1)
    ranges= [M15['high'] - M15['low'],M15['high']-M15['prev_close'],M15['low']-M15['prev_close']]
    M15['tr'] = pd.DataFrame(ranges).T.abs().max(axis=1)
    alpha = (1.0 / length) if length > 0 else 0.5
    M15['atr']=M15['tr'].ewm(alpha=alpha, min_periods=length).mean()


    M15['Up']=M15['hl2']-(Factor*M15['atr'])
    M15['Dn']=M15['hl2']+(Factor*M15['atr'])
    
    M15['TrendUp']=0.0
    M15['TrendDown']=0.0
    M15['Trend']=1
    M15['Tsl']=0.0
    M15['linecolor']='Homily'
    M15 = M15.fillna(0)

    for x in range(len(M15)):
        M15['TrendUp'].values[x] = max(M15['Up'].values[x],M15['TrendUp'].values[x-1]) if (M15['close'].values[x-1]>M15['TrendUp'].values[x-1]) else M15['Up'].values[x]
        M15['TrendDown'].values[x] = min(M15['Dn'].values[x],M15['TrendDown'].values[x-1]) if (M15['close'].values[x-1]<M15['TrendDown'].values[x-1]) else M15['Dn'].values[x]
        M15['Trend'].values[x] = 1 if (M15['close'].values[x] > M15['TrendDown'].values[x-1]) else ( -1 if (M15['close'].values[x]< M15['TrendUp'].values[x-1])else M15['Trend'].values[x-1] )
        M15['Tsl'].values[x] = M15['TrendUp'].values[x] if  (M15['Trend'].values[x]==1) else M15['TrendDown'].values[x]
        M15['linecolor'].values[x]= 'Long' if ( M15['Trend'].values[x]==1) else  'Short'
 

    linecolor=M15['linecolor'].values[-2]
    close=M15['close'].values[-2]
    Tsl=M15['Tsl'].values[-2] 


    if(M15['Trend'].values[-1] == 1 and M15['Trend'].values[-2] == -1):

        Log('SuperTrend V.1 Alert Long','Create Order Buy')
        Log('Tsl=',Tsl)
        position = exchange.GetPosition()
        if len(position) > 0:
            Amount=position[0]["Amount"]
            exchange.SetDirection("closesell")
            exchange.Buy(_C(exchange.GetTicker).Sell*1.01, Amount);
        
        exchange.SetDirection("buy")
        exchange.Buy(_C(exchange.GetTicker).Sell*1.01, vol);

    if(M15['Trend'].values[-1] == -1 and M15['Trend'].values[-2] == 1):
        Log('SuperTrend V.1 Alert Long','Create Order Sell')
        Log('Tsl=',Tsl)
        position = exchange.GetPosition()
        if len(position) > 0:
            Amount=position[0]["Amount"]
            exchange.SetDirection("closebuy")
            exchange.Sell(_C(exchange.GetTicker).Buy*0.99,Amount);
        exchange.SetDirection("sell")
        exchange.Sell(_C(exchange.GetTicker).Buy*0.99, vol*2);

Địa chỉ chiến lược công cộng:https://www.fmz.com/strategy/200625

VI. Kiểm tra hậu quả và tóm tắt

Chúng tôi đã chọn dữ liệu của năm ngoái để kiểm tra lại. Chúng tôi sử dụng hợp đồng hàng quý OKEX trong một khoảng thời gian 15 phút. Các thông số được thiết lập là: Nhân tố = 3 Pd=45 Vol=100 (100 hợp đồng cho mỗi đơn đặt hàng) Lợi nhuận hàng năm là khoảng 33%. Nói chung, việc rút tiền không quá nhiều, Sự giảm mạnh của 312 có tác động tương đối lớn đến hệ thống, Nếu không có 312, thì lợi nhuận sẽ tốt hơn.

img

VII. Viết vào cuối

SuperTrend là một hệ thống giao dịch rất tốt

Nguyên tắc chính của hệ thống SuperTrend là áp dụng chiến lược đột phá kênh ATR (tương tự như kênh Kent) Tuy nhiên, sự thay đổi của nó chủ yếu là do sử dụng chiến lược thu hẹp của Bandit Bollinger, hoặc ngược lại của nguyên tắc Donchian. Trong hoạt động thị trường, các kênh trên và dưới liên tục bị thu hẹp. Để đạt được hoạt động điều khiển xuyên kênh. (Một khi kênh vượt qua, các đường ray trên và dưới sẽ trở lại giá trị ban đầu)

Tôi vẽ lên, dn, TrendUp và TrendDn riêng biệt trên TradeView, Điều này làm cho nó dễ dàng hơn để hiểu rõ hơn chiến lược. Nhìn một cái nhìn rõ ràng:

img

Ngoài ra, có một phiên bản của js trên github. Tôi không giỏi js, nhưng có vẻ như có một cái gì đó sai với câu lệnh if. Địa chỉ:https://github.com/Dodo33/gekko-supertrend-strategy/blob/master/Supertrend.js

Cuối cùng, tôi đã tìm ra phiên bản gốc. Được xuất bản vào ngày 29 tháng 5 năm 2013 Tác giả là Rajandran R. Mã C ++ đã được xuất bản trên diễn đàn Mt4:https://www.mql5.com/en/code/viewcode/10851/128437/Non_Repainting_SuperTrend.mq4Tôi đã hiểu được ý nghĩa của C++, và tôi sẽ viết lại nó khi có cơ hội.

Tôi hy vọng bạn có thể học được bản chất từ nó. Thật khó!


Có liên quan

Thêm nữa