Hedging with Zone Recovery Area
Сообщите нам, если считаете, что материал размещён без разрешения.
Сообщить о материале
Обращение по товару «hedging_with_zone_recovery_area.lua» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
О программе
Советник «Hedging with Zone Recovery Area» — это торговый советник для платформы MetaTrader 4, реализующий стратегию хеджирования с зоной восстановления. Он отслеживает события сделок и при закрытии позиции рассчитывает суммарный профит/убыток по существующим ордерам в зоне, после чего открывает новый ордер в противоположном направлении с скорректированным объёмом.
Стратегия использует настраиваемые параметры: зона восстановления (area) и тейк-профит (tp). Поддерживается работа с несколькими торговыми счетами, а также возможность разрешения или запрета торговли. Множитель объёма (amount_multiplicator) позволяет увеличивать размер последующих ордеров при каждой итерации восстановления.
Внутри используется пользовательский торговый модуль trading для управления ордерами: создание входных ордеров, закрытие позиций, поиск открытых ордеров и сделок. Стратегия зарегистрирована в системе модулей платформы и работает через обработчик асинхронных операций (ExtAsyncOperationFinished), реагируя на обновления торговых событий.
Поиск по коду файла
local Modules = {};
function Init()
strategy:name("Hedging with Zone Recovery Area");
strategy:description("");
strategy:setTag("group", "Samples");
strategy:type(core.Both);
strategy.parameters:addInteger("area", "Zone recovery area", "", 35);
strategy.parameters:addInteger("tp", "Take profit", "", 80);
strategy.parameters:addBoolean("allow_trade", "Allow strategy to trade", "", true);
strategy.parameters:setFlag("allow_trade", core.FLAG_ALLOW_TRADE);
strategy.parameters:addString("account", "Account to trade on", "", "");
strategy.parameters:setFlag("account", core.FLAG_ACCOUNT);
strategy.parameters:addDouble("amount_multiplicator", "Amount multiplicator", "", 2);
end
local amount_multiplicator, last_amount;
local allow_trade;
local account;
local area;
local tp;
local custom_id = "Simple Strategy";
local TRADES_UPDATE = 1;
function Prepare(onlyName)
local name = string.format("%s (%s)", profile:id(), instance.bid:instrument());
instance:name(name);
if onlyName then
return;
end
allow_trade = instance.parameters.allow_trade;
account = instance.parameters.account;
area = instance.parameters.area;
tp = instance.parameters.tp;
amount_multiplicator = instance.parameters.amount_multiplicator;
last_amount = 1;
offer_id = core.host:findTable("offers"):find("Instrument", instance.bid:instrument()).OfferID;
core.host:execute("subscribeTradeEvents", TRADES_UPDATE, "trades");
end
function ExtUpdate(id, source, period)
end
local original_rate;
local opposite_rate;
local stops = {};
function ExtAsyncOperationFinished(cookie, success, message, message2, message3)
if cookie == TRADES_UPDATE then
local trade_id = message;
local close_trade = success;
if close_trade then
local closed_trade = core.host:findTable("closed trades"):find("TradeID", trade_id);
if closed_trade ~= nil then
trading:FindTrade():WhenInstrument(closed_trade.Instrument):Do(function (trade) trading:Close(trade) end);
trading:FindOrder():WhenInstrument(closed_trade.Instrument):Do(function (order) trading:DeleteOrder(order) end);
end
else
local new_trade = core.host:findTable("trades"):find("TradeID", trade_id);
if new_trade ~= nil then
local offer = core.host:findTable("offers"):find("Instrument", new_trade.Instrument);
if original_rate == nil then
original_rate = new_trade.Open;
if new_trade.BS == "B" then
opposite_rate = original_rate - area * offer.PointSize;
stops["B"] = opposite_rate - tp * offer.PointSize;
stops["S"] = original_rate + tp * offer.PointSize;
else
opposite_rate = original_rate + area * offer.PointSize;
stops["S"] = opposite_rate + tp * offer.PointSize;
stops["B"] = original_rate - tp * offer.PointSize;
end
end
local rate;
if math.abs(new_trade.Open - original_rate) < math.abs(new_trade.Open - opposite_rate) then
rate = opposite_rate;
else
rate = original_rate;
end
local total_profit = trading:FindTrade()
:WhenInstrument(new_trade.Instrument)
:Summ(
function (trade)
local mult = 1;
if new_trade.BS == trade.BS then
mult = -1;
end
local pl;
if new_trade.BS == "B" then
pl = (trade.Open - stops[trade.BS]) * trade.AmountK
else
pl = (stops[trade.BS] - trade.Open) * trade.AmountK
end
return pl * mult / offer.PointSize
end) + area;
local order_amount = math.max(1, math.ceil(-total_profit / tp * last_amount));
last_amount = last_amount * amount_multiplicator;
trading:EntryOrder(new_trade.Instrument)
:SetSide(trading:getOppositeSide(new_trade.BS))
:SetRate(rate)
:SetPipLimit(nil, tp)
:SetAccountID(account)
:SetAmount(order_amount)
:SetCustomID(custom_id)
:Execute();
end
end
end
end
dofile(core.app_path() .. "\\strategies\\standard\\include\\helper.lua");
trading = {};
trading.Name = "Trading";
trading.Version = "4.19";
trading.Debug = false;
trading.AddAmountParameter = true;
trading.AddStopParameter = true;
trading.AddLimitParameter = true;
trading.AddBreakevenParameters = true;
trading._ids_start = nil;
trading._signaler = nil;
trading._account = nil;
trading._all_modules = {};
trading._request_id = {};
trading._waiting_requests = {};
trading._used_stop_orders = {};
trading._used_limit_orders = {};
function trading:trace(str) if not self.Debug then return; end core.host:trace(self.Name .. ": " .. str); end
function trading:RegisterModule(modules) for _, module in pairs(modules) do self:OnNewModule(module); module:OnNewModule(self); end modules[#modules + 1] = self; self._ids_start = (#modules) * 100; end
function trading:AddPositionParameters(parameters, id)
if self.AddAmountParameter then
parameters:addInteger("amount" .. id, "Trade Amount in Lots", "", 1);
end
if CreateStopParameters == nil or not CreateStopParameters(parameters, id) then
parameters:addString("stop_type" .. id, "Stop Order", "", "no");
parameters:addStringAlternative("stop_type" .. id, "No stop", "", "no");
parameters:addStringAlternative("stop_type" .. id, "In Pips", "", "pips");
parameters:addStringAlternative("stop_type" .. id, "ATR", "", "atr");
parameters:addDouble("stop" .. id, "Stop Value", "In pips or ATR period", 30);
parameters:addDouble("atr_stop_mult" .. id, "ATR Stop Multiplicator", "", 2.0);
parameters:addBoolean("use_trailing" .. id, "Trailing stop order", "", false);
parameters:addInteger("trailing" .. id, "Trailing in pips", "Use 1 for dynamic and 10 or greater for the fixed trailing", 1);
end
if self.AddLimitParameter then
parameters:addString("limit_type" .. id, "Limit Order", "", "no");
parameters:addStringAlternative("limit_type" .. id, "No limit", "", "no");
parameters:addStringAlternative("limit_type" .. id, "In Pips", "", "pips");
parameters:addStringAlternative("limit_type" .. id, "ATR", "", "atr");
parameters:addStringAlternative("limit_type" .. id, "Multiplicator of stop", "", "stop");
parameters:addDouble("limit" .. id, "Limit Value", "In pips or ATR period", 30);
parameters:addDouble("atr_limit_mult" .. id, "ATR Limit Multiplicator", "", 2.0);
parameters:addString("TRAILING_LIMIT_TYPE" .. id, "Trailing Limit", "", "Off");
parameters:addStringAlternative("TRAILING_LIMIT_TYPE" .. id, "Off", "", "Off");
parameters:addStringAlternative("TRAILING_LIMIT_TYPE" .. id, "Favorable", "moves limit up for long/buy positions, vice versa for short/sell", "Favorable");
parameters:addStringAlternative("TRAILING_LIMIT_TYPE" .. id, "Unfavorable", "moves limit down for long/buy positions, vice versa for short/sell", "Unfavorable");
parameters:addDouble("TRAILING_LIMIT_TRIGGER" .. id, "Trailing Limit Trigger in Pips", "", 0);
parameters:addDouble("TRAILING_LIMIT_STEP" .. id, "Trailing Limit Step in Pips", "", 10);
elseif CreateLimitParameters ~= nil then
CreateLimitParameters(parameters, id);
end
if self.AddBreakevenParameters then
parameters:addBoolean("use_breakeven" .. id, "Use Breakeven", "", false);
parameters:addDouble("breakeven_when" .. id, "Breakeven Activation Value, in pips", "", 10);
parameters:addDouble("breakeven_to" .. id, "Breakeven To, in pips", "", 0);
parameters:addString("breakeven_trailing" .. id, "Trailing after breakeven", "", "default");
parameters:addStringAlternative("breakeven_trailing" .. id, "Do not change", "", "default");
parameters:addStringAlternative("breakeven_trailing" .. id, "Set trailing", "", "set");
parameters:addBoolean("breakeven_close" .. id, "Partial close on breakeven", "", false);
parameters:addDouble("breakeven_close_amount" .. id, "Partial close amount, %", "", 50);
end
end
function trading:Init(parameters, count)
parameters:addBoolean("allow_trade", "Allow strategy to trade", "", true);
parameters:setFlag("allow_trade", core.FLAG_ALLOW_TRADE);
parameters:addString("account", "Account to trade on", "", "");
parameters:setFlag("account", core.FLAG_ACCOUNT);
parameters:addString("allow_side", "Allow side", "", "both")
parameters:addStringAlternative("allow_side", "Both", "", "both")
parameters:addStringAlternative("allow_side", "Long/buy only", "", "buy")
parameters:addStringAlternative("allow_side", "Short/sell only", "", "sell")
parameters:addBoolean("close_on_opposite", "Close on Opposite", "", true);
parameters:addBoolean("position_cap", "Position Cap", "", false);
parameters:addInteger("no_of_positions", "Max # of open positions", "", 1);
parameters:addInteger("no_of_buy_position", "Max # of buy positions", "", 1);
parameters:addInteger("no_of_sell_position", "Max # of sell positions", "", 1);
if count == nil or count == 1 then
parameters:addGroup("Position");
self:AddPositionParameters(parameters, "");
else
for i = 1, count do
parameters:addGroup("Position #" .. i);
parameters:addBoolean("use_position_" .. i, "Open position #" .. i, "", i == 1);
self:AddPositionParameters(parameters, "_" .. i);
end
end
end
function trading:Prepare(name_only)
if name_only then return; end
end
function trading:ExtUpdate(id, source, period)
end
function trading:OnNewModule(module)
if module.Name == "Signaler" then self._signaler = module; end
self._all_modules[#self._all_modules + 1] = module;
end
function trading:AsyncOperationFinished(cookie, success, message, message1, message2)
local res = self._waiting_requests[cookie];
if res ~= nil then
res.Finished = true;
res.Success = success;
if not success then
res.Error = message;
if self._signaler ~= nil then
self._signaler:Signal(res.Error);
else
self:trace(res.Error);
end
elseif res.OnSuccess ~= nil then
res:OnSuccess();
end
self._waiting_requests[cookie] = nil;
elseif cookie == self._order_update_id then
for _, order in ipairs(self._monitored_orders) do
if order.RequestID == message2 then
order.FixStatus = message1;
end
end
elseif cookie == self._ids_start + 2 then
if not success then
if self._signaler ~= nil then
self._signaler:Signal("Close order failed: " .. message);
else
self:trace("Close order failed: " .. message);
end
end
end
end
function trading:getOppositeSide(side) if side == "B" then return "S"; end return "B"; end
function trading:getId()
for id = self._ids_start, self._ids_start + 100 do
if self._waiting_requests[id] == nil then return id; end
end
return self._ids_start;
end
function trading:CreateStopOrder(trade, stop_rate, trailing)
local valuemap = core.valuemap();
valuemap.Command = "CreateOrder";
valuemap.OfferID = trade.OfferID;
valuemap.Rate = stop_rate;
if trade.BS == "B" then
valuemap.BuySell = "S";
else
valuemap.BuySell = "B";
end
local can_close = core.host:execute("getTradingProperty", "canCreateMarketClose", trade.Instrument, trade.AccountID);
if can_close then
valuemap.OrderType = "S";
valuemap.AcctID = trade.AccountID;
valuemap.TradeID = trade.TradeID;
valuemap.Quantity = trade.Lot;
valuemap.TrailUpdatePips = trailing;
else
valuemap.OrderType = "SE"
valuemap.AcctID = trade.AccountID;
valuemap.NetQtyFlag = "Y"
end
local id = self:getId();
local success, msg = terminal:execute(id, valuemap);
if not(success) then
local message = "Failed create stop " .. msg;
self:trace(message);
if self._signaler ~= nil then
self._signaler:Signal(message);
end
local res = {};
res.Finished = true;
res.Success = false;
res.Error = message;
return res;
end
local res = {};
res.Finished = false;
res.RequestID = msg;
self._waiting_requests[id] = res;
self._request_id[trade.TradeID] = msg;
return res;
end
function trading:CreateLimitOrder(trade, limit_rate)
local valuemap = core.valuemap();
valuemap.Command = "CreateOrder";
valuemap.OfferID = trade.OfferID;
valuemap.Rate = limit_rate;
if trade.BS == "B" then
valuemap.BuySell = "S";
else
valuemap.BuySell = "B";
end
local can_close = core.host:execute("getTradingProperty", "canCreateMarketClose", trade.Instrument, trade.AccountID);
if can_close then
valuemap.OrderType = "L";
valuemap.AcctID = trade.AccountID;
valuemap.TradeID = trade.TradeID;
valuemap.Quantity = trade.Lot;
else
valuemap.OrderType = "LE"
valuemap.AcctID = trade.AccountID;
valuemap.NetQtyFlag = "Y"
end
local success, msg = terminal:execute(200, valuemap);
if not(success) then
terminal:alertMessage(trade.Instrument, limit_rate, "Failed create limit " .. msg, core.now());
else
self._request_id[trade.TradeID] = msg;
end
end
function trading:ChangeOrder(order, rate, trailing)
local min_change = core.host:findTable("offers"):find("Instrument", order.Instrument).PointSize;
if math.abs(rate - order.Rate) > min_change then
self:trace(string.format("Changing an order to %s", tostring(rate)));
-- stop exists
local valuemap = core.valuemap();
valuemap.Command = "EditOrder";
valuemap.AcctID = order.AccountID;
valuemap.OrderID = order.OrderID;
valuemap.TrailUpdatePips = trailing;
valuemap.Rate = rate;
local id = self:getId();
local success, msg = terminal:execute(id, valuemap);
if not(success) then
local message = "Failed change order " .. msg;
self:trace(message);
if self._signaler ~= nil then
self._signaler:Signal(message);
end
local res = {};
res.Finished = true;
res.Success = false;
res.Error = message;
return res;
end
local res = {};
res.Finished = false;
res.RequestID = msg;
self._waiting_requests[id] = res;
return res;
end
local res = {};
res.Finished = true;
res.Success = true;
return res;
end
function trading:IsLimitOrderType(order_type) return order_type == "L" or order_type == "LE" or order_type == "LT" or order_type == "LTE"; end
function trading:IsStopOrderType(order_type) return order_type == "S" or order_type == "SE" or order_type == "ST" or order_type == "STE"; end
function trading:FindLimitOrder(trade)
local can_close = core.host:execute("getTradingProperty", "canCreateMarketClose", trade.Instrument, trade.AccountID);
if can_close then
local order_id;
if trade.LimitOrderID ~= nil and trade.LimitOrderID ~= "" then
order_id = trade.LimitOrderID;
self:trace("Using limit order id from the trade");
elseif self._request_id[trade.TradeID] ~= nil then
self:trace("Searching limit order by request id: " .. tostring(self._request_id[trade.TradeID]));
local order = core.host:findTable("orders"):find("RequestID", self._request_id[trade.TradeID]);
if order ~= nil then
order_id = order.OrderID;
self._request_id[trade.TradeID] = nil;
end
end
-- Check that order is stil exist
if order_id ~= nil then return core.host:findTable("orders"):find("OrderID", order_id); end
else
local enum = core.host:findTable("orders"):enumerator();
local row = enum:next();
while (row ~= nil) do
if row.ContingencyType == 3 and IsLimitOrderType(row.Type) and self._used_limit_orders[row.OrderID] ~= true then
self._used_limit_orders[row.OrderID] = true;
return row;
end
row = enum:next();
end
end
return nil;
end
function trading:FindStopOrder(trade)
local can_close = core.host:execute("getTradingProperty", "canCreateMarketClose", trade.Instrument, trade.AccountID);
if can_close then
local order_id;
if trade.StopOrderID ~= nil and trade.StopOrderID ~= "" then
order_id = trade.StopOrderID;
self:trace("Using stop order id from the trade");
elseif self._request_id[trade.TradeID] ~= nil then
self:trace("Searching stop order by request id: " .. tostring(self._request_id[trade.TradeID]));
local order = core.host:findTable("orders"):find("RequestID", self._request_id[trade.TradeID]);
if order ~= nil then
order_id = order.OrderID;
self._request_id[trade.TradeID] = nil;
end
end
-- Check that order is stil exist
if order_id ~= nil then return core.host:findTable("orders"):find("OrderID", order_id); end
else
local enum = core.host:findTable("orders"):enumerator();
local row = enum:next();
while (row ~= nil) do
if row.ContingencyType == 3 and self:IsStopOrderType(row.Type) and self._used_stop_orders[row.OrderID] ~= true then
self._used_stop_orders[row.OrderID] = true;
return row;
end
row = enum:next();
end
end
return nil;
end
function trading:MoveStop(trade, stop_rate, trailing)
local order = self:FindStopOrder(trade);
if order == nil then
if trailing == 0 then
trailing = nil;
end
return self:CreateStopOrder(trade, stop_rate, trailing);
else
if trailing == 0 then
if order.TrlMinMove ~= 0 then
trailing = order.TrlMinMove
else
trailing = nil;
end
end
return self:ChangeOrder(order, stop_rate, trailing);
end
end
function trading:MoveLimit(trade, limit_rate)
self:trace("Searching for a limit");
local order = self:FindLimitOrder(trade);
if order == nil then
self:trace("Limit order not found, creating a new one");
return self:CreateLimitOrder(trade, limit_rate);
else
return self:ChangeOrder(order, limit_rate);
end
end
function trading:RemoveStop(trade)
self:trace("Searching for a stop");
local order = self:FindStopOrder(trade);
if order == nil then self:trace("No stop"); return nil; end
self:trace("Deleting order");
return self:DeleteOrder(order);
end
function trading:RemoveLimit(trade)
self:trace("Searching for a limit");
local order = self:FindLimitOrder(trade);
if order == nil then self:trace("No limit"); return nil; end
self:trace("Deleting order");
return self:DeleteOrder(order);
end
function trading:DeleteOrder(order)
self:trace(string.format("Deleting order %s", order.OrderID));
local valuemap = core.valuemap();
valuemap.Command = "DeleteOrder";
valuemap.OrderID = order.OrderID;
local id = self:getId();
local success, msg = terminal:execute(id, valuemap);
if not(success) then
local message = "Delete order failed: " .. msg;
self:trace(message);
if self._signaler ~= nil then
self._signaler:Signal(message);
end
local res = {};
res.Finished = true;
res.Success = false;
res.Error = message;
return res;
end
local res = {};
res.Finished = false;
res.RequestID = msg;
self._waiting_requests[id] = res;
return res;
end
function trading:GetCustomID(qtxt)
if qtxt == nil then
return nil;
end
local metadata = self:GetMetadata(qtxt);
if metadata == nil then
return qtxt;
end
return metadata.CustomID;
end
function trading:FindOrder()
local search = {};
function search:WhenCustomID(custom_id) self.CustomID = custom_id; return self; end
function search:WhenSide(bs) self.Side = bs; return self; end
function search:WhenInstrument(instrument) self.Instrument = instrument; return self; end
function search:WhenAccountID(account_id) self.AccountID = account_id; return self; end
function search:WhenRate(rate) self.Rate = rate; return self; end
function search:WhenOrderType(orderType) self.OrderType = orderType; return self; end
function search:Do(action)
local enum = core.host:findTable("orders"):enumerator();
local row = enum:next();
local count = 0
while (row ~= nil) do
if self:PassFilter(row) then
if action(row) then
count = count + 1;
end
end
row = enum:next();
end
return count;
end
function search:Summ(action)
local enum = core.host:findTable("orders"):enumerator();
local row = enum:next();
local summ = 0
while (row ~= nil) do
if self:PassFilter(row) then
summ = summ + action(row);
end
row = enum:next();
end
return summ;
end
function search:PassFilter(row)
return (row.Instrument == self.Instrument or not self.Instrument)
and (row.BS == self.Side or not self.Side)
and (row.AccountID == self.AccountID or not self.AccountID)
and (trading:GetCustomID(row.QTXT) == self.CustomID or not self.CustomID)
and (row.Rate == self.Rate or not self.Rate)
and (row.Type == self.OrderType or not self.OrderType);
end
function search:All()
local enum = core.host:findTable("orders"):enumerator();
local row = enum:next();
local orders = {};
while (row ~= nil) do
if self:PassFilter(row) then orders[#orders + 1] = row; end
row = enum:next();
end
return orders;
end
function search:First()
local enum = core.host:findTable("orders"):enumerator();
local row = enum:next();
while (row ~= nil) do
if self:PassFilter(row) then return row; end
row = enum:next();
end
return nil;
end
return search;
end
function trading:FindTrade()
local search = {};
function search:WhenCustomID(custom_id) self.CustomID = custom_id; return self; end
function search:WhenSide(bs) self.Side = bs; return self; end
function search:WhenInstrument(instrument) self.Instrument = instrument; return self; end
function search:WhenAccountID(account_id) self.AccountID = account_id; return self; end
function search:WhenOpen(open) self.Open = open; return self; end
function search:WhenOpenOrderReqID(open_order_req_id) self.OpenOrderReqID = open_order_req_id; return self; end
function search:Do(action)
local enum = core.host:findTable("trades"):enumerator();
local row = enum:next();
local count = 0
while (row ~= nil) do
if self:PassFilter(row) then
if action(row) then
count = count + 1;
end
end
row = enum:next();
end
return count;
end
function search:Summ(action)
local enum = core.host:findTable("trades"):enumerator();
local row = enum:next();
local summ = 0
while (row ~= nil) do
if self:PassFilter(row) then
summ = summ + action(row);
end
row = enum:next();
end
return summ;
end
function search:PassFilter(row)
return (row.Instrument == self.Instrument or not self.Instrument)
and (row.BS =Показано первые 50% кода (символов: 36162)
Открыть по тарифу
Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.
Разовая покупка файла
На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.
Зачем нужен email? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.