Скачать MetaTrader 5
Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий
Статьи помогут закрепить твои знания. Заходи и читай!
4x4ever
439
4x4ever 2013.12.11 10:30 

Помогите разобратся, наблюдаю некую странную хрень и не пойму - баг в МТ4 (тестере?) или в моем коде? Наверное в коде но если судить по Print-ам вроде баг...


Открываю ордер. Проверяю OrderTicket --> 1.

Через один день закрываю.

OrderTicket опять таки 1, что правильно.

Теперь хочу взять OrderOpenTime и OrderCloseTime с только-что закрытого ордера.

OrderOpenTime дает верное время открытия ордера и GetLastError() = 0.

OrderCloseTime однако возвращает 0. Интересно что GetLastError() тоже дает 0 тоесть ошибки нету а время 1970 год??


В доках написано что единственная возможная ошибка в OrderCloseTime есть ERR_NO_ORDER_SELECTED (4105).

(MQL4 Reference - Program Run - Runtime errors)


У кого есть идеи почему так получается: ордер закрыт успешно, OrderSelect закрытый ордер нашел, ошибки от OrderCloseTime тоже нет а в результате фигня?


Вот сорс:

//+------------------------------------------------------------------+
//|                                                 TestMaxDD-EA.mq4 |
//|                                                               AV |
//|                                                                  |
//+------------------------------------------------------------------+
#property copyright "AV"
#property link      ""

#include <stderror.mqh>
#include <stdlib.mqh>

#define MAGIC       20131211
#define ALL_PERIODS 9

#define DONT_TRADE_MID_PERIOD   1
#define DONT_TRADE_BAD_t0       2
#define DONT_TRADE_AT_ALL       3

#define ERR_IN_PROG         -1111111

// relation between quoted, base and home currencies
#define QH      1
#define HB      2
#define QBBH    3
#define QBHB    4

//--- input parameters
extern double Lots              = 0.1;
extern double MaximumRisk       = 0.02;
extern double DecreaseFactor    = 3;
extern bool   TestingBuy        = true;

extern string str_t_open        = "2013.12.03 00:00"; // simulated time of Open
extern string str_t_close       = "2013.12.04 00:00"; // simulated time of Close

datetime    tm_open;    // StrToTime( str_t_open )
datetime    tm_close;   // StrToTime( str_t_close )

string allowed_pairs[] = { "EURUSD", "GBPUSD", "USDJPY", "EURGBP", "EURJPY", "GBPJPY", "USDCHF", "EURCHF", "USDCAD", "AUDUSD", "AUDJPY", "AUDCAD", "" };

double POINT;   // MODE_POINT of pair used in chart
double DIGITS;  // MODE_DIGITS of pair used in chart

string crcy_quot;
string crcy_base;
string crcy_home;
string symb_ref;

double sprd;
double price_of_pair_now;   // needed when calculating profit on a cross pair
double pips = 0;            // the trade move, e.g. 0.00123 on a EURUSD with 5pt broker
double lots_optimized;

int conv_code = 0;  // see the QH...QB defines above
int op = OP_BUY;    // depends on TestingBuy

int     order_close_time_success = 0;
bool    expert_crit_error = false;

/* Unused in this EA which is for testing only
int         allowed_timeframes_key[ALL_PERIODS] = { PERIOD_M1, PERIOD_M5, PERIOD_M15, PERIOD_M30, PERIOD_H1, PERIOD_H4, PERIOD_D1, PERIOD_W1, PERIOD_MN1 };
bool        allowed_timeframes_val[ALL_PERIODS] = { false,     true,      false,      false,      true,      false,     true,      false,     false };
string      allowed_timeframes_str[ALL_PERIODS] = { "1 min",   "5 min",   "15 min",   "30 min",   "Hourly",  "4 hr",    "Daily",   "Weekly",  "Monthly" };
*/

//+------------------------------------------------------------------+
//| expert deinitialization function                                 |
//+------------------------------------------------------------------+
int deinit()
{
    return( 0 );
}

