Hệ thống backtest tần số cao dựa trên mỗi giao dịch và các khiếm khuyết của backtest đường K

Tác giả:Tốt, Tạo: 2020-06-16 10:30:19, Cập nhật: 2023-11-01 20:26:21

img

Khi tôi viếtNghiên cứu về Binance Futures Multi-currency Hedging Strategy, Tôi cũng phát hành một công cụ backtest. Và báo cáo đầu tiên dựa trên backtest K-line một giờ, xác minh hiệu quả của chiến lược. Nhưng thời gian ngủ của chiến lược nguồn mở thực tế là 1 giây, đó là một chiến lược tần số khá cao. Rõ ràng, sử dụng backtest K-line hàng giờ không thể tạo ra kết quả chính xác. Sau đó, kết quả backtest của K-line cấp phút đã được thêm vào, và doanh thu backtest đã cải thiện rất nhiều, nhưng vẫn không thể xác định các thông số nên được sử dụng trong trường hợp seconds level, và sự hiểu biết về toàn bộ chiến lược không rõ ràng. Lý do chính là nhược điểm quan trọng của backtest dựa trên K-line.

Các vấn đề dựa trên K-line backtest

Đầu tiên, đường K lịch sử là gì? Dữ liệu đường K chứa bốn giá cao, mở, thấp, đóng, hai lần đầu tiên và khối lượng của khoảng thời gian. Hầu hết các nền tảng và khung định lượng dựa trên kiểm tra hậu quả đường K, và nền tảng FMZ cũng cung cấp kiểm tra hậu quả cấp độ dấu chấm. Tốc độ kiểm tra hậu quả đường K rất nhanh, và trong hầu hết các trường hợp nó có rất ít vấn đề, nhưng nó cũng có những khiếm khuyết rất nghiêm trọng, đặc biệt là chiến lược đa đa dạng và chiến lược tần số cao, hầu như không thể rút ra kết luận chính xác.

Đầu tiên là vấn đề thời gian. Thời gian của giá cao nhất và giá thấp nhất của dữ liệu đường K không được đưa ra và không cần phải xem xét, nhưng giá mở và đóng quan trọng nhất không phải là thời gian mở và đóng. Ngay cả các loại giao dịch ít phổ biến hơn thường không giao dịch trong hơn mười giây, và khi chúng ta kiểm tra lại chiến lược đa loại, chúng ta thường giả định rằng giá mở và giá đóng của chúng là giống nhau, cũng dựa trên kiểm tra lại giá đóng.

Hãy tưởng tượng sử dụng đường K cấp phút để kiểm tra lại sự điều chỉnh của hai loại. Sự khác biệt giữa chúng thường là 10 nhân dân tệ (hoặc đô la). Bây giờ, lúc 10:01, giá đóng của hợp đồng A là 100, hợp đồng B là 112, và sự khác biệt là 12 nhân dân tệ. Vì vậy, chiến lược bắt đầu phòng ngừa rủi ro. Vào một thời điểm nhất định, sự khác biệt giá trở lại, và chiến lược đã tạo ra lợi nhuận trở lại 2 nhân dân tệ.

nhưng tình huống thực tế có thể là lúc 10:00:45, hợp đồng A đã tạo ra một giao dịch 100 nhân dân tệ, sau đó không có giao dịch, hợp đồng B có một giao dịch 112 nhân dân tệ lúc 10:00:58, lúc 10:01:00 Cả hai giá cả không tồn tại. Giá thị trường vào thời điểm này là gì, và hoạt động phòng hộ có thể nhận được bao nhiêu? Tôi không thể biết. Một tình huống có thể là: lúc 10:00:58, giá lệnh chờ mua 1 và bán 1 của hợp đồng A là101.9đến102.1, và không có sự khác biệt của 2 nhân dân tệ ở tất cả.

Thứ hai là vấn đề kết hợp. Kết hợp thực sự là ưu tiên giá và ưu tiên thời gian. Nếu người mua vượt quá giá Bán 1, họ thường sẽ giao dịch trực tiếp với giá Bán 1, nếu không họ sẽ nhập sổ đơn đặt hàng đang chờ và chờ đợi. Dữ liệu đường K rõ ràng không có giá Buy 1 và giá Bán 1, không thể mô phỏng mức độ kết hợp giá chi tiết.

