Tester: not enough money

 
Непонятно, как тестер считает свободную маржу. Например:
OrderSend error not enough money
Tester: PrevBalance: 10000.00, PrevPL: 7489.70, PrevEquity 17489.70, PrevMargin: 5630.00, NewMargin: 23110, FreeMargin: -5620.30
Tester: not enough money for sell 11.85 EURUSD


Казалось бы,

PrevFreeMargin = PrevEquity - PrevMargin = 17489.70 - 5630.00 = 11859.7


должно хватить на продажу 11.85 лот

11.85 * (100000 / 100) = 11850


Тестер почему-то пытается увеличить маржу на значение Equity вместо Lots * LotSize / Leverage:

NewMargin - PrevMargin = 23110 - 5630.00 = 17480

Понятное дело, денег в этом случае не хватит.
В трассировке AccountFreeMargin() выдает корректные 11859.7.

Баг выползает только при попытке открыть ордер на маржу, большую чем AccountFreeMargin() - AccountMargin().
При ручной торговле в терминале таких проблем не возникает.

Билд 193.04.05.06

Тема была озвучена в "Что обозначают эти наднписи в Логе?", но, к сожалению, должного осмысления и развития не получила.

 
Вы совершенно забыли про дополнительный убыток в размере спреда от новой открытой позиции. 4 пипса на 11.85 лотов евро = - 474. Поэтому тестер все верно посчитал.

В оценке размера лота надо оценивать не только AccountFreeMargin, но и явно учитывать появляющийся убыток на открытой позиции. Да и торговать на все доступные деньги очень опасно из-за чрезмерного риска.

Обычно в формулах расчета лотов используется важный компонент "процент риска/использования средств", который принимает достаточно малые значения 3-5-10%. В этом случае можно не учитывать появляющийся убыток, так как им можно пренебречь. Если же процент риска доходит до 100% доступных средств, то очень важно точно расчитать лоты.
 
Нет не забыл. Какое отношение цены покупки/продажи могут иметь к свободной марже и количеству лотов, которые можно на нее купить (или продать)? Equity, действительно, должен уменьшиться на стоимость спреда, и сделка должна состояться, если его хватает, чтобы покрыть эту стоимость. При чем тут FreeMargin?

Я как раз и пытаюсь точно рассчитать лоты при максимальном риске.

Например, при ручной продаже в примере выше все прекрасно работает:
PrevBalance: 10000.00
PrevPL: 7489.70
PrevEquity: 17489.70
PrevMargin: 5630.00
PrevFreeMargin = PrevEquity - PrevMargin = 17489.70 - 5630.00 = 11859.70
Lots = PrevFreeMargin / (LotSize / Leverage) = 11859.70 / (100000 / 100) = 11.85 (с округлением вниз до LotStep)
NewMargin = PrevMargin + Lots * LotSize / Leverage = 5630.00 + 11.85 * 100000 / 100 = 17480.00 (никак не 23110, как в трассировке)
NewEquity = PrevEquity - Lots * LotSize * Spread * Point = 17489.70 - 11.85 * 4 * 0.0001 *  100000 = 17489.70 - 474 = 17015.70
FreeMargin = NewEquity - NewMargin = 17015.70 - 17480.00 = -464.30 (в трассировке -5620.30)


Похоже, при расчете NewFreeMargin тестер к добавляемой аллокируемой марже ошибочно прибавляет существующую маржу еще раз, т.е. NewFreeMargin = FreeMargin + FreeMargin + Lots * LotSize / Leverage, и не дает открыть ордер.Если максимальный лот рассчитывать исходя из AccountFreeMargin() - AccountMargin(), ордер открывается. После этого AccountFreeMargin() дает корректное новое значение, не то, которое тестер выводит в журнал.
В данном случае совершенно не важно, какие компоненты используются "обычно" в формулах расчета лотов. Даже если тестер учитывает зачем-то спред в расчете свободной маржи и посчитал все правильно, значения, которые он выдал, на порядок отличаются от приведенного Вами. Кто тогда считает неправильно? Совсем плохо, если и терминал при торговле экспертом будет спотыкаться об этот же баг.
Я призываю Вас обратиться к конкретике. Опасность торговли на все доступные деньги и "процент риска/использования средств" не имеют никакого отношения к реализации торговой арифметики в тестере и терминале.

 
Спасибо за детальные расчеты - мы у себя все перепроверим.
Уточните, пожалуйста, какие позиции и по каким ценам были открыты, какие цены EURUSD в момент попытки открытия, в какой валюте депозит. Происходили несколько сделок подряд (несколько OrderSend) или они разнесены по разным тикам.
 
Вот код. Гонял его с одинаковым "успехом" на eurusd и gbpusd.
Ни цены, ни последовательность действий большой роли не играют.
Важно, что, во-первых, невозможно открыть инстант ордер на всю свободную маржу, если использованная маржа больше 0, т.е. уже есть по крайней мере один открытый ордер.
И, во-вторых, после 134 ошибки AccountFreeMargin() выдает неправильное значение. До ошибки - корректное.
В торговле не проверял, только в тестере.
Удачи в ловле.
//+------------------------------------------------------------------+
//|                                                FreeMarginBug.mq4 |
//|                                                     No Copyright |
//|                                         http://www.metaquotes.ru |
//+------------------------------------------------------------------+
#property copyright "No Copyright"
#property link      "http://www.metaquotes.ru"

#include <stderror.mqh>

extern double FirstOrderPercentage = 0.3;
extern int SameTick = 1;

int exitflag = 4;

