Многим трейдерам отлично известна тактика Мартингейла – увеличение объема сделки после каждого полученного убытка. Эта тактика является гарантированно выигрышной, но как всегда имеет одно «но». Для полной гарантии получения прибыли нужно располагать бесконечно большими средствами. В этом и заключается основное противоречие. Зачем пытаться увеличивать капитал, если его размер и без того бесконечен?
Тем не менее, в локальных масштабах применение тактики Мартингейла имеет право на жизнь. Например, такая модификация тактики как добавление к убыточной позиции. В данном случае упор делается на улучшение (уменьшение для длинных сделок и увеличение для коротких сделок) средней цены открытия совокупной сделки. Как следствие, для получения планируемой общей прибыли цене уже не нужно возвращаться к уровню профита первой позиции. Соответствующий новый уровень профита может быть расположен гораздо ближе к текущей цене, чем повышается вероятность его достижения.
Основной головной болью трейдера в применении такой тактики торговли является определение средней цены открытия нескольких однонаправленных сделок, равно как и общий уровень профита, который должен соответствовать значению той прибыли, которую планировалось получить еще при открытии самой первой позиции.
Помощником трейдера в этом случае может выступить обычный скрипт, который будет рассчитывать совокупный уровень профита так, будто прибыль была достигнута всего лишь одной сделкой.
Алгоритм работы скрипта следует из главной его функции — start:
int start()
{
// — 1 — =========== Получение информации о торговых условиях ===========
Tick = MarketInfo(Symbol(), MODE_TICKSIZE); // минимальный тик
TickValue = MarketInfo(Symbol(), MODE_TICKVALUE); // стоимость тика в алюте депозита
Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд
StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // текущий уровень стопов
// — 1 — ================ Окончание блока ========================
// — 2 — ================= Проверка правильности TakeProfit ============
if (TakeProfit <= (StopLevel-Spread)/Point)
{
Alert(«TakeProfit должен быть больше «, (StopLevel-Spread)/Point, » пунктов.»);
return(0);
}
if (Lots < MarketInfo(Symbol(), MODE_MINLOT))
{
Alert(«Указанное значение объема слишком мало!»);
return(0);
}
// — 2 — ==================== Окончание блока ====================
// — 3 — ================ Постоянный мониторинг позиций ==============
while (!IsStopped())
{
GetAveragePrices(); // Расчет средней цены открытия и общего профита
if (BuyProfit > 0 || SellProfit > 0) // Если есть позиции по текущему инструменту
{
TickValue = MarketInfo(Symbol(), MODE_TICKVALUE); // цена тика в валюте депозита
Spread = ND(MarketInfo(Symbol(), MODE_SPREAD)*Point); // текущий спрэд
StopLevel = ND(MarketInfo(Symbol(), MODE_STOPLEVEL)*Point); // уровень стопов
SetCommonProfit(); // установка уровней профита на нужный уровень
}
}
// — 3 — ============= Окончание блока ===========================
return(0);
}
Первый блок просто собирает необходимую информацию – значение спрэда и минимального уровня стопов. Еще одним значением, которое потребуется при работе скрипта, является стоимость одного минимального изменения цены в валюте депозита. Иначе говоря, это стоимость одного тика на один полный лот.
Второй блок занимается проверкой правильности ввода пользователем двух входных параметров скрипта – TakeProfit и Lots. Первый параметр задает количество пунктов планируемой прибыли, а второй – объем сделки, которым планируется получить эту прибыль.
Третий блок содержит «бесконечный цикл», который все же может быть закончен пользователем при отсоединении скрипта от графика. В теле цикла рассчитывается совокупный уровень профита для длинных сделок (BuyProfit) и для коротких (SellProfit). Это делает функция GetAveragePrices. Следующим шагом является установка рассчитанного уровня профита для всех найденных позиций, чем занимается функция SetCommonProfit.
Функция GetAveragePrices работает таким образом:
void GetAveragePrices()
{
// — 1 — ======= Нахождение всех сделок по текущему инструменту ===========
BuyProfit = 0; SellProfit = 0;
double BuyAveragePrice = 0, SellAveragePrice = 0;
double BuyLots = 0, SellLots = 0;
for (int i = 0; i < OrdersTotal(); i++)
if (OrderSelect(i, SELECT_BY_POS))
if (OrderType() < 2) // Учитываются только позиции
if (OrderSymbol() == Symbol()) // и только текущего инструмента
if (OrderType() == OP_BUY)
{
BuyAveragePrice += OrderOpenPrice()*OrderLots(); // Средняя цена открытия
BuyLots += OrderLots(); // и общий объем для длинных позиций
}
else
{
SellAveragePrice += OrderOpenPrice()*OrderLots(); // Средняя цена открытия
SellLots += OrderLots(); // и общий объем для коротких позиций
}
// — 1 — ============= Окончание блока ===========================
// — 2 — ====== Расчет общей цены закрытия сделок с учетом целевого профита ====
// сколько прибыли требуется в валюте депозита
double TargetProfit = TickValue*Lots*TakeProfit;
if (BuyLots != 0)
BuyProfit = BuyAveragePrice/BuyLots + Tick*(TargetProfit/(TickValue*BuyLots));
if (SellLots != 0)
SellProfit = SellAveragePrice/SellLots — Tick*(TargetProfit/(TickValue*SellLots));
// — 2 — ================ Окончание блока ========================
}
Далее рассчитывается уровень профита для длинных сделок BuyProfit. Для этого к средней цене открытия (отношение BuyAveragePrice к BuyLots) прибавляется выражение Tick*(TargetProfit/(TickValue*BuyLots)), которое разберем по частям. Итак, TickValue*BuyLots – это изменение средств (Equity) в валюте депозита с каждым тиком. Поэтому, если на него разделить планируемую прибыль (TargetProfit), то получим количество пунктов, которое нужно прибавить к средней цене открытия.
Точно также рассчитывается уровень профита для коротких позиций SellProfit.
Последняя рассматриваемая функция SetCommonProfit:
void SetCommonProfit()
{
// — 1 — ========= Нахождение позиций по текущему инструменту ===============
for (int i = 0; i < OrdersTotal(); i++)
if (OrderSelect(i, SELECT_BY_POS))
if (OrderType() < 2) // Учитываются только позиции
if (OrderSymbol() == Symbol()) // и только текущего инструмента
// — 1 — ==================== Окончание блока ========================
{
double TP = 0; // Если 0, то не требуется изменение
// — 2 — ============== Проверка длинных позиций =======================
if (OrderType() == OP_BUY && BuyProfit > 0) // Если уровень профита рассчитан,
if (MathAbs(OrderTakeProfit() — BuyProfit) >= Tick && //отличается от текущего
BuyProfit — Bid > StopLevel) // и расстояние для изменения достаточное
TP = NP(BuyProfit);
// — 2 — =============== Окончание блока =============================
// — 3 — ============ Проверка коротких позиций =========================
if (OrderType() == OP_SELL && SellProfit > 0)// Если уровень профита рассчитан,
if (MathAbs(OrderTakeProfit() — SellProfit) >= Tick&& //отличается от текущего
Ask — SellProfit > StopLevel) // и расстояние для изменения достаточное
TP = NP(SellProfit);
// — 3 — ===================== Окончание блока =======================
// — 4 — ============== Изменение уровня TakeProfit позиции =================
if (TP > 0) // изменение требуется, если новый уровень не равен нулю
if (WaitForTradeContext())
if (!OrderModify(OrderTicket(), 0, OrderStopLoss(), TP, 0))
return;
// — 4 — ===================== Окончание блока =======================
}
}
Первый блок просто находит сделки по текущему инструменту.
Второй блок проверяет соответствие уровня профита найденной длинной сделки рассчитанному общему уровню BuyProfit. Если текущий уровень профита не равен рассчитанному и при этом текущее значение Bid достаточно далеко от BuyProfit, то будет произведено изменение уровня Take Profit сделки.
Третий блок является копией второго, с той лишь разницей, что работает с короткими позициями.
Четвертый блок занимается непосредственно модификацией уровня профита, которая происходит, если значение переменной TP не равно нулю.
Скрипт может быть использован как с одной, так и сразу с несколькими валютными парами одновременно. Его задачей является слежение за уровнем профита всех открываемых позиций. Поэтому при открытии очередной сделки уровень профита можно попросту не указывать.
В настройках скрипта достаточно указать количество пунктов прибыли, которое планируется получить (TakeProfit) и объем первой сделки (Lots), исходя из которого будет рассчитываться совокупная прибыль. Стоит учитывать, что результирующая прибыль будет немного отличаться от планируемой, правда, в лучшую сторону. Это происходит из-за невозможности указания дробного количества пунктов при установке целей каждой сделки.
После запуска скрипта трейдер должен вручную открывать сделки, указывая лишь уровень стоп-приказа. С открытием каждой новой сделки уровень профита каждой позиции автоматически будет изменяться скриптом.
Полный исходный код скрипта можно найти здесь.