vdemydiuk / mtapi

MetaTrader API (terminal bridge)
http://mtapi4.net/
MIT License
523 stars 282 forks source link

SendCommand: Client is not connected Error #74

Open cia76 opened 6 years ago

cia76 commented 6 years ago

Сделал простую программу для тестов MtApi. После подключения раз в 5 секунд по таймеру получаю OrdersTotal(). Сначала все принимается корректно. Примерно через минуту работы получаю:

MtApi.MtConnectionException: Client is not connected. ---> System.ServiceModel.CommunicationException: Client is not connected. в MTApiService.MtClient.SendCommand(Int32 commandType, ArrayList parameters, Dictionary2 namedParams, Int32 expertHandle) в MtApi.MtApiClient.SendCommand[T](MtCommandType commandType, ArrayList commandParameters, Dictionary2 namedParams)

Начал искать причину ошибки.

        /// <exception cref="CommunicationException">Thrown when connection failed</exception>
        public MtResponse SendCommand(int commandType, ArrayList parameters, Dictionary<string, object> namedParams, int expertHandle)
        {
            Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count);

            if (IsConnected == false)
            {
                Log.Error("SendCommand: Client is not connected.");
                throw new CommunicationException("Client is not connected.");
            }
...

Проверяется свойство IsConnected

private bool IsConnected => _proxy.State == CommunicationState.Opened;

_proxy класса MtApiProxy. Этот класс наследуется от DuplexClientBase. Тот наследуется от ClientBase. В нем реализовано свойство State:

    public CommunicationState State
    {
        get
        {
            IChannel channel = (IChannel)this.channel;
            if (channel != null)
            {
                return channel.State;
            }
            else
            {
                // we may have failed to create the channel under open, in which case we our factory wouldn't be open
                if (!this.useCachedFactory)
                {
                    return GetChannelFactory().State;
                }
                else
                {
                    return CommunicationState.Created;
                }
            }
        }
    }
vdemydiuk commented 6 years ago

Добрый день. Можно ли вас попросить сбросить мне вашу программу на почту vdemydiuk@gmail.com? Я проверю ее у себя.

cia76 commented 6 years ago

Похоже, ошибка происходит только тогда, когда идет обращение из разных потоков. Я запускаю один поток вначале для получения исторических данных. Затем запускаю таймер раз в 5 секунд для проверки заявок и позиций. Раз в минуту запускается поток обновления исторических данных. Когда каждый поток первый раз обращается к статичному свойству MtApiClient, то получаю ошибку.

Попробую сделать тестовый код, который воспроизводит ошибку. Весь проект слишком сложный, да и не нужен он весь.

vdemydiuk commented 6 years ago

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

cia76 commented 6 years ago

Это понятно. Интересует не состояние соединения, а новые заявки, которые появились за последние 5 секунд. Как я понимаю, в MT4 события постановки заявки/позиции нет.

vdemydiuk commented 6 years ago

Все верно, таких событий нет. Но есть уже готовая реализация TradeMonitor (TimerTradeMonitor, TimeframeTradeMonitor), который выдает ивенты по закрытым и открытим ордерам. Возможно это то, что вам нужно.

cia76 commented 6 years ago

Продолжаю отладку. Перед тем как получаю ошибку, то вижу, что ((System.ServiceModel.ClientBase)(MTApiClient.Client._proxy)).State переходит из статуса Open в статус Close.

channel при этом существует. Вопрос становится таким: При каких обстоятельствах происходит разрыв соединения? Чтобы статус сменился с Open на Close нужно принудительно закрыть канал методом Close();

Пройдя по исходникам, я не нашел ситуаций принудительного разрыва соединения.

vdemydiuk commented 6 years ago

Это очень странная ситуация, когда канал произвольно закрывается. Мне нужно исходники примера с данной проблемой, чтобы воспроизвести ее локально и попробовать определить ее характер.

cia76 commented 6 years ago

Возвращаясь к исходному коду:

        /// <exception cref="CommunicationException">Thrown when connection failed</exception>
        public MtResponse SendCommand(int commandType, ArrayList parameters, Dictionary<string, object> namedParams, int expertHandle)
        {
            Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count);

            if (IsConnected == false)
            {
                Log.Error("SendCommand: Client is not connected.");
                throw new CommunicationException("Client is not connected.");
            }

Зачем выполняется проверка ISconnected? В каком случае может быть ситуация, когда прокси не подключен?

Насчет кода. Если я упрощаю код и вывожу код из своего проекта, то он работает ОК. Поэтому, я постепенно усложняю тестовый вариант до тех пор, пока не поймаю ошибку.

vdemydiuk commented 6 years ago

