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à
Đầ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 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á
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
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 Transaction
Dự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
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 Transaction
Bà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ườngMaker
và 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ề
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àbid
vàask
tương ứngprice
thấp hơnask
và 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ằngprice
trong thời gian tồn tại của lệnh sẽ được khớp với lệnh này (nếuprice
thấp hơn hoặc bằngbid
Cá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ộttaker
Sau đó, 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 Transaction
Sự khác biệt giữamaker
vàtaker
Đố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
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ữamaker
vàtaker
Sau đâ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ấtmaker
, và khi giá mua lớn hơn Priority matching
, priority
xác định giá có bằng giá mua hay không, và người tạo xác định phí giao dịch.
Cácmaker
vàpriority
Nế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ượcinterval
là cần thiết, nó có thể đại diện cho sự chậm trễ của thị trường.
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
Đầ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:
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.
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.