Cuối cùng là tác động của chính chiến lược trên thị trường. Nếu đó là một backtest của số tiền nhỏ, tác động không lớn. Nhưng nếu khối lượng giao dịch lớn, nó sẽ có tác động đến thị trường. Không chỉ sẽ có sự trượt giá lớn khi bạn đặt một lệnh khối lượng lớn, nếu bạn mua lệnh dài thực hiện, hành động này thực sự nắm bắt các lệnh của các nhà giao dịch khác ban đầu muốn mua, hiệu ứng bướm sẽ có tác động đến thị trường. Hiệu ứng này không thể định lượng được. Chúng ta chỉ có thể nói từ kinh nghiệm rằng giao dịch tần số cao chỉ có thể chứa các quỹ nhỏ.

Backtest dựa trên độ sâu và dấu hiệu thời gian thực

FMZ cung cấp thực tế cấp backtest, có thể có được thực sự lịch sử20 layer depth price, thời gian thực cấp haiTicks, Each Individual TransactionDựa trên các tính năng này, FMZ đã tạo ra một chức năng phát lại giao dịch thời gian thực.

Số lượng dữ liệu backtest này rất lớn và tốc độ backtest cũng rất chậm, thường chỉ có thể backtest trong hai ngày. Đối với các chiến lược có tần suất tương đối cao hoặc thời gian quan trọng, backtest cấp thị trường thực sự là cần thiết. Các cặp giao dịch và thời gian giao dịch được thu thập bởi FMZ không quá dài, nhưng vẫn còn hơn 70 tỷ dữ liệu lịch sử.

Cơ chế kết hợp hiện tại là nếu lệnh mua lớn hơn Bán 1, nó sẽ được hoàn toàn khớp ngay lập tức mà không cần xem số tiền, và nếu nhỏ hơn Bán 1, nó sẽ vào hàng đợi để chờ. Cơ chế backtest như vậy giải quyết hai vấn đề đầu tiên của backtest K-line, nhưng vẫn không thể giải quyết vấn đề cuối cùng. Và vì số lượng dữ liệu quá lớn, tốc độ và phạm vi thời gian backtest bị hạn chế.

img

Cơ chế backtest dựa trên dòng giao dịch theo từng lệnh

Có quá ít thông tin trong đường K, và chiều sâu giá cũng có thể là một chiều sâu giả, nhưng có một loại dữ liệu đó là thị trường thực sự sẵn sàng giao dịch, phản ánh lịch sử giao dịch thực nhất, đó là,Each Individual TransactionBài viết này sẽ đề xuất một hệ thống backtest tần số cao dựa trên dòng lệnh, sẽ làm giảm đáng kể khối lượng dữ liệu backtest trên thị trường thực và ở một mức độ nhất định mô phỏng tác động của khối lượng giao dịch trên thị trường.

