Tính toán ký quỹ cho một lệnh tương lai: OrderCalcMargin
Trước khi gửi yêu cầu giao dịch đến máy chủ, một chương trình MQL có thể tính toán ký quỹ cần thiết cho một giao dịch đã lên kế hoạch bằng hàm OrderCalcMargin
. Nên luôn thực hiện điều này để tránh tải quá mức ký quỹ.
bool OrderCalcMargin(ENUM_ORDER_TYPE action, const string symbol,
double volume, double price, double &margin)
2
Hàm tính toán ký quỹ cần thiết cho loại lệnh action
và công cụ tài chính symbol
với volume
lô. Điều này phù hợp với cài đặt của tài khoản hiện tại nhưng không xem xét các lệnh chờ và vị thế mở hiện có. Liệt kê ENUM_ORDER_TYPE
đã được giới thiệu trong phần Các loại lệnh.
Giá trị ký quỹ (bằng đơn vị tiền tệ của tài khoản) được ghi vào tham số margin
được truyền qua tham chiếu.
Cần nhấn mạnh rằng đây là ước tính ký quỹ cho một vị thế hoặc lệnh mới duy nhất, không phải giá trị tổng của tài sản thế chấp, mà nó sẽ trở thành sau khi thực hiện. Hơn nữa, việc đánh giá được thực hiện như thể không có các lệnh chờ và vị thế mở khác trên tài khoản hiện tại. Trong thực tế, giá trị ký quỹ phụ thuộc vào nhiều yếu tố, bao gồm các lệnh và vị thế khác, và có thể thay đổi khi môi trường thị trường (như đòn bẩy) thay đổi.
Hàm trả về chỉ báo thành công (true
) hoặc lỗi (false
). Mã lỗi có thể được lấy theo cách thông thường từ biến _LastError
.
Hàm
OrderCalcMargin
chỉ có thể được sử dụng trong Expert Advisors và scripts. Để tính toán ký quỹ trong indicators, bạn cần triển khai một phương pháp thay thế, ví dụ, khởi chạy một Expert Advisor phụ trợ trong một đối tượng biểu đồ, truyền tham số cho nó và lấy kết quả qua cơ chế sự kiện, hoặc tự mô tả các phép tính trong MQL5 bằng công thức theo loại công cụ. Trong phần tiếp theo, chúng ta sẽ đưa ra một ví dụ về việc triển khai như vậy, cùng với ước tính lợi nhuận/thua lỗ tiềm năng.
Chúng ta có thể viết một script đơn giản gọi OrderCalcMargin
cho các ký hiệu từ Market Watch
, và so sánh giá trị ký quỹ của chúng. Thay vào đó, hãy làm phức tạp nhiệm vụ một chút và xem xét tệp tiêu đề LotMarginExposure.mqh
, cho phép đánh giá tải ký quỹ và mức ký quỹ sau khi mở một vị thế với mức rủi ro được xác định trước. Một lát sau, chúng ta sẽ thảo luận về hàm OrderCheck
có khả năng cung cấp thông tin tương tự. Tuy nhiên, thuật toán của chúng ta sẽ bổ sung khả năng giải bài toán ngược về việc chọn kích thước lô theo mức tải hoặc rủi ro đã cho.
Việc sử dụng các tính năng mới được thể hiện trong một Expert Advisor không giao dịch LotMarginExposureTable.mq5
.
Về lý thuyết, việc một chương trình MQL được triển khai dưới dạng Expert Advisor không có nghĩa là các hoạt động giao dịch phải được thực hiện trong đó. Rất thường xuyên, như trong trường hợp của chúng ta, các tiện ích khác nhau được tạo ra dưới dạng Expert Advisor. Ưu điểm của chúng so với scripts là chúng tồn tại trên biểu đồ và có thể thực hiện chức năng của mình vô thời hạn để phản hồi các sự kiện nhất định.
Trong Expert Advisor mới, chúng ta sử dụng kỹ năng tạo giao diện đồ họa tương tác bằng đối tượng. Nói một cách đơn giản hơn, với danh sách các ký hiệu đã cho, Expert Advisor sẽ hiển thị một bảng với nhiều cột chỉ báo ký quỹ trên biểu đồ, và bảng này có thể được sắp xếp theo từng cột. Chúng ta sẽ cung cấp danh sách các cột sau một chút.
Vì việc phân tích lô, ký quỹ và tải ký quỹ là một nhiệm vụ phổ biến, chúng ta sẽ tách biệt việc triển khai vào một tệp tiêu đề riêng LotMarginExposure.mqh
.
Tất cả các hàm trong tệp được nhóm trong một không gian tên để tránh xung đột và vì sự rõ ràng (việc chỉ định ngữ cảnh trước khi gọi một hàm nội bộ cung cấp thông tin về nguồn gốc và vị trí của hàm này).
namespace LEMLR
{
...
};
2
3
4
Viết tắt LEMLR
có nghĩa là "Lot, Exposure, Margin Level, Risk".
Các phép tính chính được thực hiện trong hàm Estimate
. Xem xét nguyên mẫu của hàm tích hợp OrderCalcMargin
, trong các tham số của hàm Estimate
, chúng ta cần truyền tên ký hiệu, loại lệnh, khối lượng và giá. Nhưng đó không phải là tất cả những gì chúng ta cần.
bool Estimate(const ENUM_ORDER_TYPE type, const string symbol, const double lot,
const double price,...)
2
Chúng ta dự định đánh giá một số chỉ báo của một hoạt động giao dịch, chúng liên kết với nhau và có thể được tính toán theo các hướng khác nhau, tùy thuộc vào dữ liệu đầu vào mà người dùng nhập và những gì họ muốn tính toán. Ví dụ, sử dụng các tham số trên, dễ dàng tìm ra mức ký quỹ mới và tải tài khoản. Công thức của chúng hoàn toàn ngược nhau:
Ml = money / margin * 100
Ex = margin / money * 100
2
Ở đây biến margin
biểu thị số tiền ký quỹ, mà chỉ cần gọi OrderCalcMargin
là đủ.
Tuy nhiên, các nhà giao dịch thường thích bắt đầu từ mức tải hoặc mức ký quỹ được xác định trước và tính toán khối lượng cho điều đó. Hơn nữa, còn có cách tiếp cận tính toán lô dựa trên rủi ro phổ biến không kém. Rủi ro được hiểu là số tiền lỗ tiềm năng từ giao dịch trong trường hợp giá di chuyển bất lợi, dẫn đến việc nội dung của một biến khác từ các công thức trên sẽ giảm, tức là money
.
Để tính toán lỗ, điều quan trọng là phải biết độ biến động của công cụ tài chính trong suốt thời gian giao dịch (thời gian của chiến lược) hoặc khoảng cách của stop loss mà người dùng giả định.
Do đó, danh sách tham số của hàm Estimate
được mở rộng.
bool Estimate(const ENUM_ORDER_TYPE type, const string symbol, const double lot,
const double price,
const double exposure, const double riskLevel, const int riskPoints,
const ENUM_TIMEFRAMES riskPeriod, double money,...)
2
3
4
Trong tham số exposure
, chúng ta chỉ định mức tải ký quỹ mong muốn dưới dạng phần trăm, và trong tham số riskLevel
, chúng ta chỉ định phần ký quỹ (cũng theo phần trăm) mà chúng ta sẵn sàng mạo hiểm. Để tính toán dựa trên rủi ro, bạn có thể truyền kích thước stop loss bằng điểm trong tham số riskPoints
. Khi nó bằng 0, tham số riskPeriod
sẽ phát huy tác dụng: nó chỉ định khoảng thời gian mà thuật toán sẽ tự động tính toán phạm vi báo giá ký hiệu bằng điểm. Cuối cùng, trong tham số money
, chúng ta có thể chỉ định một số tiền ký quỹ tự do bất kỳ để đánh giá lô. Một số nhà giao dịch chia ký quỹ theo điều kiện giữa nhiều robot. Khi money
là 0, hàm sẽ điền biến này với thuộc tính AccountInfoDouble(ACCOUNT_MARGIN_FREE)
.
Bây giờ chúng ta cần quyết định cách trả về kết quả của hàm. Vì nó có khả năng đánh giá nhiều chỉ báo giao dịch và một số tùy chọn khối lượng, việc định nghĩa cấu trúc SymbolLotExposureRisk
là hợp lý.
struct SymbolLotExposureRisk
{
double lot; // khối lượng yêu cầu (hoặc tối thiểu)
int atrPointsNormalized; // phạm vi giá chuẩn hóa theo kích thước tick
double atrValue; // phạm vi dưới dạng số tiền lợi nhuận/lỗ cho 1 lô
double lotFromExposureRaw; // chưa chuẩn hóa (có thể nhỏ hơn lô tối thiểu)
double lotFromExposure; // lô chuẩn hóa từ tải ký quỹ
double lotFromRiskOfStopLossRaw; // chưa chuẩn hóa (có thể nhỏ hơn lô tối thiểu)
double lotFromRiskOfStopLoss; // lô chuẩn hóa từ rủi ro
double exposureFromLot; // tải dựa trên khối lượng của 'lot'
double marginLevelFromLot; // mức ký quỹ từ khối lượng 'lot'
int lotDigits; // số chữ số trong lô chuẩn hóa
};
2
3
4
5
6
7
8
9
10
11
12
13
Trường lot
trong cấu trúc chứa lô được truyền vào hàm Exposure
nếu lô không bằng 0. Nếu lô truyền vào là 0, thuộc tính ký hiệu SYMBOL_VOLUME_MIN
được thay thế.
Hai trường được phân bổ cho các giá trị khối lượng tính toán dựa trên tải ký quỹ và rủi ro: với hậu tố Raw
(lotFromExposureRaw
, lotFromRiskOfStopLossRaw
), và không có hậu tố (lotFromExposure
, lotFromRiskOfStopLoss
). Các trường Raw
chứa kết quả "thuần túy số học", có thể không khớp với thông số ký hiệu. Trong các trường không có hậu tố, lô được chuẩn hóa dựa trên tối thiểu, tối đa và bước. Sự trùng lặp này hữu ích, đặc biệt cho những trường hợp tính toán cho ra giá trị nhỏ hơn lô tối thiểu (ví dụ, lotFromExposureRaw
bằng 0.023721 với tối thiểu 0.1, do đó lotFromExposure
giảm xuống 0): sau đó từ nội dung của các trường Raw
, bạn có thể đánh giá cần thêm bao nhiêu tiền hoặc tăng rủi ro bao nhiêu để đạt được lô tối thiểu.
Hãy mô tả tham số đầu ra cuối cùng của hàm Estimate
dưới dạng tham chiếu đến cấu trúc này. Chúng ta sẽ dần điền vào tất cả các trường trong thân hàm. Trước tiên, chúng ta lấy ký quỹ cho một lô bằng cách gọi OrderCalcMargin
và lưu nó vào biến cục bộ lot1margin
.
bool Estimate(const ENUM_ORDER_TYPE type, const string symbol, const double lot,
const double price, const double exposure,
const double riskLevel, const int riskPoints, const ENUM_TIMEFRAMES riskPeriod,
double money, SymbolLotExposureRisk &r)
{
double lot1margin;
if(!OrderCalcMargin(type, symbol, 1.0,
price == 0 ? GetCurrentPrice(symbol, type) : price,
lot1margin))
{
Print("OrderCalcMargin ", symbol, " failed: ", _LastError);
return false;
}
if(lot1margin == 0)
{
Print("Margin ", symbol, " is zero, ", _LastError);
return false;
}
...
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
Nếu giá vào không được chỉ định, tức là price
bằng 0, hàm trợ giúp GetCurrentPrice
trả về một giá phù hợp dựa trên loại lệnh: đối với lệnh mua, thuộc tính ký hiệu SYMBOL_ASK
sẽ được lấy, và đối với lệnh bán sẽ là SYMBOL_BID
. Hàm này và các hàm trợ giúp khác được bỏ qua ở đây, nội dung của chúng có thể được tìm thấy trong mã nguồn đính kèm.
Nếu tính toán ký quỹ thất bại, hoặc nhận được giá trị 0, hàm Estimate
sẽ trả về false
.
Hãy nhớ rằng ký quỹ bằng 0 có thể là bình thường, nhưng cũng có thể là lỗi, tùy thuộc vào công cụ và loại lệnh. Vì vậy, đối với các mã giao dịch sàn, các lệnh chờ phải chịu ký quỹ, nhưng không phải đối với các mã OTC (tức là ký quỹ 0 là đúng). Điểm này nên được xem xét trong mã gọi: nó chỉ nên yêu cầu ký quỹ cho những kết hợp ký hiệu và loại hoạt động mà điều đó có ý nghĩa và được假 định là không bằng 0.
Có ký quỹ cho một lô, chúng ta có thể tính toán số lô để đảm bảo mức tải ký quỹ đã cho.
double usedMargin = 0;
if(money == 0)
{
money = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
usedMargin = AccountInfoDouble(ACCOUNT_MARGIN);
}
r.lotFromExposureRaw = money * exposure / 100.0 / lot1margin;
r.lotFromExposure = NormalizeLot(symbol, r.lotFromExposureRaw);
...
2
3
4
5
6
7
8
9
10
Hàm trợ giúp NormalizeLot
được hiển thị dưới đây.
Để có được lô phụ thuộc vào rủi ro và biến động, cần thêm một chút tính toán.
// (Phần bị cắt bớt trong tài liệu gốc)
Các phần tử của liệt kê đặc biệt LME_FIELDS
được sử dụng làm chỉ số mảng, đồng thời cung cấp tên và số cho các chỉ báo từ cấu trúc.
enum LME_FIELDS // 10 trường + 3 thuộc tính ký hiệu bổ sung
{
eLot,
eAtrPointsNormalized,
eAtrValue,
eLotFromExposureRaw,
eLotFromExposure,
eLotFromRiskOfStopLossRaw,
eLotFromRiskOfStopLoss,
eExposureFromLot,
eMarginLevelFromLot,
eLotDig,
eMinLot,
eContract,
eSymbol
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Các thuộc tính SYMBOL_VOLUME_MIN
và SYMBOL_TRADE_CONTRACT_SIZE
được thêm vào để tham khảo. Tên ký hiệu được "đóng gói" thành giá trị gần đúng kiểu double
bằng hàm pack2double
, nhằm thực hiện việc sắp xếp thống nhất theo bất kỳ trường nào, bao gồm cả tên.
double pack2double(const string s)
{
double r = 0;
for(int i = 0; i < StringLen(s); i++)
{
r = (r * 255) + (StringGetCharacter(s, i) % 255);
}
return r;
}
2
3
4
5
6
7
8
9
Tại giai đoạn này, chúng ta đã có thể chạy Expert Advisor và in kết quả vào nhật ký, đại loại như sau.
ArrayPrint(LME);
Nhưng việc xem nhật ký mọi lúc không tiện. Hơn nữa, định dạng thống nhất của các giá trị từ các cột khác nhau, và càng không thể nói đến việc trình bày các hàng "đóng gói" trong double
, không thể gọi là thân thiện với người dùng. Do đó, lớp bảng điểm (Tableau.mqh
) đã được phát triển để hiển thị một bảng tùy ý trên biểu đồ. Ngoài việc chúng ta có thể tự kiểm soát định dạng của từng trường khi chuẩn bị bảng (trong tương lai, làm nổi bật nó bằng màu khác), lớp này cho phép sắp xếp bảng tương tác theo bất kỳ cột nào: nhấp chuột đầu tiên sắp xếp theo một hướng, nhấp thứ hai sắp xếp theo hướng ngược lại, và nhấp thứ ba hủy sắp xếp.
Ở đây chúng ta sẽ không mô tả chi tiết lớp này nhưng bạn có thể nghiên cứu mã nguồn của nó. Điều quan trọng cần lưu ý là giao diện dựa trên đối tượng đồ họa. Thực tế, các ô bảng được hình thành bởi các đối tượng loại OBJ_LABEL
, và tất cả các thuộc tính của chúng đã quen thuộc với người đọc. Tuy nhiên, một số kỹ thuật được sử dụng trong mã nguồn của bảng điểm, đặc biệt là làm việc với tài nguyên đồ họa và đo lường văn bản hiển thị, sẽ được trình bày sau, trong phần thứ bảy.
Hàm khởi tạo của lớp tableau
nhận một số tham số:
prefix
— tiền tố cho tên của các đối tượng đồ họa được tạorows
— số hàngcols
— số cộtheight
— chiều cao dòng tính bằng pixel (-1 nghĩa là gấp đôi kích thước phông chữ)width
— chiều rộng ô tính bằng pixelc
— góc của biểu đồ để neo các đối tượngg
— khoảng cách tính bằng pixel giữa các ôf
— kích thước phông chữfont
— tên phông chữ cho các ô thông thườngbold
— tên phông chữ đậm cho tiêu đềbgc
— màu nềnbgt
— độ trong suốt nền
class Tableau
{
public:
Tableau(const string prefix, const int rows, const int cols,
const int height = 16, const int width = 100,
const ENUM_BASE_CORNER c = CORNER_RIGHT_LOWER, const int g = 8,
const int f = 8, const string font = "Consolas", const string bold = "Arial Black",
const int mask = TBL_FLAG_COL_0_HEADER,
const color bgc = 0x808080, const uchar bgt = 0xC0)
...
};
2
3
4
5
6
7
8
9
10
11
Hầu hết các tham số này có thể được người dùng thiết lập trong các biến đầu vào của Expert Advisor LotMarginExposureTable.mq5
.
input ENUM_BASE_CORNER Corner = CORNER_RIGHT_LOWER;
input int Gap = 16;
input int FontSize = 8;
input string DefaultFontName = "Consolas";
input string TitleFontName = "Arial Black";
input string MotoTypeFontsHint = "Consolas/Courier/Courier New/Lucida Console/Lucida Sans Typewriter";
input color BackgroundColor = 0x808080;
input uchar BackgroundTransparency = 0xC0; // BackgroundTransparency (255 - opaque, 0 - glassy)
2
3
4
5
6
7
8
Số cột trong bảng được xác định trước, số hàng bằng số ký hiệu, cộng với dòng trên cùng với tiêu đề.
Điều quan trọng cần lưu ý là phông chữ cho bảng nên được chọn không có chữ cái tỷ lệ, vì vậy trong biến MotoTypeFontsHint
cung cấp một gợi ý với tập hợp các phông chữ đơn cách tiêu chuẩn của Windows.
Các đối tượng đồ họa được tạo được điền bằng phương thức fill
của lớp Tableau
.
bool fill(const string &data[], const string &hint[]) const;
Expert Advisor của chúng ta truyền mảng chuỗi data
thu được từ mảng LME
qua một loạt chuyển đổi bằng StringFormat
, cũng như mảng hint
với gợi ý cho tiêu đề.
Hình ảnh sau cho thấy một phần của biểu đồ với Expert Advisor đang chạy với cài đặt mặc định nhưng với danh sách ký hiệu được chỉ định "EURUSD,USDRUB,USDCNH,XAUUSD,XPDUSD".
Mức tải ký quỹ và ký quỹ với lô tối thiểu cho mỗi ký hiệu
Tên ký hiệu được hiển thị trong cột bên trái. Là tiêu đề của cột đầu tiên, số tiền quỹ được hiển thị (trong trường hợp này, là số tiền tự do trên tài khoản tại thời điểm hiện tại, vì trong tham số đầu vào Money
được để là 0). Khi di chuột qua tên cột, bạn có thể thấy gợi ý với giải thích.
Trong các cột tiếp theo:
L(E)
— lô được tính toán cho mức tải E của ký quỹ 5% sau giao dịchL(R)
— lô được tính toán tại rủi ro R cho 5% ký quỹ sau giao dịch không thành công (phạm vi bằng điểm và số tiền rủi ro — trong cột cuối cùng)E%
— tải ký quỹ sau khi vào với lô tối thiểuM%
— mức ký quỹ sau khi vào với lô tối thiểuMinL
— lô tối thiểu cho mỗi ký hiệuContract
— kích thước hợp đồng (1 lô) cho mỗi ký hiệuRisk
— lợi nhuận/lỗ bằng tiền khi giao dịch 1 lô và cùng phạm vi bằng điểm
Trong các cột E%
và M%
, trong trường hợp này, các lô tối thiểu được sử dụng, vì tham số đầu vào Lot
là 0 (mặc định).
Khi tải 5% ký quỹ, giao dịch có thể thực hiện cho tất cả các ký hiệu đã chọn ngoại trừ "XPDUSD". Đối với ký hiệu cuối cùng, khối lượng là 0.03272, nhỏ hơn lô tối thiểu 0.1, và do đó kết quả được đặt trong ngoặc. Nếu chúng ta cho phép tải 20% (nhập 20 vào tham số Exposure
), chúng ta sẽ nhận được lô tối thiểu cho "XPDUSD" là 0.1.
Nếu chúng ta nhập giá trị 1 vào tham số Lot
, chúng ta sẽ thấy các giá trị cập nhật trong các cột E%
và M%
trong bảng (tải sẽ tăng, và mức ký quỹ sẽ giảm).
Mức tải ký quỹ và ký quỹ cho một lô duy nhất cho mỗi ký hiệu
Ảnh chụp màn hình cuối cùng minh họa hoạt động của Expert Advisor cho thấy một tập hợp lớn các cổ phiếu blue-chip của sàn giao dịch Nga MOEX được sắp xếp theo khối lượng tính toán cho mức tải ký quỹ 5% (cột thứ hai). Trong số các cài đặt không chuẩn, có thể lưu ý rằng Lot=10
, và khoảng thời gian để tính toán phạm vi giá và rủi ro là MN1
. Nền được làm trong suốt màu trắng, neo vào góc trên bên trái của biểu đồ.
Lots, tải ký quỹ và mức ký quỹ cho các công cụ MOEX