Проверка выполняется лишь с той целью, что пользователь может случайно сделать вызов функции API без подключения. При всегда правильном вызове функций это проверка немного избыточна.

cia76 commented 6 years ago

Добрый день! Извиняюсь за запоздалый ответ. Если работать с MTApi из нескольких потоков одновременно, то канал, действительно, закрывается через произвольное время. Пришлось решать задачу через управление потоками. Чтобы в текущий момент времени только 1 поток мог работать с MTApi. После этого, описанная мною ошибка, исчезла. Если появится снова, то отпишусь в этой ветке. Благодарю за MTApi!

cia76 commented 6 years ago

Все-таки, ошибка есть. Работаю в одном Task для получения котировок. Пример кода:

    private static readonly ILog Logger = LogManager.GetLogger(typeof(MTWrapper));
    private static readonly MtApiClient MTApiClient = new MtApiClient();

    internal TimeArrayResult Time(string Symbol, ChartPeriod ChartPeriod)
    {
        var result = new TimeArrayResult();
        lock (MTApiClient)
        {
            result.Value = MTApiClient.iTimeArray(Symbol, ChartPeriod);
            result.ErrorId = MTApiClient.GetLastError();
            result.ErrorDescription = MTApiClient.ErrorDescription(result.ErrorId);
        }
        Logger.Debug($"Time {Symbol} {ChartPeriod} Кол-во бар: {result.Value.Length}");
        if (result.ErrorId != 0)
            Logger.Error($"Ошибка Time {result.ErrorId} - {result.ErrorDescription}");
        return result;
    }

Я получаю эти данные в Task раз в 4 часа. Сначала при вызове

result.Value = MTApiClient.iTimeArray(Symbol, ChartPeriod);

новый бар не получаю, но ошибки нет. Такое может быть, т.к. часы на компьютере и в терминале MT4 не синхронизированы. Часы на компьютере чуть спешат. Чтобы получить новый бар, через 10 секунд повторно делаю тот же самый вызов. Получаю:

MtApi.MtConnectionException: Client is not connected. ---> System.ServiceModel.CommunicationException: Client is not connected. в MTApiService.MtClient.SendCommand(Int32 commandType, ArrayList parameters, Dictionary2 namedParams, Int32 expertHandle) в MtApi.MtApiClient.SendCommand[T](MtCommandType commandType, ArrayList commandParameters, Dictionary2 namedParams)

Ошибка происходит на разных компьютерах под Windows 10 со всеми установленными обновлениями.

vdemydiuk commented 6 years ago

Буду пробовать воспроизвести эту проблему со своей стороны. Надеюсь получиться в скором времени ее пофиксить.

cia76 commented 6 years ago

Погонял код прошлую неделю. Если через интервалы от 1 минуты до 1 часа вызывать OrdersTotal и пр. методы работы с заявками, то все ОК.

    internal int OrdersTotal
    {
        get
        {
            int result;
            lock (MTApiClient)
            {
                result = MTApiClient.OrdersTotal();
                Logger.Debug($"Активных заявок {result}");
                int errorId = MTApiClient.GetLastError();
                if (errorId != 0)
                    Logger.Error($"Ошибка OrdersTotal {errorId} - {MTApiClient.ErrorDescription(errorId)}");
            }
            return result;
        }
    }

Когда первый раз вызываю запрос Time/Open/High/Low/Close/Volume, то все также ОК

internal class MTWrapper
{
    private static readonly ILog Logger = LogManager.GetLogger(typeof(MTWrapper));
    private static readonly MtApiClient MTApiClient = new MtApiClient();

    internal DateTime[] Time(string Symbol, ChartPeriod ChartPeriod)
    {
        DateTime[] result;
        lock (MTApiClient)
        {
            result = MTApiClient.iTimeArray(Symbol, ChartPeriod);
            Logger.Debug($"Time {Symbol} {ChartPeriod} Кол-во бар: {result.Length}");
            int errorId = MTApiClient.GetLastError();
            if (errorId != 0)
                Logger.Error($"Ошибка Time {errorId} - {MTApiClient.ErrorDescription(errorId)}");
        }
        return result;
    }

    internal double[] Open(string Symbol, ChartPeriod ChartPeriod)
    {
        double[] result;
        lock (MTApiClient)
        {
            result = MTApiClient.iOpenArray(Symbol, ChartPeriod);
            Logger.Debug($"Open {Symbol} {ChartPeriod} Кол-во бар: {result.Length}");
            int errorId = MTApiClient.GetLastError();
            if (errorId != 0)
                Logger.Error($"Ошибка Open {errorId} - {MTApiClient.ErrorDescription(errorId)}");
        }
        return result;
    }