int start()
{
    int ticket, i;
    double lots;
    
    
    while (AccountFreeMargin() >= MarketInfo(Symbol(), MODE_LOTSIZE) * 
                                  MarketInfo(Symbol(), MODE_MINLOT) / 
                                  AccountLeverage())
    { 
        
        if (exitflag == 0)
            return (0);
    
        exitflag--;

        if (AccountFreeMargin() != AccountEquity() - AccountMargin())
        {
            /* Free margin bug entry */
            Print("");
            Print("AccountFreeMargin bug. Balance ", AccountBalance(),
                  ", Equity ", AccountEquity(),
                  ", FreeMargin (INCORRECT!) ", AccountFreeMargin(),
                  ", FreeMargin (correct) ", AccountEquity() - AccountMargin(),
                  ", Margin() ", AccountMargin());
            Print("");
        }

        ticket = (AccountEquity() - AccountMargin()) / 
                 (MarketInfo(Symbol(), MODE_LOTSIZE) / AccountLeverage()) /
                 MarketInfo(Symbol(), MODE_LOTSTEP);
        
        lots = ticket * MarketInfo(Symbol(), MODE_LOTSTEP);

        if (FirstOrderPercentage > 0.)
        {
            lots *= FirstOrderPercentage;
            FirstOrderPercentage = 0.;
        }
            
        for (i = 0; i < 3 && lots >= MarketInfo(Symbol(), MODE_MINLOT); i++)
        {
            string incorrect;
            string correct;
            
            if (AccountFreeMargin() != AccountEquity() - AccountMargin())
            {
                incorrect = "IN";
                correct = StringConcatenate(", FreeMargin (correct) ", AccountEquity() - AccountMargin()); 
            }
            else
            {    
                incorrect = "";
                correct = "";
            }
            
            Print("Iter ", i, ", ", Bid, "/", Ask,
                  ", Balance ", AccountBalance(),
                  ", Equity ", AccountEquity(),
                  ", FreeMargin (", incorrect, "CORRECT!) ", AccountFreeMargin(),
                  correct,
                  ", Margin() ", AccountMargin(), 
                  ", lots ", lots);

            ticket = OrderSend(Symbol(), OP_BUY, lots, Ask, 5, 0., 0.);
               
            if (ticket >= 0)
                break;

            ticket = GetLastError();
    
            if (ticket == ERR_NOT_ENOUGH_MONEY)
            {
                /* Max lots bug entry */
                if (AccountFreeMargin() != AccountEquity() - AccountMargin())
                {
                    incorrect = "IN";
                    correct = StringConcatenate(", FreeMargin (correct) ", AccountEquity() - AccountMargin()); 
                }
                else
                {    
                    incorrect = "";
                    correct = "";
                }

                Print("Max lots bug. FreeMargin (", incorrect, "CORRECT!) ", AccountFreeMargin(), correct,
                      ", lots ", lots, ", spread ", Ask - Bid);
            
                if (i == 0)
                {
                    ticket = (Ask - Bid) * MarketInfo(Symbol(), MODE_LOTSIZE) / AccountLeverage() /
                                           MarketInfo(Symbol(), MODE_LOTSTEP);
                    
                    Print("Allocate spread cost in free margin (nonsense): lots -= ",
                          ticket * MarketInfo(Symbol(), MODE_LOTSTEP)); 
                    
                    lots -= ticket * MarketInfo(Symbol(), MODE_LOTSTEP);
                }
                else 
                {
                    ticket = (AccountEquity() - AccountMargin() /* Correct free margin */
                              - AccountMargin() /* minus yet another margin!*/) / 
                             (MarketInfo(Symbol(), MODE_LOTSIZE) / AccountLeverage()) /
                             MarketInfo(Symbol(), MODE_LOTSTEP);

                    lots = ticket * MarketInfo(Symbol(), MODE_LOTSTEP);

                    Print("Workaround: lots = ", lots);
                }
            }
        }
        
        if (SameTick == 0)
            break;
    }
               

    return(0);
}
 
Ошибка найдена - будет исправлена в ближайшем билде терминала.
Большое спасибо за помощь.
 
В сборке от 29 мая симптомы исчезли, спасибо.

Небольшая специфическая проблема с Account[Equity|Margin|FreeMargin] в торговле все же осталась.

После успешного OrderSend() на инстант ордер OrderTotal() немедленно возвращает значение, увеличившееся на единицу, что очень даже правильно.
А вот параметры счета, возвращаемые тремя функциями выше, остаются неизменными в течение какого-то времени.

Почему они не обновляются мгновенно наряду с информацией об открытых ордерах?
RefreshRates() после OrderSend() не помогает.

Помогает конструкция вида
    if (TimeOut > 0)
        Sleep(TimeOut);
    RefreshRates();


Вопрос.
Как обеспечить обновление параметров счета после успешной покупки/продажи (по аналогии с RefreshRates() после неудавшегося открытия ордера)?
Если только таймаутом, то какой таймаут гарантирует обновление данных?

Спасибо.

 
Ошибка найдена. В ближайшем билде, обновлённые данные торгового счёта будут доступны немедленно после открытия позиции.
Спасибо за помощь
 
Похоже, где-то, все-таки, ошибка округления выскакивает:
2006.06.21 11:11:17	2005.11.03 13:00  EURUSD,M1: OrderSend error 134 not enough money, op 0, 1.204/1.2042, price 1.2042, sl/tp 1.2014/1.2051, lots 1.1
2006.06.21 11:11:17	2005.11.03 13:00  Tester: PrevBalance: 3610.70, PrevPL: -160.70, PrevEquity 3450.00, PrevMargin: 2350.00, NewMargin: 3450, FreeMargin: -0.00


Строка

        lots = NormalizeDouble(lots, 2);

перед OrderSend() есть.
Сборка 194.16.06.06.


Причина обращения: