Số lượng các chiến lược mã nguồn mở trên TradingView là rất lớn. Thật đáng tiếc là rất nhiều chiến lược, ý tưởng và chỉ số xuất sắc không thể được sử dụng trong bot thực sự. Nhìn thấy điều này, FMZ, vốn cam kết phổ biến công nghệ giao dịch định lượng cho nhiều nhà giao dịch, tự nhiên không thể đàn áp sự thôi thúc này để giải quyết vấn đề!
Sự chia sẻ kinh nghiệm này là hoàn toàn được cung cấp!
Vì vậy, sau khi đi bộ qua thế giới lập trình và phát triển mã, đi qua 9 * 9 = 81 hố, sống sót vô số đêm không ngủ, và chồng chất lên một ngọn núi của lon Red Bull trống rỗng ở góc.
Khi nói đến ngôn ngữ Pine, tôi mới gần đây mới tự học. Nhưng thành thật mà nói, ngôn ngữ Pine cho giao dịch định lượng thực sự rất dễ sử dụng và dễ học. Tôi sẽ viết cho bạn một chiến lược lưới điện:
/*backtest
start: 2021-06-01 00:00:00
end: 2022-05-23 00:00:00
period: 1h
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
args: [["v_input_float_1",500],["v_input_string_1",2],["v_input_float_2",0.01],["v_input_int_1",20],["v_input_int_2",500],["RunMode",1,358374],["MinStock",0.001,358374]]
*/
strategy(overlay=true)
varip beginPrice = 0
var spacing = input.float(-1, title="Spacing prices")
var dir = input.string("long", title="Directions", options = ["long", "short", "both"])
var amount = input.float(-1, title="Order quantity")
var numbers = input.int(-1, title="Number of grids")
var profit = input.int(-1, title="Profit spreads") / syminfo.mintick
if spacing == -1 and amount == -1 and numbers == -1 and profit == -1
runtime.error("Parameter errors")
if not barstate.ishistory and beginPrice == 0
beginPrice := close
findTradeId(id) =>
ret = "notFound"
for i = 0 to strategy.opentrades - 1
if strategy.opentrades.entry_id(i) == id
ret := strategy.opentrades.entry_id(i)
ret
// Real-time K-line stage
if not barstate.ishistory
// Retrieve grid
for i = 1 to numbers
// Going long
direction = dir == "both" ? "long" : dir
plot(beginPrice-i*spacing, direction+str.tostring(i), color.green)
if direction == "long" and beginPrice-i*spacing > 0 and beginPrice-i*spacing < close and findTradeId(direction+str.tostring(i)) == "notFound"
strategy.order(direction+str.tostring(i), strategy.long, qty=amount, limit=beginPrice-i*spacing)
strategy.exit("exit-"+direction+str.tostring(i), direction+str.tostring(i), qty_percent=100, profit=profit)
// Going short
direction := dir == "both" ? "short" : dir
plot(beginPrice+i*spacing, direction+str.tostring(i), color.red)
if direction == "short" and beginPrice+i*spacing > close and findTradeId(direction+str.tostring(i)) == "notFound"
strategy.order(direction+str.tostring(i), strategy.short, qty=amount, limit=beginPrice+i*spacing)
strategy.exit("exit-"+direction+str.tostring(i), direction+str.tostring(i), qty_percent=100, profit=profit)
FMZ
Tất nhiên, chiến lược này là một chiến lược lưới, cũng có những khiếm khuyết, và nó không phải là một máy in tiền luôn chiến thắng. Chìa khóa phụ thuộc vào việc sử dụng và các thông số. Chúng tôi sẽ tập trung nhiều hơn vào cách viết chiến lược dễ dàng để thực hiện logic giao dịch của riêng mình, và kiếm tiền bằng cách viết chiến lược và tự giao dịch. Thật tuyệt vời khi không yêu cầu giúp đỡ!
Tôi sẽ giải thích cho tất cả các bạn, mã là đơn giản và dễ hiểu, với một dễ dàng như vậy để học và sử dụng ngôn ngữ Pine, nếu bạn vẫn không thể viết một chiến lược, sau đó tôi sẽ... nói với bạn chi tiết!
Nội dung kèm theo:/*backtest
và*/
ở đầu là mã cấu hình backtest của FMZ. Đây là chức năng của FMZ, không phải là nội dung của ngôn ngữ Pine. Tất nhiên, bạn có thể bỏ qua phần này, và bạn sẽ nhấp vào điều khiển tham số theo cách thủ công để đặt cấu hình và tham số backtest trong quá trình backtesting.
/*backtest
start: 2021-06-01 00:00:00
end: 2022-05-23 00:00:00
period: 1h
basePeriod: 1m
exchanges: [{"eid":"Bitfinex","currency":"BTC_USD"}]
args: [["v_input_float_1",500],["v_input_string_1",2],["v_input_float_2",0.01],["v_input_int_1",20],["v_input_int_2",500],["RunMode",1,358374],["MinStock",0.001,358374]]
*/
Mã tiếp theo:
strategy(overlay=true)
varip beginPrice = 0
var spacing = input.float(-1, title="Spacing prices")
var dir = input.string("long", title="Directions", options = ["long", "short", "both"])
var amount = input.float(-1, title="Order quantity")
var numbers = input.int(-1, title="Number of grids")
var profit = input.int(-1, title="Profit points") / syminfo.mintick
strategy(overlay=true)
: Nó được sử dụng để thiết lập một số tùy chọn của kịch bản, overlay=true, đó là để gán giá trị true cho tham sốoverlay
, để khi vẽ biểu đồ, nó được vẽ trên biểu đồ chính (K-line biểu đồ là biểu đồ chính, nó có thể được hiểu đơn giản như vậy).varip beginPrice = 0
: Một biến startPrice được khai báo bằng từ khóa varip với giá trị ban đầu là 0, được sử dụng như là giá ban đầu cho lưới.var spacing = input.float(-1, title="Spacing prices")
: Đặt một tham số chiến lược, tên tham số là var dir = input.string("long", title="Directions", options = ["long", "short", "both"])
: Thiết lập một tham số chiến lược có tên var amount = input.float(-1, title="Order quantity")
: Thiết lập một tham số để kiểm soát khối lượng giao dịch tại mỗi giao dịch điểm lưới.var numbers = input.int(-1, title="Number of grids")
: Số điểm lưới, thiết lập 20 là 20 điểm lưới theo một hướng.var profit = input.int(-1, title="Profit spreads") / syminfo.mintick
: Đặt một tham số để kiểm soát lợi nhuận của mỗi vị trí điểm lưới trước khi đóng vị trí.Tiếp theo, hãy nhìn vào mã:
if spacing == -1 and amount == -1 and numbers == -1 and profit == -1
runtime.error("Parameter errors")
Nó có nghĩa là nếu bất kỳ thông số như khoảng cách, số tiền, số lượng, và lợi nhuận không được thiết lập, mặc định là -1, và chiến lược sẽ dừng lại (bạn không thể hoạt động mù quáng mà không đặt các thông số)
Nhanh lên!
if not barstate.ishistory and beginPrice == 0
beginPrice := close
Điều này có nghĩa ở đây là khi chiến lược ở giai đoạn đường K thời gian thực và startPrice == 0, thay đổi giá trị của startPrice sang giá mới nhất hiện tại. Có thể hiểu rằng khi chiến lược được chạy chính thức, giá hiện tại ban đầu là giá ban đầu của lưới. Bởi vì kịch bản có giai đoạn BAR đường K lịch sử, chiến lược sẽ thực hiện logic một lần trong giai đoạn BAR lịch sử, và chắc chắn không có ý nghĩa để sắp xếp lưới trên BAR lịch sử.
Giai đoạn lịch sử của BAR là gì?
Để đưa ra một ví dụ đơn giản, tại thời điểm hiện tại A, chiến lược bắt đầu chạy, và chiến lược có được dữ liệu với 100 K-line BAR. Theo thời gian, 100 BAR sẽ trở thành 101, 102...N. Khi bắt đầu chạy từ thời điểm A, BAR thứ 101 là giai đoạn K-line thời gian thực, và thời gian này là dữ liệu thời gian thực mới nhất. Sau đó từ BAR thứ 1 đến BAR thứ 100, đây là giá thị trường lịch sử đã qua, nhưng chiến lược cũng sẽ chạy trên các giá thị trường lịch sử này, vì vậy giai đoạn này là giai đoạn K-line lịch sử.
barstate.ishistory
đây là một biến tích hợp trong ngôn ngữ Pine,barstate.ishistory
là true nếu BAR hiện tại là một BAR lịch sử, và sai nếu nó không phải là một BAR lịch sử.
Tiếp theo, một hàm được tạo ra
findTradeId(id) =>
ret = "notFound"
for i = 0 to strategy.opentrades - 1
if strategy.opentrades.entry_id(i) == id
ret := strategy.opentrades.entry_id(i)
ret
Vai trò của chức năng này là tìm hiểu xem một id nhất định có tồn tại trong tất cả các lệnh hiện đã mở một vị trí hay không. Nếu có một cuộc gọi chức năng findTradeId, nó sẽ trả về ID của lệnh hiện có (lưu ý rằng ID này không phải là ID lệnh của sàn giao dịch, đó là tên được đặt cho lệnh bởi chiến lược hoặc được hiểu là một nhãn), nếu nó không tồn tại, chuỗi
Bước tiếp theo là khởi động bảng lưới:
// Real-time K-line stage
if not barstate.ishistory
// Retrieve grid
for i = 1 to numbers
// Going long
direction = dir == "both" ? "long" : dir
plot(beginPrice-i*spacing, direction+str.tostring(i), color.green)
if direction == "long" and beginPrice-i*spacing > 0 and beginPrice-i*spacing < close and findTradeId(direction+str.tostring(i)) == "notFound"
strategy.order(direction+str.tostring(i), strategy.long, qty=amount, limit=beginPrice-i*spacing)
strategy.exit("exit-"+direction+str.tostring(i), direction+str.tostring(i), qty_percent=100, profit=profit)
// Going short
direction := dir == "both" ? "short" : dir
plot(beginPrice+i*spacing, direction+str.tostring(i), color.red)
if direction == "short" and beginPrice+i*spacing > close and findTradeId(direction+str.tostring(i)) == "notFound"
strategy.order(direction+str.tostring(i), strategy.short, qty=amount, limit=beginPrice+i*spacing)
strategy.exit("exit-"+direction+str.tostring(i), direction+str.tostring(i), qty_percent=100, profit=profit)
Chuỗi for được sử dụng, và số vòng được xác định theo giá trị của tham số số, tức là số lượng lệnh tương ứng được sắp xếp. Đặt hướng theo tham số dir. Sử dụng hàm findTradeId để tìm hiểu xem lệnh của nhãn ở vị trí lưới hiện tại đã được mở hay không, và chỉ đặt lệnh dự kiến nếu không có vị trí mở (nếu vị trí được mở, nó không thể được lặp lại). Để đặt lệnh, sử dụng hàm strategy.order để chỉ định tham số giới hạn như một lệnh dự kiến. Đặt lệnh đóng tương ứng trong khi đặt lệnh dự kiến.strategy.exitchức năng, xác định tham số lợi nhuận và xác định điểm lợi nhuận.
Nhìn vào đường cong lợi nhuận, chúng ta có thể thấy rằng lưới điện cũng có rủi ro. Nó không phải là một chiến thắng đảm bảo. Nó chỉ là rủi ro mở rộng lưới điện trên quy mô lớn là một chút nhỏ hơn.
Nếu anh không biết cách viết chiến lược bằng ngôn ngữ Pine dễ học và dễ sử dụng, thì tôi...