    internal double[] High(string Symbol, ChartPeriod ChartPeriod)
    {
        double[] result;
        lock (MTApiClient)
        {
            result = MTApiClient.iHighArray(Symbol, ChartPeriod);
            Logger.Debug($"High {Symbol} {ChartPeriod} Кол-во бар: {result.Length}");
            int errorId = MTApiClient.GetLastError();
            if (errorId != 0)
                Logger.Error($"Ошибка High {errorId} - {MTApiClient.ErrorDescription(errorId)}");
        }
        return result;
    }

    internal double[] Low(string Symbol, ChartPeriod ChartPeriod)
    {
        double[] result;
        lock (MTApiClient)
        {
            result = MTApiClient.iLowArray(Symbol, ChartPeriod);
            Logger.Debug($"Low {Symbol} {ChartPeriod} Кол-во бар: {result.Length}");
            int errorId = MTApiClient.GetLastError();
            if (errorId != 0)
                Logger.Error($"Ошибка Low {errorId} - {MTApiClient.ErrorDescription(errorId)}");
        }
        return result;
    }

    internal double[] Close(string Symbol, ChartPeriod ChartPeriod)
    {
        double[] result;
        lock (MTApiClient)
        {
            result = MTApiClient.iCloseArray(Symbol, ChartPeriod);
            Logger.Debug($"Close {Symbol} {ChartPeriod} Кол-во бар: {result.Length}");
            int errorId = MTApiClient.GetLastError();
            if (errorId != 0)
                Logger.Error($"Ошибка Close {errorId} - {MTApiClient.ErrorDescription(errorId)}");
        }
        return result;
    }

    internal double[] Volume(string Symbol, ChartPeriod ChartPeriod)
    {
        double[] result;
        lock (MTApiClient)
        {
            result = MTApiClient.iVolumeArray(Symbol, ChartPeriod);
            Logger.Debug($"Volume {Symbol} {ChartPeriod} Кол-во бар: {result.Length}");
            int errorId = MTApiClient.GetLastError();
            if (errorId != 0)
                Logger.Error($"Ошибка Volume {errorId} - {MTApiClient.ErrorDescription(errorId)}");
        }
        return result;
    }
}

Вызовы идут из Task вот так:

private readonly MTWrapper _mtWrapper = new MTWrapper(); // Обертка для связи с терминалом MT4

time = _mtWrapper.Time(Symbol, chartPeriod); open = _mtWrapper.Open(Symbol, chartPeriod); high = _mtWrapper.High(Symbol, chartPeriod); low = _mtWrapper.Low(Symbol, chartPeriod); close = _mtWrapper.Close(Symbol, chartPeriod); volume = _mtWrapper.Volume(Symbol, chartPeriod);

После этих вызовов любой другой вызов через любое время ведет к ошибке:

MtApi.MtConnectionException: Client is not connected. ---> System.ServiceModel.CommunicationException: Client is not connected. в MTApiService.MtClient.SendCommand(Int32 commandType, ArrayList parameters, Dictionary2 namedParams, Int32 expertHandle) в MtApi.MtApiClient.SendCommand[T](MtCommandType commandType, ArrayList commandParameters, Dictionary2 namedParams) --- Конец трассировки внутреннего стека исключений --- в MtApi.MtApiClient.SendCommand[T](MtCommandType commandType, ArrayList commandParameters, Dictionary`2 namedParams)

cia76 commented 6 years ago

Вячеслав, мне отписался наш трейдер. У него возникает та же самая ошибка. Может, сделаем отладку, найдем ошибку и покончим с ней?

cia76 commented 6 years ago

Вячеслав, долго я возился с этой ошибкой, но, наконец-то ее разрешил. Решение неочевидное. MtApi.dll использует библиотеку Newtonsoft.Json.dll Причем, старую, 8-ой версии. Другие мои коннекторы, которые компилируются в ту же папку, что и MtApi.dll тоже используют Newtonsoft.Json.dll, но последней, 10-ой версии. При компиляции в папку сборки попадает 10-ая версия. MtApi.dll вызывает 8-ую версию, ей отвечает 10-ая, вот и ошибка.

Что сделал. При компиляции MtApi.dll указал 10-ую версию, заодно повысил Framework с 4 на 4.5. После этого ошибка исчезла. Благодарю за труды!

abdullahkhan-devops commented 2 years ago

Hi @vdemydiuk , I am using MtApi.dll and currently I am having the same issue, I have tried already whatever mentioned in the above comments. I am consuming api in asp.net mvc web, and its working fine but when deployed to iis it throws this no connection error.

I am fighting with this error for last 2 days, any help would be greatly appriciated. Thanks.