//+------------------------------------------------------------------+
//| Check if there are open positions                                         |
//+------------------------------------------------------------------+
int CalculateCurrentOrders( string symbol )
{
    int buys = 0, sells = 0;

//----
    for( int i = 0; i < OrdersTotal(); i++ )
    {
        if( OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false )
        {
            break;
        }

        if( OrderSymbol() == Symbol() && OrderMagicNumber() == MAGIC )
        {
            if( OrderType() == OP_BUY )
            {
                buys++;
            }

            if( OrderType() == OP_SELL )
            {
                sells++;
            }
        }
    }

//---- return num orders
    if( buys > 0 )
    {
        if( sells > 0 )
        {
            Alert( "ERROR: ", buys, " Buy and ", sells, " Sell orders open at the same time! Only one type should be open!" );
            expert_crit_error = true;
        }
        else
        {
            return( buys );
        }
    }
    else
    {
        return( -sells );
    }

    return( 0 );
}

//+------------------------------------------------------------------+
//| Calculate optimal lot size                                       |
//+------------------------------------------------------------------+
double LotsOptimized()
{
    double  lot = Lots;
    int     orders = HistoryTotal();   // history orders total
    int     losses = 0;                // number of losses orders without a break
//---- select lot size
    lot = NormalizeDouble( AccountFreeMargin() * MaximumRisk / 1000.0, 1 );

//---- calcuulate number of losses orders without a break
    if( DecreaseFactor > 0 )
    {
        for( int i = orders - 1; i >= 0; i-- )
        {
            if( OrderSelect( i, SELECT_BY_POS, MODE_HISTORY ) == false )
            {
                Print( "Error in history!" );
                break;
            }

            if( OrderSymbol() != Symbol() || OrderType() > OP_SELL )
            {
                continue;
            }

            //----
            if( OrderProfit() > 0 )
            {
                break;
            }

            if( OrderProfit() < 0 )
            {
                losses++;
            }
        }

        if( losses > 1 )
        {
            lot = NormalizeDouble( lot - lot * losses / DecreaseFactor, 1 );
        }
    }

    if( lot < MarketInfo( Symbol(), MODE_MINLOT ) )
    {
        lot = MarketInfo( Symbol(), MODE_MINLOT );
    }

    return( lot );
}

//+------------------------------------------------------------------+
//| Check for open order conditions                                  |
//+------------------------------------------------------------------+
int CheckForOpen()
{
    int    ticket;

//---- go trading only for first tiks of new bar
    if( Volume[0] > 1 )
    {
        return;
    }

//---- open sell conditions
    if( Signal_Sell() )
    {
        lots_optimized = LotsOptimized();
        ticket = OrderSend( Symbol(), OP_SELL, lots_optimized, Bid, 3, 0, 0, "", MAGIC, 0, Red );
        PostSellFunc( ticket );
    }
//---- open buy conditions
    else if( Signal_Buy() )
    {
        lots_optimized = LotsOptimized();
        ticket = OrderSend( Symbol(), OP_BUY, lots_optimized, Ask, 3, 0, 0, "", MAGIC, 0, Blue );
        PostBuyFunc( ticket );
    }

//----
    return( ticket );
}

//+------------------------------------------------------------------+
bool CheckChartPairAllowed()
{
    int i = 0;

    while( StringLen( allowed_pairs[i] ) > 0 )
    {
        if( allowed_pairs[i] == Symbol() )
        {
            return( true );
        }

        i++;
    }

    return( false );
}

//+------------------------------------------------------------------+
//  Finds a reference pair which can be used to calculate the profit on
//  a pair which does not use the AccountCurrency() currency. Example:
//      EA dropped into EURJPY and HOME currency is USD. In this case
//      this function will return "USDJPY" as the reference pair.
//+------------------------------------------------------------------+
string FindRefPair()
{
    int     err = 0, err2 = 0;
    string  base_home = StringConcatenate( crcy_base, crcy_home );
    string  home_base = StringConcatenate( crcy_home, crcy_base );

    Print( "IsTesting : ", IsTesting() );
//        sprd = MarketInfo( base_home, MODE_SPREAD ) * MarketInfo( base_home, MODE_POINT );
    sprd = MarketInfo( base_home, MODE_SPREAD );
    err = GetLastError();
    Print( "Error 1 is ", err );
    sprd = sprd * MarketInfo( base_home, MODE_POINT );
    err = GetLastError();
    Print( "Error 2 is ", err );
    Print( "Trying ", base_home, " ...  MktInfo( SPREAD ) is: ", sprd, " Error : ", err );

    if( 0 == err )
    {
        return( base_home );
    }
    else if( ERR_UNKNOWN_SYMBOL == err )
    {
        sprd = MarketInfo( home_base, MODE_SPREAD );
        err2 = GetLastError();
        Print( "Error 3 is ", err2 );
        sprd = sprd * MarketInfo( home_base, MODE_POINT );
        err2 = GetLastError();
        Print( "Error 4 is ", err2 );
        Print( "Trying ", home_base, " ...  MktInfo( SPREAD ) is: ", sprd, " Error : ", err2 );

        if( ERR_FUNC_NOT_ALLOWED_IN_TESTING == err2 )
        {
            sprd = 0.0005;
            Print( "1.Simulated SPREAD for ", home_base, " is: ", sprd );
        }

        return( home_base );
    }
    else
    {
        sprd = 0.0005;
        Print( "2.Simulated SPREAD for ", base_home, " is: ", sprd );
    }

    return( base_home );
}

