SONU SCALPER
Сообщите нам, если считаете, что материал размещён без разрешения.
Сообщить о материале
Обращение по товару «gold.mq5» отправится на рассмотрение. Укажите контакт для ответа и опишите причину.
О программе
Это торговый советник для MetaTrader 5, работающий на тиковых данных. Советник анализирует поток котировок, отслеживает краткосрочные изменения цены и открывает рыночные позиции при появлении определённых условий. Для каждой позиции автоматически рассчитываются защитные уровни стоп-лосс и тейк-профит с учётом ограничений брокера по минимальному расстоянию и шагу цены. Объём сделки нормализуется под требования символа.
Советник ведёт учёт торговых результатов за текущий день и с момента запуска, включая количество сделок, прибыльных и убыточных закрытий. При достижении заданной дневной цели по прибыли или дневного лимита убытка новые позиции не открываются до наступления следующего торгового дня. Статистика подгружается из истории сделок при инициализации.
На графике отображается информационная панель с текущими показателями счёта, дневным результатом, прогрессом к цели, статусом торговли и счётчиком сделок. Панель обновляется на каждом тике. Советник не использует внешние индикаторы и не требует дополнительных библиотек.
Настройки программы
| Настройка | Описание |
|---|---|
| Capital | Опорная величина капитала, используемая для расчёта процентного отношения дневного результата к заданному значению при отображении на панели. |
| DailyProfitTarget | Целевая величина дневной прибыли. При достижении этого значения новые позиции не открываются до начала следующего торгового дня. |
| DailyLossLimit | Предельная величина дневного убытка. При достижении этого значения новые позиции не открываются до начала следующего торгового дня. |
| LotSize | Фиксированный объём сделки, который нормализуется под ограничения брокера по минимальному и максимальному объёму и шагу изменения лота. |
Поиск по коду файла
#property version "1.00"
#property strict
input double Capital = 100.0; // Reference Capital
input double DailyProfitTarget = 50.0; // Daily profit target
input double DailyLossLimit = 20.0; // Daily loss limit
input double LotSize = 0.10; // Fixed lot size
const ulong MAGIC_NUMBER = 12345;
const int TICK_WINDOW = 20;
const double SPIKE_FACTOR = 1.8;
const double REVERSAL_FACTOR = 0.5;
const int DEVIATION_POINTS = 10;
const int MAX_TRADES_CANDLE = 4;
double g_point = 0.0;
double g_tickSize = 0.0;
double g_tickValue = 0.0;
double g_minLot = 0.0;
double g_maxLot = 0.0;
double g_lotStep = 0.0;
int g_digits = 0;
long g_stopsLevel = 0;
long g_freezeLevel = 0;
double g_prices[20];
int g_priceCount = 0;
double g_lastPrice = 0.0;
bool g_pendingSignal = false;
int g_pendingDirection = 0; // 1 = BUY, -1 = SELL
double g_pendingPrice = 0.0;
double g_pendingAvgAbsMove = 0.0;
datetime g_expertStartTime = 0;
datetime g_dayStart = 0;
double g_closedPnlToday = 0.0;
double g_closedPnlTotal = 0.0;
double g_openPnl = 0.0;
double g_dayPnl = 0.0;
double g_equity = 0.0;
double g_balance = 0.0;
double g_marginUsed = 0.0;
double g_freeMargin = 0.0;
ulong g_tradesToday = 0;
ulong g_winsToday = 0;
ulong g_lossesToday = 0;
datetime g_candleTime = 0;
int g_tradesCandle = 0;
bool g_tradingStopped = false;
string PREFIX = "SONU_DASH_";
const int DASH_X = 20;
const int DASH_Y = 20;
const int STEP_Y = 20;
int OnInit()
{
// код доступен только после оплаты
}
void OnDeinit(const int reason)
{
DeleteDashboardObjects();
Comment("");
}
void OnTick()
{
// код доступен только после оплаты
}
void OnTradeTransaction(const MqlTradeTransaction &trans,
const MqlTradeRequest &request,
const MqlTradeResult &result)
{
if(trans.type != TRADE_TRANSACTION_DEAL_ADD)
return;
ulong dealTicket = trans.deal;
if(dealTicket == 0)
return;
if(!HistoryDealSelect(dealTicket))
return;
string dealSymbol = HistoryDealGetString(dealTicket, DEAL_SYMBOL);
if(dealSymbol != _Symbol)
return;
ulong dealMagic = (ulong)HistoryDealGetInteger(dealTicket, DEAL_MAGIC);
if(dealMagic != MAGIC_NUMBER)
return;
ENUM_DEAL_ENTRY entryType = (ENUM_DEAL_ENTRY)HistoryDealGetInteger(dealTicket, DEAL_ENTRY);
double dealPnl = HistoryDealGetDouble(dealTicket, DEAL_PROFIT)
+ HistoryDealGetDouble(dealTicket, DEAL_SWAP)
+ HistoryDealGetDouble(dealTicket, DEAL_COMMISSION);
datetime dealTime = (datetime)HistoryDealGetInteger(dealTicket, DEAL_TIME);
if(entryType == DEAL_ENTRY_IN || entryType == DEAL_ENTRY_INOUT)
{
if(IsSameServerDay(dealTime, g_dayStart))
g_tradesToday++;
}
if(entryType == DEAL_ENTRY_OUT || entryType == DEAL_ENTRY_OUT_BY || entryType == DEAL_ENTRY_INOUT)
{
g_closedPnlTotal += dealPnl;
if(IsSameServerDay(dealTime, g_dayStart))
{
g_closedPnlToday += dealPnl;
if(dealPnl > 0.0)
g_winsToday++;
else if(dealPnl < 0.0)
g_lossesToday++;
}
UpdateAccountInfo();
EvaluateTradingStatus();
UpdateDashboard();
}
}
bool ReadSymbolProperties()
{
g_point = SymbolInfoDouble(_Symbol, SYMBOL_POINT);
g_tickSize = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_SIZE);
g_tickValue = SymbolInfoDouble(_Symbol, SYMBOL_TRADE_TICK_VALUE);
g_minLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MIN);
g_maxLot = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_MAX);
g_lotStep = SymbolInfoDouble(_Symbol, SYMBOL_VOLUME_STEP);
g_digits = (int)SymbolInfoInteger(_Symbol, SYMBOL_DIGITS);
g_stopsLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_STOPS_LEVEL);
g_freezeLevel = SymbolInfoInteger(_Symbol, SYMBOL_TRADE_FREEZE_LEVEL);
if(g_point <= 0.0)
{
Print("SONU SCALPER: Invalid SYMBOL_POINT for ", _Symbol);
return false;
}
if(g_tickSize <= 0.0)
g_tickSize = g_point;
if(g_minLot <= 0.0)
g_minLot = 0.01;
if(g_maxLot <= 0.0)
g_maxLot = g_minLot;
if(g_lotStep <= 0.0)
g_lotStep = g_minLot;
return true;
}
void UpdateAccountInfo()
{
g_equity = AccountInfoDouble(ACCOUNT_EQUITY);
g_balance = AccountInfoDouble(ACCOUNT_BALANCE);
g_marginUsed = AccountInfoDouble(ACCOUNT_MARGIN);
g_freeMargin = AccountInfoDouble(ACCOUNT_MARGIN_FREE);
g_openPnl = 0.0;
int total = PositionsTotal();
for(int i = total - 1; i >= 0; --i)
{
string sym = PositionGetSymbol(i);
if(sym != _Symbol)
continue;
ulong magic = (ulong)PositionGetInteger(POSITION_MAGIC);
if(magic != MAGIC_NUMBER)
continue;
g_openPnl += PositionGetDouble(POSITION_PROFIT)
+ PositionGetDouble(POSITION_SWAP);
}
g_dayPnl = g_closedPnlToday + g_openPnl;
}
void CheckDayReset()
{
datetime now = TimeCurrent();
if(now <= 0)
now = TimeLocal();
datetime newDay = GetServerDayStart(now);
if(g_dayStart == 0)
{
g_dayStart = newDay;
LoadDailyStatistics();
g_tradingStopped = false;
return;
}
if(newDay != g_dayStart)
{
g_dayStart = newDay;
g_closedPnlToday = 0.0;
g_tradesToday = 0;
g_winsToday = 0;
g_lossesToday = 0;
g_tradingStopped = false;
LoadDailyStatistics();
}
}
void UpdateCandleCounter()
{
datetime currentCandle = iTime(_Symbol, PERIOD_M1, 0);
if(currentCandle <= 0)
return;
if(g_candleTime == 0)
{
g_candleTime = currentCandle;
g_tradesCandle = CountEntriesForCandle(g_candleTime);
return;
}
if(currentCandle != g_candleTime)
{
g_candleTime = currentCandle;
g_tradesCandle = CountEntriesForCandle(g_candleTime);
}
}
void ExecuteSniper(const MqlTick &tick)
{
const double currentPrice = tick.bid;
if(currentPrice <= 0.0)
return;
if(g_priceCount == 0)
{
g_prices[0] = currentPrice;
g_priceCount = 1;
g_lastPrice = currentPrice;
return;
}
const double previousPrice = g_lastPrice;
const double currentMove = currentPrice - previousPrice;
g_lastPrice = currentPrice;
if(g_pendingSignal)
{
bool confirmed = false;
if(g_pendingDirection > 0) // BUY: opposite move is upward
confirmed = (currentPrice - g_pendingPrice) >= (REVERSAL_FACTOR * g_point);
else if(g_pendingDirection < 0) // SELL: opposite move is downward
confirmed = (g_pendingPrice - currentPrice) >= (REVERSAL_FACTOR * g_point);
g_pendingSignal = false;
if(confirmed && !g_tradingStopped && !HasOurOpenPosition() && g_tradesCandle < MAX_TRADES_CANDLE)
{
OpenOrder(g_pendingDirection, tick, g_pendingAvgAbsMove);
UpdateAccountInfo();
EvaluateTradingStatus();
}
AddPriceToRollingWindow(currentPrice);
return;
}
AddPriceToRollingWindow(currentPrice);
if(g_priceCount < 3)
return;
if(g_tradingStopped)
return;
if(HasOurOpenPosition())
return;
if(g_tradesCandle >= MAX_TRADES_CANDLE)
return;
double avgAbsMove = 0.0;
double stdDev = 0.0;
if(!CalculateTickStats(avgAbsMove, stdDev))
return;
if(stdDev <= 0.0)
return;
if(MathAbs(currentMove) > (SPIKE_FACTOR * stdDev))
{
g_pendingSignal = true;
g_pendingDirection = (currentMove < 0.0 ? 1 : -1);
g_pendingPrice = currentPrice;
g_pendingAvgAbsMove = avgAbsMove;
}
}
void AddPriceToRollingWindow(const double price)
{
if(g_priceCount < TICK_WINDOW)
{
g_prices[g_priceCount] = price;
g_priceCount++;
return;
}
for(int i = 0; i < TICK_WINDOW - 1; ++i)
g_prices[i] = g_prices[i + 1];
g_prices[TICK_WINDOW - 1] = price;
}
bool CalculateTickStats(double &avgAbsMove, double &stdDev)
{
avgAbsMove = 0.0;
stdDev = 0.0;
if(g_priceCount < 2)
return false;
const int moves = g_priceCount - 1;
double sumMove = 0.0;
double sumAbs = 0.0;
for(int i = 1; i < g_priceCount; ++i)
{
double d = g_prices[i] - g_prices[i - 1];
sumMove += d;
sumAbs += MathAbs(d);
}
avgAbsMove = sumAbs / moves;
double meanMove = sumMove / moves;
double sumSq = 0.0;
for(int i = 1; i < g_priceCount; ++i)
{
double d = g_prices[i] - g_prices[i - 1];
double diff = d - meanMove;
sumSq += diff * diff;
}
stdDev = MathSqrt(sumSq / moves); // population standard deviation
return true;
}
bool OpenOrder(const int direction, const MqlTick &tick, const double avgAbsMove)
{
if(direction != 1 && direction != -1)
return false;
if(HasOurOpenPosition())
return false;
if(g_tradesCandle >= MAX_TRADES_CANDLE)
return false;
ENUM_SYMBOL_TRADE_MODE tradeMode = (ENUM_SYMBOL_TRADE_MODE)SymbolInfoInteger(_Symbol, SYMBOL_TRADE_MODE);
if(tradeMode == SYMBOL_TRADE_MODE_DISABLED)
{
Print("SONU SCALPER: Trading disabled for ", _Symbol);
return false;
}
double volume = NormalizeVolume(LotSize);
if(volume <= 0.0)
return false;
double minTpDistance = 3.0 * g_point;
double minSlDistance = 5.0 * g_point;
double brokerMinDistance = (double)MathMax(g_stopsLevel, g_freezeLevel) * g_point;
double tpDistance = MathMax(avgAbsMove * 0.5, minTpDistance);
double slDistance = MathMax(avgAbsMove * 1.0, minSlDistance);
tpDistance = MathMax(tpDistance, brokerMinDistance);
slDistance = MathMax(slDistance, brokerMinDistance);
double price = 0.0;
double sl = 0.0;
double tp = 0.0;
if(direction == 1)
{
price = tick.ask;
sl = FloorToTick(price - slDistance); // keep SL safely outside
tp = CeilToTick(price + tpDistance); // keep TP safely outside
}
else
{
price = tick.bid;
sl = CeilToTick(price + slDistance); // keep SL safely outside
tp = FloorToTick(price - tpDistance); // keep TP safely outside
}
if(price <= 0.0 || sl <= 0.0 || tp <= 0.0)
return false;
MqlTradeRequest request = {};
MqlTradeResult result = {};
request.action = TRADE_ACTION_DEAL;
request.magic = MAGIC_NUMBER;
request.symbol = _Symbol;
request.volume = volume;
request.type = (direction == 1 ? ORDER_TYPE_BUY : ORDER_TYPE_SELL);
request.price = price;
request.sl = sl;
request.tp = tp;
request.deviation = DEVIATION_POINTS;
request.type_filling = GetFillingMode();
request.type_time = ORDER_TIME_GTC;
request.comment = "SONU SCALPER";
ResetLastError();
bool sent = OrderSend(request, result);
if(!sent)
{
Print("SONU SCALPER: OrderSend failed. Error=", GetLastError(),
" Retcode=", result.retcode, " Comment=", result.comment);
return false;
}
if(result.retcode != TRADE_RETCODE_DONE &&
result.retcode != TRADE_RETCODE_DONE_PARTIAL &&
result.retcode != TRADE_RETCODE_PLACED)
{
Print("SONU SCALPER: Broker rejected order. Retcode=", result.retcode,
" Comment=", result.comment);
return false;
}
g_tradesCandle++;
return true;
}
bool HasOurOpenPosition()
{
int total = PositionsTotal();
for(int i = total - 1; i >= 0; --i)
{
string sym = PositionGetSymbol(i);
if(sym != _Symbol)
continue;
ulong magПоказано первые 50% кода (символов: 18631)
Открыть по тарифу
Купите тариф с пакетом открытий или войдите, если тариф уже есть. Открытый файл останется в вашем кабинете навсегда.
Разовая покупка файла
На этой странице откроется полный исходный код файла вместо ознакомительного фрагмента.
Зачем нужен email? После подтверждения оплаты файл сохранится в кабинете на этот email. Если аккаунта ещё нет, мы создадим его и пришлём ссылку для задания пароля. Если аккаунт уже есть, покупка добавится в него. Укажите адрес, к которому у вас есть доступ.
Чтобы оформить заказ, введите email и перейдите к оплате.