Tôi đã tải về giao dịch của 5 ngày qua Binance XTZ hợp đồng vĩnh viễn (địa chỉ tải về:https://www.fmz.com/upload/asset/1ff487b007e1a848ead.csv), là một loại không phổ biến, nó có tổng số 213000 dữ liệu giao dịch, trước tiên hãy xem xét thành phần của dữ liệu:

[['XTZ', 1590981301905, 2.905, 0.4, 'False\n'],
 ['XTZ', 1590981303044, 2.903, 3.6, 'True\n'],
 ['XTZ', 1590981303309, 2.903, 3.7, 'True\n'],
 ['XTZ', 1590981303738, 2.903, 238.1, 'True\n'],
 ['XTZ', 1590981303892, 2.904, 0.1, 'False\n'],
 ['XTZ', 1590981305250, 2.904, 0.1, 'False\n'],
 ['XTZ', 1590981305643, 2.903, 197.3, 'True\n'],

Dữ liệu là một danh sách hai chiều, được sắp xếp theo thứ tự thời gian. Ý nghĩa cụ thể là như sau: tên giống, giá giao dịch, dấu thời gian giao dịch, số lượng giao dịch, cho dù đó là một giao dịch hoạt động lệnh bán. Có mua và bán bên, và mỗi giao dịch bao gồm cả người mua và người bán. Nếu người mua là một thị trườngMakervà người bán là một người hoạt độngTaker, dữ liệu cuối cùng làTrue.

Trước hết, theo hướng giao dịch, bạn có thể suy đoán khá chính xác về Buy 1Sell 1 trên thị trường. Nếu đó là lệnh bán tích cực, thì giá Buy 1 tại thời điểm này là giá giao dịch, nếu đó là lệnh mua tích cực, giá Sell 1 sẽ là giá giao dịch. Nếu có giao dịch mới, thì tất cả giá sẽ được gia hạn và cập nhật. Kết quả cuối cùng sẽ được giữ lại nếu không có gia hạn và cập nhật. Thật dễ dàng để đưa ra khoảnh khắc cuối cùng của dữ liệu trên, giá Buy 1 là 2.903, và giá Sell 1 là 2.904.

Theo dòng lệnh, nó có thể được phù hợp theo cách này: lấy một lệnh mua như một ví dụ, giá làprice, số lượng đặt hàng làamount, sau đó mua và bán 1 tại thời điểm này làbidasktương ứngpricethấp hơnaskvà cao hơnbid, sau đó nó được đánh giá làmakerđầu tiên, và ưu tiên có thể được phù hợp để thực hiện một thỏa thuận, sau đó tất cả các giao dịch với một giá giao dịch thấp hơn hoặc bằngpricetrong thời gian tồn tại của lệnh sẽ được khớp với lệnh này (nếupricethấp hơn hoặc bằngbidCác lệnh với giá giao dịch thấp hơnpriceđược khớp với thứ tự này.)

Giá phù hợp làprice, và khối lượng là khối lượng giao dịch củaEach Individual Transaction, cho đến khi lệnh được hoàn thành hoàn toàn hoặc lệnh bị hủy bỏ.ask, nó được đánh giá là mộttakerSau đó, trong thời gian lệnh tồn tại, tất cả các giao dịch với giá giao dịch thấp hơn hoặc bằngpriceđược khớp với lệnh này, và giá khớp là giá giao dịch củaEach Individual TransactionSự khác biệt giữamakertakerĐối với các chiến lược tần số cao, sự khác biệt này phải được xem xét.

Nó dễ dàng để thấy một vấn đề với loại này phù hợp.taker, tình hình thực tế là nó có thể được thực hiện ngay lập tức, thay vì chờ đợi một đơn đặt hàng mới được phù hợp với nó. trước hết, chúng tôi không xem xét khối lượng các đơn đặt hàng đang chờ, ngay cả khi có một số dữ liệu, trực tiếp đánh giá giao dịch cũng đã thay đổi chiều sâu giá, ảnh hưởng đến thị trường.

Một phần của cơ chế khớp cũng ảnh hưởng đến khối lượng đơn đặt hàng, từ đó ảnh hưởng đến doanh thu của chiến lược, phản ánh định lượng năng lực của chiến lược. Sẽ không có kiểm tra hậu quả truyền thống, khi số tiền tăng gấp đôi và lợi nhuận tăng gấp đôi.

Vẫn còn một số chi tiết nhỏ. Nếu giá mua của lệnh bằng Buy 1, vẫn có một xác suất nhất định rằng giá mua sẽ được khớp với Buy 1, tình huống này sẽ không được xem xét ở đây.

Mã khớp

Các đối tượng trao đổi có thể tham chiếu đến việc giới thiệu ở đầu, về cơ bản không thay đổi, chỉ thêm sự khác biệt giữamakertakerSau đây sẽ chủ yếu giới thiệu mã phù hợp.

 symbol = 'XTZ'
    loop_time = 0
    intervel = 1000 # The sleep time of the strategy is 1000ms
    init_price = data[0][2] # Initial price
    e = Exchange([symbol],initial_balance=1000000,maker_fee=maker_fee,taker_fee=taker_fee,log='') # Initialize the exchange
    depth = {'ask':data[0][2], 'bid':data[0][2]} # depth
    order = {'buy':{'price':0,'amount':0,'maker':False,'priority':False,'id':0},
             'sell':{'price':0,'amount':0,'maker':False,'priority':False,'id':0}} # order
    for tick in data:
        price = int(tick[2]/tick_sizes[symbol])*tick_sizes[symbol] # executed price
        trade_amount = tick[3] # executed volume
        time_stamp = tick[1] # executed timestamp
        if tick[4] == 'False\n':
            depth['ask'] = price
        else:
            depth['bid'] = price
        
        if depth['bid'] < order['buy']['price']:
            order['buy']['priority'] = True
        if depth['ask'] > order['sell']['price']:
            order['sell']['priority'] = True
        if price > order['buy']['price']:
            order['buy']['maker'] = True
        if price < order['sell']['price']:
            order['sell']['maker'] = True
        
        # Order network delay can also be used as one of the matching conditions, not considered here
        cond1 = order['buy']['priority'] and order['buy']['price'] >= price and order['buy']['amount'] > 0
        cond2 = not order['buy']['priority'] and order['buy']['price'] > price and order['buy']['amount'] > 0
        cond3 = order['sell']['priority'] and order['sell']['price'] <= price and order['sell']['amount'] > 0
        cond4 = not order['sell']['priority'] and order['sell']['price'] < price and order['sell']['amount'] > 0

        if cond1 or cond2:
            buy_price = order['buy']['price'] if order['buy']['maker'] else price
            e.Buy(symbol, buy_price, min(order['buy']['amount'],trade_amount), order['buy']['id'], order['buy']['maker'])
            order['buy']['amount'] -= min(order['buy']['amount'],trade_amount)
            e.Update(time_stamp,[symbol],{symbol:price})
        if cond3 or cond4:
            sell_price = order['sell']['price'] if order['sell']['maker'] else price
            e.Sell(symbol, sell_price, min(order['sell']['amount'],trade_amount), order['sell']['id'], order['sell']['maker'])
            order['sell']['amount'] -= min(order['sell']['amount'],trade_amount)
            e.Update(time_stamp,[symbol],{symbol:price})

        if time_stamp - loop_time > intervel:
            order = get_order(e,depth,order) # Trading logic, not given here
            loop_time += int((time_stamp - loop_time)/intervel)*intervel

Một vài chi tiết cần lưu ý:

  • Khi có một giao dịch mới, trước tiên chúng ta phải phù hợp với lệnh, và sau đó đặt lệnh theo giá mới nhất.

  • Mỗi đơn đặt hàng có hai thuộc tính: nhà sản xuấtnếu là một nhà sản xuất, ưu tiêncân bằng ưu tiên, lấy một đơn đặt hàng mua như một ví dụ, khi giá mua nhỏ hơn Bán 1, nó được đánh dấu nhưmaker, và khi giá mua lớn hơn Buy 1, nó được đánh dấu nhưPriority matching, priorityxác định giá có bằng giá mua hay không, và người tạo xác định phí giao dịch.

  • CácmakerpriorityNếu một lượng lớn mua được đặt và vượt quá khả năng thị trường. Khi một giá lớn hơn giá mua, khối lượng còn lại sẽ làmaker.

  • Chiến lượcintervallà cần thiết, nó có thể đại diện cho sự chậm trễ của thị trường.

Kiểm tra lại chiến lược lưới điện

Cuối cùng, đó là giai đoạn backtest thực tế. Hãy backtest một trong những chiến lược lưới cổ điển nhất ở đây để xem liệu chúng ta có thể đạt được kết quả mong đợi. Nguyên tắc của chiến lược là mỗi khi giá tăng 1%, chúng ta giữ một lệnh ngắn của một giá trị nhất định (trái ngược lại, chúng ta giữ một lệnh dài), tính toán lệnh mua và bán trước. tôi sẽ không cho bạn thấy mã nguồn. chúng đều được đóng gói vàoGrid('XTZ', 100, 0.3, 1000, maker_fee=-0.00002, taker_fee=0.0003)chức năng, các thông số là: cặp giao dịch, giá lệch từ giá trị giữ 1%, mật độ lệnh đang chờ là 0,3%, Sleep intervalms, phí đặt hàng đang chờ và phí đặt hàng được thực hiện.

Giá thị trường của XTZ đã bị sốc trong 5 ngày qua, rất phù hợp với lưới điện.

img

Đầu tiên, chúng tôi kiểm tra hiệu quả của các vị trí nắm giữ khác nhau đối với lợi nhuận.

e1 = Grid('XTZ',100,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e1.account['USDT'])
e2 = Grid('XTZ',1000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e2.account['USDT'])
e3 = Grid('XTZ',10000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e3.account['USDT'])
e4 = Grid('XTZ',100000,0.3,1000,maker_fee=-0.00002,taker_fee=0.0003)
print(e4.account['USDT'])

Tổng cộng bốn nhóm đã được kiểm tra lại, giá trị các vị trí nắm giữ là 100, 1000, 10000, 100.000, và tổng thời gian kiểm tra lại là 1,3 giây.

{'realised_profit': 28.470993031132966, 'margin': 0.7982662957624465, 'unrealised_profit': 0.0104554474048441, 'total': 10000028.481448, 'leverage': 0.0, 'fee': -0.3430967859046398, 'maker_fee': -0.36980249726699727, 'taker_fee': 0.026705711362357405}
{'realised_profit': 275.63148945320177, 'margin': 14.346335829979132, 'unrealised_profit': 4.4382117331794045e-14, 'total': 10000275.631489, 'leverage': 0.0, 'fee': -3.3102045933457784, 'maker_fee': -3.5800688964477048, 'taker_fee': 0.2698643031019274}
{'realised_profit': 2693.8701498889504, 'margin': 67.70120400534114, 'unrealised_profit': 0.5735269329348516, 'total': 10002694.443677, 'leverage': 0.0001, 'fee': -33.984021415250744, 'maker_fee': -34.879233866850974, 'taker_fee': 0.8952124516001403}
{'realised_profit': 22610.231198585603, 'margin': 983.3853688758861, 'unrealised_profit': -20.529965947304365, 'total': 10022589.701233, 'leverage': 0.002, 'fee': -200.87094000385412, 'maker_fee': -261.5849078470078, 'taker_fee': 60.71396784315319}

Có thể thấy rằng lợi nhuận thực hiện cuối cùng lần lượt là 28,4%, 27,5%, 26,9% và 22,6% giá trị vị trí nắm giữ. Điều này cũng phù hợp với tình hình thực tế. Giá trị của vị trí nắm giữ càng lớn, giá trị của lệnh đang chờ càng lớn, khả năng giao dịch một phần sẽ xảy ra càng cao và lợi nhuận thực hiện cuối cùng càng nhỏ so với số tiền lệnh đang chờ. Bảng dưới đây là một so sánh lợi nhuận tương đối của giá trị vị trí lần lượt là 100 và 10000:

img

Chúng ta cũng có thể backtest tác động của các thông số khác nhau về doanh thu backtest, chẳng hạn như mật độ đơn đặt hàng đang chờ, thời gian ngủ, phí giao dịch, vv. Hãy lấy thời gian ngủ làm ví dụ, thay đổi nó thành 100ms, và so sánh thời gian ngủ với 1000ms để xem lợi nhuận trở lại.

{'realised_profit': 29.079440803790423, 'margin': 0.7982662957624695, 'unrealised_profit': 0.0104554474048441, 'total': 10000029.089896, 'leverage': 0.0, 'fee': -0.3703702128662524, 'maker_fee': -0.37938946377435134, 'taker_fee': 0.009019250908098965}

thu nhập đã tăng lên một chút, bởi vì chiến lược chỉ gửi một tập hợp các đơn đặt hàng, một số đơn đặt hàng sẽ không thể thực hiện giá dao động vì họ không có thời gian để thay đổi, và việc giảm thời gian ngủ cải thiện vấn đề này.

Tóm lại

Bài viết này sáng tạo đề xuất một hệ thống backtest mới dựa trên luồng lệnh, có thể mô phỏng một phần tình hình khớp của các lệnh đang chờ xử lý, thực hiện lệnh, lệnh được thực hiện một phần, chậm trễ, v.v., và một phần phản ánh tác động của số tiền chiến lược đối với doanh thu. Đối với các chiến lược tần suất cao và phòng ngừa rủi ro, Nó có giá trị tham chiếu quan trọng.


Có liên quan

Thêm nữa