//+------------------------------------------------------------------+
double CalcProfitInHomeCurrency( double move )
{
    double LCM; //Lots * Contract_size * Move

    LCM = MarketInfo( Symbol(), MODE_LOTSIZE ) * lots_optimized * move;

    Print( "Lots = ", DoubleToStr( lots_optimized, DIGITS ) );
    Print( "CS   = ", MarketInfo( Symbol(), MODE_LOTSIZE ) );
    Print( "Move = ", DoubleToStr( move, DIGITS ) );
    Print( "LCM  = ", DoubleToStr( LCM, DIGITS ) );

    double numr, dnom; // conversion coefficients get to Home currency

    switch( conv_code )
    {
        case QH:
            numr = 1.0;
            dnom = 1.0;
            break;
        case HB:
            numr = 1.0;
            dnom = price_of_pair_now;
            break;
        case QBHB:
            numr = 1.0;
            dnom = iClose( symb_ref, 0, 0 );
            break;
        case QBBH:
            numr = iClose( symb_ref, 0, 0 );
            dnom = 1.0;
            break;
        default:
            return( ERR_IN_PROG );
    }

    return( LCM * numr / dnom );
}

//+-----------------------------------------------------------------+
// Simulate a BUY signal - at tm_open as entered by user
//+-----------------------------------------------------------------+
bool Signal_Buy()
{
    datetime t_now = TimeCurrent();

    if( TestingBuy &&
        t_now >= tm_open &&
        t_now <  tm_close )
    {
        Print( "Signal BUY: Ask=", DoubleToStr( Ask, DIGITS ),
               " (bid ", DoubleToStr( Bid, DIGITS ), ")" );
        return( true );
    }

    return( false );
}

//+-----------------------------------------------------------------+
// Simulate a SELL signal - at tm_open as entered by user
//+-----------------------------------------------------------------+
bool Signal_Sell()
{
    datetime t_now = TimeCurrent();

    if( !TestingBuy &&
        t_now >= tm_open &&
        t_now <  tm_close )
    {
        Print( "Signal SELL: Bid=", DoubleToStr( Bid, DIGITS ),
               " (ask ", DoubleToStr( Ask, DIGITS ), ")" );
        return( true );
    }

    return( false );
}

//+-----------------------------------------------------------------+
void    PostBuyFunc( int ticket )
{
    int ticket2 = OrderTicket();
    int err = GetLastError();

    if( err != 0 )
    {
        Print( "BUY: ERROR ", err, " from OrderTicket" );
    }
    else
    {
        Print( "Ticket of opened BUY order : ", ticket, " should match ", ticket2 );
    }
}

//+-----------------------------------------------------------------+
void    PostSellFunc( int ticket )
{
    int ticket2 = OrderTicket();
    int err = GetLastError();

    if( err != 0 )
    {
        Print( "SELL: ERROR ", err, " from OrderTicket" );
    }
    else
    {
        Print( "Ticket of opened SELL order : ", ticket, " should match  ", ticket2 );
    }
}

//+-----------------------------------------------------------------+
// Simulate a CLOSE signal - at tm_close as entered by user
//+-----------------------------------------------------------------+
bool Signal_Close()
{
    return( op != -1 && TimeCurrent() >= tm_close );
}

