MetaTrader 4. Build 160. - страница 3

 
А так я бы во время дрэга скрипта на график держал бы нажатой нужную клавишу и не надо было бы вводить инпут параметр.

Если сможем, то добавим функцию, которая сможет выдавать код клавиши, нажатой во время операции drag'n'drop.

А еще прикольнее была бы возможность поместить подобные скрипты на отдельную панель в виде кнопок, задать надписи на кнопках и тащить их на график!

Да, панель быстрого вызова будет. Как созреем до реализации - сразу сделаем. Но пока закончим тестер.
 
Renat, объясните пожалуйста как взаимодействуют в MT4
входящий поток данных и функция start() индикатора/эксперта.

Котировки приходят по запросу или как-то иначе ?
Если по запросу, то в какой момент он выполняется ?

Очередная котировка приходит независимо от работы
индикатора/эксперта или только после отработки его start() ?

При инициализации эксперта/индикатора вызывается функция init(), а снятии deinit().

Индикатор вызывается на перерасчет когда:
- история сильно изменилась (в первом отображении тоже)
- по приходу ценового тика по этому инструменту
- если бы изменен MQL4 код (например, в редакторе)

Котировки приходят и накапливаются в базах независимо от всего остального, то есть, прием котировок идет в отдельном потоке. После приема котировки посылается уведомление на перерасчет индикаторов. Соответственно, если индикаторы считаются долго (функция start()), то они будут пропускать некоторые уведомления о пересчете. Индикаторы надо всегда стараться писать по экономному с учетом ранее просчитанных баров.

А эксперт может запускать функцию start() только после прихода нового тика. Если эксперт еще не завершил работу, то уведомление пропускается. Эксперт теперь может тратить неограниченное количество времени на себя и использовать функцию Sleep(). Чтобы избежать работы со старыми историческими данными при длительных задержках в эксперте, мы рекомендуем использовать (например не чаще чем раз в секунду) функцию RefreshRates( ).
 
Если сможем, то добавим функцию, которая сможет выдавать код клавиши, нажатой во время операции drag'n'drop.


имхо, лучше бы не во время drag'n'drop, а просто при работе скрипта. Тогда в start() можно было бы поймать нажатую клавишу.

эту же возможность еще лучше было бы использовать при работе экспертов. Например, работает советник, жмем определенную клавишу и советник нам выдает результаты какого нибудь расчета, жмем другую - еще что-нибудь делает.
Хорошо было бы при этом ловить координаты мыши.

Так, "Остапа понесло" (с) :)
 
имхо, лучше бы не во время drag'n'drop, а просто при работе скрипта. Тогда в start() можно было бы поймать нажатую клавишу.
Так, "Остапа понесло"

Точно понесло...
 
Некоторые ошибки и замечания по языку.

1. Некорректно работает ArrayCopy для двумерных массивов. Код копирующий первые (total-1) элементов двумерного масива в конец этого же масива
   int i, total = 3;
   int data[][2];
   ArrayResize(data, total);
   for (i = 0; i < total; i++) {
   	data[i][0] = i;
   	data[i][1] = (i + 10);
   }
   
   for (i = 0; i < total; i++)
   	Print("data[" + i + "]="+ data[i][0] + ", " + data[i][1]);
   	 
    ArrayCopy(data, data, 1, 0, total - 1);
	
   Print("-------");
   for (i = 0; i < total; i++)
   	Print("data[" + i + "]="+ data[i][0] + ", " + data[i][1]);



генерирует вывод

data[2]=2, 12
data[1]=10, 11
data[0]=0, 0
-------
data[2]=2, 12
data[1]=1, 11
data[0]=0, 10



хотя после ArrayCopy должно быть:

data[2]=1, 11
data[1]=0, 10
data[0]=0, 10



Если вместо ArrayCopy копировать руками:

	for (i = total-2; i >= 0; i--) {
  		data[i+1][0] = data[i][0];
  		data[i+1][1] = data[i][1];
	}    



все будет правильно. Также непонятно как проинициализировать двумерный массив при объявлении

int data2[3][2] = {{1, 10}, {2, 11}, {3, 12}};



компилятором не понимается.

 
2. При объявлении функции с параметром по умолчанию в БИБЛИОТЕКЕ и импорте этой библиотеки в эксперте, к функции обратиться можно, но с указанием всех параметров.
string orderStr(bool closed=false) {
}



к этой функции нельзя обратиться как orderStr(), а только как orderStr(state). Если функция юудет объявлена в эксперте - обращайся как хочешь.

3. Highest и Lowest возвращают 1.0 или 2.0, а не реальные цены:

	double lowestPrice = Lowest(NULL, 0, MODE_LOW, 3);
	double highestPrice = Highest(NULL, 0, MODE_HIGH, 3);
 
2. При объявлении функции с параметром по умолчанию в БИБЛИОТЕКЕ и импорте этой библиотеки в эксперте, к функции обратиться можно, но с указанием всех параметров.
string orderStr(bool closed=false) {}


