← К каталогу
Исходный код · ID 4569

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 и перейдите к оплате.

Стоимость полного кода
1 863,10₽ ≈ $23.21