//+------------------------------------------------------------------+
// TODO: testing func, REMOVE!!
//+------------------------------------------------------------------+
void TestOrderCloseTime()
{
    if( order_close_time_success >= 0 )
    {
        return;
    }

    int err = 0;
    int ticket = -2;
    datetime dt_o, dt_c;

    ticket = OrderTicket();
    err = GetLastError();

    if( err != 0 )
    {
        Print( "ERROR ", err, " from OrderTicket" );
    }
    else
    {
        Print( "Ticket of closed order : ", ticket );
    }

    dt_o = OrderOpenTime();
    err = GetLastError();

    if( err != 0 )
    {
        Print( "ERROR ", err, " from OrderOpenTime" );
    }

    dt_c = OrderCloseTime();
    err = GetLastError();

    if( err != 0 )
    {
        Print( "ERROR ", err, " from OrderCloseTime" );
    }

    if( err == 0 && dt_c != 0 )
    {
        order_close_time_success = 1;
    }
    else
    {
        order_close_time_success--;
    }

    string prefx;

    if( order_close_time_success == -2 )
    {
        prefx = "Trying";
    }
    else
    {
        prefx = "Re-trying";
    }

    Print( prefx, " order Open/Close times: ", TimeToStr( dt_o ),
           " / ", TimeToStr( dt_c ) );
}

//+------------------------------------------------------------------+
// @pre An was selected by OrderSelect and then closed by OrderClose
//+------------------------------------------------------------------+
void    PostCloseFunc()
{
    order_close_time_success = -1; // begin attempts to get OrderCloseTime
    TestOrderCloseTime();
}

//+------------------------------------------------------------------+
bool TryCloseCurrentOrder()
{
    int     error = 0;
    bool    ok = false;
    double  prc;
    string  str_op;
    int     retries = 3;

    if( OrderType() == OP_BUY )
    {
        prc = Bid;
        str_op = "BUY";
    }
    else if( OrderType() == OP_SELL )
    {
        prc = Ask;
        str_op = "SELL";
    }
    else
    {
        Print( " ??? no order selected ???" );
        return( false );
    }

    ok = OrderClose( OrderTicket(), OrderLots(), prc, 3, White );

    while( !ok && retries > 0 )
    {
        error = GetLastError();

        if( ERR_REQUOTE == error )
        {
            Print( "CLOSE-", str_op, ": Error " + error );
            retries--;
            //---- 10 seconds wait
            Sleep( 10000 );
            //---- refresh price data
            RefreshRates();
            ok = OrderClose( OrderTicket(), OrderLots(), prc, 3, White );
        }
        else
        {
            break;
        }
    }

    if( ok )
    {
        op = -1;
        price_of_pair_now = OrderClosePrice();

        if( OP_BUY == OrderType() )
        {
            pips = OrderClosePrice() - OrderOpenPrice();
        }
        else
        {
            pips = OrderOpenPrice() - OrderClosePrice();
        }
    }

    return( ok );
}

//+------------------------------------------------------------------+
// @pre  A successful OrderSelect
//+------------------------------------------------------------------+
bool CheckAndTryCloseCurrentOrder()
{
    if( Signal_Close() )
    {
        return( TryCloseCurrentOrder() );
    }

    return( false );
}

//+------------------------------------------------------------------+
//| Go thru all orders and check for close conditions                |
//+------------------------------------------------------------------+
int CheckAllOrdersForClose()
{
    bool ok = true;

    if( Volume[0] > 1 )
    {
        return;    //---- go trading only for first ticks of new bar
    }

    for( int i = 0; i < OrdersTotal(); i++ )
    {
        if( OrderSelect( i, SELECT_BY_POS, MODE_TRADES ) == false )
        {
            break;
        }

        if( OrderMagicNumber() != MAGIC || OrderSymbol() != Symbol() )
        {
            continue;
        }

        ok = CheckAndTryCloseCurrentOrder();

        if( !ok )
        {
            return( -1 );
        }
        else
        {
            PostCloseFunc();
            return( 1 );
        }
    }

    return( 0 );
}

//+------------------------------------------------------------------+
int DontTrade()
{
    if( !IsTradeAllowed() )
    {
        Comment( WindowExpertName(), ": Expert Disabled." );
        return( DONT_TRADE_AT_ALL );
    }
    else if( !CheckChartPairAllowed() )
    {
        Print( "Unsupported PAIR! Choose another one.." );
        return( -1 );
    }
    else if( Volume[0] < 1 )
    {
        Comment( WindowExpertName(), ": Waiting for start of new period." );
        return( DONT_TRADE_MID_PERIOD );
    }


    Comment( WindowExpertName(), ": Trading..." );
    return( false );
}