к этой функции нельзя обратиться как orderStr(), а только как orderStr(state). Если функция юудет объявлена в эксперте - обращайся как хочешь.


об этом говорили - типа так должно быть =)
 
ArrayCopy(data, data,

Я что-то сомневаюсь, что такое копирование массива самого в себя разрешено...
Слава в понедельник точнее скажет.

При объявлении функции с параметром по умолчанию в БИБЛИОТЕКЕ и импорте этой библиотеки в эксперте, к функции обратиться можно, но с указанием всех параметров.

Это правильно - так и задумано.

Highest и Lowest возвращают 1.0 или 2.0, а не реальные цены:

Эти функции возвращают индексы, а не сами значения. К сожалению, в описании ошибка.
Спасибо что указали на нее.
 
4. Непонятно почему не инициализированы поля Order'a после его удачной отправки или закрытия. Я сделал функцию orderStr, которая возвращает описание ордера:
string orderStr(bool closed=false) {
	double lots  = OrderLots();
    string orderStr = "#" + OrderTicket() + " " + 
    orderTypeStr(OrderType()) + " "  + 
    p2Str(OrderLots()) + " (" + OrderLots() + ") " + OrderSymbol() + 
    ", open=" + quoteStr(OrderOpenPrice()) + 
    ", sl=" + quoteStr(OrderStopLoss()) + 
    ", tp=" + quoteStr(OrderTakeProfit()) + 
    ", comm=" + OrderComment() + 
    ", exp=" + TimeToStr(OrderExpiration()) + 
    ", openTime=" + TimeToStr(OrderOpenTime());
    
    if (closed) {
        double points = (OrderClosePrice() - OrderOpenPrice())/Point;
        if (OrderType() == OP_SELL)
            points = -points;
            
        orderStr = "Profit=" + quoteStr(OrderProfit()) + 
        " (" + DoubleToStr(points, 0) + " points) " +  orderStr + 
        ", closeTime=" + TimeToStr(OrderCloseTime()) + 
        ", close=" + quoteStr(OrderClosePrice()) +
        ", swap=" + quoteStr(OrderSwap());
    }
    
    return (orderStr);
}

string p2Str(double value) {
    return (DoubleToStr(value, 2));
}



Но после открытия ордера:

int sendOrder(int type, double price, double slPrice, double tpPrice) {
    log("Trying send order " + 
    orderTypeStr(type) + " " + Lots + " " + Symbol() + 
    ", price=" + quoteStr(price) + ", slPrice=" + quoteStr(slPrice) + 
    ", tpPrice=" + quoteStr(tpPrice) + ", slippage=" + Slippage + 
    ", Bid=" + quoteStr(Bid) + ", Ask=" + quoteStr(Ask));

    int ticket = OrderSend(Symbol(), type, Lots, price, 
    Slippage, slPrice, tpPrice, ORDER_PREFIX, 16384, 0, Red);
    if (ticket < 0) {
        int err = logLastError("OrderSend");
        switch (err) {
            case 134: setMarketEnter(-1); break; // not enough money
            case 135: RefreshRates(); break; // prices changed
        }
    }
    else {
        log("Order sent: " + orderStr(false));
        setMarketEnter(-1);
    }

    flushLog();
    return (ticket);    
}



получаем следующее описание в логе:

2005.03.25 17:19:55 Trying send order SELL 0.10000000 EURUSD, price=1.2951, slPrice=1.2961, tpPrice=1.2921, slippage=1, Bid=1.2951, Ask=1.2955
2005.03.25 17:19:59 Order sent: #0 SELL 8.14 (0.00000000) , open=1.2955, sl=1.2955, tp=1.2955, comm=, exp=1970.01.01 00:00, openTime=1970.01.01 00:00

Как видим ticket не инициализирован, время открытия тоже, с OrderLots() - вообще цирк.

После закрытии ордера таже картина:
2005.03.25 18:15:25 Trying close order #401619, price=1.29540000, volume=0.10000000, slippage=1, Bid=1.2950, Ask=1.2954
2005.03.25 18:15:27 Order closed Profit=1.2954 (0 points) #0 BUY 10.55 (0.00000000) , open=1.2954, sl=1.2954, tp=1.2954, comm=, exp=1970.01.01 00:00, openTime=1970.01.01 00:00, closeTime=1970.01.01 00:00, close=1.2954, swap=1.2954


Почему некоторые поля не инициализированы, swap вообще совпадает с close, а OrderLots() - рандомизирован?

 
О библиотеках:
для скорости я рекомендую (по возможности) включать код библиотеки в саму программу через #include, а не через импорт #import. Импорт библиотек через #import - это серьезные накладные расходы, сопоставимые с COM. С помощью #include исходный код библиотеки встраивается непосредственно в основной код, тем самым получается экономия на вызовах функций.