//+------------------------------------------------------------------+
//| expert initialization function                                   |
//+------------------------------------------------------------------+
int init()
{
    DIGITS  = MarketInfo( Symbol(), MODE_DIGITS );
    Print( "DIGITS for ", Symbol(), " is ", DIGITS );
    POINT   = MarketInfo( Symbol(), MODE_POINT );
    Print( "POINT for ", Symbol(), " is ", DoubleToStr( POINT, DIGITS ) );

    tm_open     = StrToTime( str_t_open );
    tm_close    = StrToTime( str_t_close );

    conv_code = 0;
    sprd = -1;
    symb_ref = "";
    crcy_quot = StringSubstr( Symbol(), 0, 3 );
    crcy_base = StringSubstr( Symbol(), 3, 3 );
    crcy_home = AccountCurrency();

    Print( "BASE currency is ", crcy_base );
    Print( "QUOTED currency is ", crcy_quot );
    Print( "HOME currency is ", crcy_home );

    if( TestingBuy )
    {
        op = OP_BUY;
    }
    else
    {
        op = OP_SELL;
    }

    if( crcy_home == crcy_quot )
    {
        conv_code += HB;
    }
    else if( crcy_home == crcy_base )
    {
        conv_code += QH;
    }

    if( conv_code > 2 )
    {
        Print( " WTF ?!?!" );
        return( -1 );
    }
    else if( 0 == conv_code )
    {
        symb_ref = FindRefPair();

        if( symb_ref == "" )
        {
            Print( "Can not convert ", Symbol(), " to the home currency! ", crcy_home );
            return( -1 );
        }

        if( StringSubstr( symb_ref, 0, 3 ) == crcy_home )
        {
            conv_code = QBHB;
        }
        else if( StringSubstr( symb_ref, 3, 3 ) == crcy_home )
        {
            conv_code = QBBH;
        }

        double prc_ref = iClose( symb_ref, 0, 0 );
        int retries = 4;

        while( prc_ref == 0 && retries > 0 )
        {
            Sleep( 1000 );
            RefreshRates();
            prc_ref = iClose( symb_ref, 0, 0 );
            retries--;
        }

        Print( "Using ",
               symb_ref,
               " {",
               DoubleToStr( iClose( symb_ref, 0, 0 ), MarketInfo( symb_ref, MODE_DIGITS ) ),
               "} to calculate profit in ",
               crcy_home,
               " on ",
               Symbol() );
    }

    return( 0 );
}

//+------------------------------------------------------------------+
//| Start function                                                   |
//+------------------------------------------------------------------+
int start()
{
    int dont_trade_code;

//---- check for critical errors and disable opening of new orders if yes
    if( expert_crit_error )
    {
        dont_trade_code = DONT_TRADE_AT_ALL;
    }
    else
    {
        //---- check for trading allowed, enough history collected, start of bar, bad t0, etc
        dont_trade_code = DontTrade();
    }

//---- Check for open orders
    if( CalculateCurrentOrders( Symbol() ) == 0 )
    {
        if( 0 == dont_trade_code )
        {
            CheckForOpen();
        }

        if( Volume[0] <= 1 )
        {
            TestOrderCloseTime();
        }
    }
    else
    {
        CheckAllOrdersForClose();
    }

    if( pips != 0 )
    {
        double profit_home_crcy = CalcProfitInHomeCurrency( pips );
        Print( "Profit in ", crcy_home, " is : ", profit_home_crcy );
        Print( "Swap ", OrderSwap() );
        pips = 0;
    }

    return( 0 );
}
//+------------------------------------------------------------------+
TarasBY
1742
TarasBY 2013.12.11 10:35  
Прежде чем запрашивать информацию об ордере, его нужно ВЫДЕЛИТЬ (OrderSelect()). А искать его (в данном случае) в списке OrdersHistoryTotal().
Vitalie Postolache
11023
Vitalie Postolache 2013.12.11 10:37  

Два несочетающихся понятия: Мультивалютник и Тестер...

Только реал-тайм.

4x4ever
439
4x4ever 2013.12.11 10:39  

Пробовал на:

EURUSD H1

Open prices / Ctrl Pts / Every tick

From: 2013.12.06 To: 2013.12.11

Visual (ON and OFF)


Остальные параметры (Expert Properties):

TestingBuy: true

str_t_open: 2013.12.09 00:00

str_t_close: 2013.12.10 00:00


Lots, MaxRisk, DecreaseFactor оставьте как есть... Visual (on/OFF) и Model (Open/CtrlPts/EveryTick) ничего не меняют...
4x4ever
439
4x4ever 2013.12.11 10:46  
evillive:

Два несочетающихся понятия: Мультивалютник и Тестер...

Только реал-тайм.

...не мултьтивалютник в тестере - просто часть кода так выглядит так как взята из EA который деиствительно мульти. Но в примере вверху я просто пытаюсь проверить совпадают ли мои калькуляции MaximalDrawDown с тем что показывает Report. Как я уже сказал - тест сделан на EURUSD, единственый ордер - тоже на EURUSD и OrderCloseTime проверяю на том же ордере...

4x4ever
439
4x4ever 2013.12.11 10:51  
TarasBY:
Прежде чем запрашивать информацию об ордере, его нужно ВЫДЕЛИТЬ (OrderSelect()). А искать его (в данном случае) в списке OrdersHistoryTotal().

Я думал что выделен, по крайней мере там где TestOrderCloseTime вызвана из PostCloseFunc он точно выделен... так как только что успешно прошел OrderClose. Но может бъть там не работает потому что там еще слишком рано - ордер только что закрыт и еще не считаеся как History?

A там где TestOrderCloseTime вызвана из start возможно вы правы и он не выделен... спасибо за подсказку, щас проверю.

TarasBY
1742
TarasBY 2013.12.11 11:03  
4x4ever:

Я думал что выделен, по крайней мере там где TestOrderCloseTime вызвана из PostCloseFunc он точно выделен... так как только что успешно прошел OrderClose. Но может бъть там не работает потому что там еще слишком рано - ордер только что закрыт и еще не считаеся как History?

A там где TestOrderCloseTime вызвана из start возможно вы правы и он не выделен... спасибо за подсказку, щас проверю.

Вы выделяете ордер в списке открытых ордеров и после его закрытия (этого ордера) ищите его в "списке живых". Для проверки нужно выделять так:

    int li_Ticket = OrderTicket(); // Ticket закрытого ордера.
    OrderSelect (li_Ticket, SELECT_BY_TICKET, MODE_HISTORY);
можно перед проверкой сделать небольшую паузу для надёжности.
4x4ever
439
4x4ever 2013.12.11 11:16  
TarasBY:

Вы выделяете ордер в списке открытых ордеров и после его закрытия (этого ордера) ищите его в "списке живых". Для проверки нужно выделять так:

можно перед проверкой сделать небольшую паузу для надёжности.

ааа... понял. Спасибо большое :)

То что особенно вводит в заблуждение, однако, ето то что GetLastError ошибки не выдает.... и к тому же и TestOrderOpenTime проходит и результат дает правильный... если бы там ошибки были то было бы гороздо логичнее. И легче найти свою ошибку.

4x4ever
439
4x4ever 2013.12.11 12:13  
Работает. Крррасота... :)
Alexey Subbotin
4999
Alexey Subbotin 2013.12.11 17:20  
Вот учитесь, учитесь: человек своими руками сделал, где-то ошибся, попытался сам разобраться в проблеме(!), прочитал доки(!!), и только потом задал конкретный, не допускающий двусмысленности вопрос, приведя при этом код прграммы(!!!). На что закономерно получил вразумительный ответ и, как следствие, решение. Расцеловал бы.
4x4ever
439
4x4ever 2013.12.12 15:12  

и код то у меня ровненький, не в раскоряку(!!!!) ;) ... ну куда ни посмотри - отличник я! Только вот не работает, хаха... (впрочем уже работает)


Целовать дам только после того как доки доведете до кондиции! :P

...или хотя бы чтоб error во всех функциях был как описано. Я так думаю - активность в форуме спала бы по крайней мере на 50% если бы доки были получше. Ясно конечно что приоритетов много и доки не номер 1, но всетаки - ох как хотелось бы... Я вот МТ4 занимался сериозно лет 5-6 назад, потом приостановил (и у меня другие приоритеты были :)), думаю дай ка посмотрю как там да что... Улучшений наверное немало за ети годы но увы - не в документации... жаль... Хорошо что добрые люди на форуме не перевелись, "а то кто снес бы униженья века..."

/
Авторизуйтесь или зарегистрируйтесь, чтобы добавить комментарий