diff --git a/MTApiService/MTApiService.csproj b/MTApiService/MTApiService.csproj index 56010ac1..fb9fd2b3 100755 --- a/MTApiService/MTApiService.csproj +++ b/MTApiService/MTApiService.csproj @@ -83,7 +83,7 @@ - + diff --git a/MTApiService/MtServerInstance.cs b/MTApiService/MtAdapter.cs old mode 100755 new mode 100644 similarity index 77% rename from MTApiService/MtServerInstance.cs rename to MTApiService/MtAdapter.cs index a1e4e882..ac203b54 --- a/MTApiService/MtServerInstance.cs +++ b/MTApiService/MtAdapter.cs @@ -4,13 +4,13 @@ using log4net; namespace MTApiService { - public class MtServerInstance + public class MtAdapter { #region Fields private const string LogProfileName = "MtApiService"; - private static readonly ILog Log = LogManager.GetLogger(typeof(MtServerInstance)); - private static readonly MtServerInstance Instance = new MtServerInstance(); + private static readonly ILog Log = LogManager.GetLogger(typeof(MtAdapter)); + private static readonly MtAdapter Instance = new MtAdapter(); private readonly Dictionary _servers = new Dictionary(); private readonly Dictionary _experts = new Dictionary(); @@ -18,16 +18,16 @@ namespace MTApiService #region Init Instance - private MtServerInstance() + private MtAdapter() { LogConfigurator.Setup(LogProfileName); } - static MtServerInstance() + static MtAdapter() { } - public static MtServerInstance GetInstance() + public static MtAdapter GetInstance() { return Instance; } @@ -208,6 +208,50 @@ namespace MTApiService return retval; } + + public object GetNamedParameter(int expertHandle, string name) + { + Log.DebugFormat("GetNamedParameter: begin. expertHandle = {0}, name = {1}", expertHandle, name); + + MtExpert expert; + lock (_experts) + { + expert = _experts[expertHandle]; + } + + if (expert == null) + { + Log.WarnFormat("GetNamedParameter: expert with id {0} has not been found.", expertHandle); + } + + var retval = expert?.GetNamedParameter(name); + + Log.DebugFormat("GetNamedParameter: end. retval = {0}", retval); + + return retval; + } + + public bool ContainsNamedParameter(int expertHandle, string name) + { + Log.DebugFormat("ContainsNamedParameter: begin. expertHandle = {0}, name = {1}", expertHandle, name); + + MtExpert expert; + lock (_experts) + { + expert = _experts[expertHandle]; + } + + if (expert == null) + { + Log.WarnFormat("ContainsNamedParameter: expert with id {0} has not been found.", expertHandle); + } + + bool retval = expert != null ? expert.ContainsNamedParameter(name) : false; + + Log.DebugFormat("ContainsNamedParameter: end. retval = {0}", retval); + + return retval; + } #endregion #region Private Methods diff --git a/MTApiService/MtClient.cs b/MTApiService/MtClient.cs index 5dba56a7..47c0e1d0 100755 --- a/MTApiService/MtClient.cs +++ b/MTApiService/MtClient.cs @@ -139,7 +139,7 @@ namespace MTApiService } /// Thrown when connection failed - public MtResponse SendCommand(int commandType, ArrayList parameters, int expertHandle) + public MtResponse SendCommand(int commandType, ArrayList parameters, Dictionary namedParams, int expertHandle) { Log.DebugFormat("SendCommand: begin. commandType = {0}, parameters count = {1}", commandType, parameters?.Count); @@ -153,7 +153,11 @@ namespace MTApiService try { - result = _proxy.SendCommand(new MtCommand { CommandType = commandType, Parameters = parameters, ExpertHandle = expertHandle}); + result = _proxy.SendCommand(new MtCommand { + CommandType = commandType, + Parameters = parameters, + NamedParams = namedParams, + ExpertHandle = expertHandle}); } catch (Exception ex) { diff --git a/MTApiService/MtCommand.cs b/MTApiService/MtCommand.cs index 1ac04d39..eac8e8e9 100755 --- a/MTApiService/MtCommand.cs +++ b/MTApiService/MtCommand.cs @@ -1,5 +1,6 @@ using System.Runtime.Serialization; using System.Collections; +using System.Collections.Generic; namespace MTApiService { @@ -12,6 +13,9 @@ namespace MTApiService [DataMember] public ArrayList Parameters { get; set; } + [DataMember] + public Dictionary NamedParams { get; set; } + [DataMember] public int ExpertHandle { get; set; } diff --git a/MTApiService/MtExpert.cs b/MTApiService/MtExpert.cs index b609ec50..4f05f569 100755 --- a/MTApiService/MtExpert.cs +++ b/MTApiService/MtExpert.cs @@ -60,7 +60,7 @@ namespace MTApiService public object GetCommandParameter(int index) { - Log.DebugFormat("GetCommandType: called. index = {0}", index); + Log.DebugFormat("GetCommandParameter: called. index = {0}", index); var command = _currentTask?.Command; if (command?.Parameters != null && index >= 0 && index < command.Parameters.Count) @@ -71,6 +71,46 @@ namespace MTApiService return null; } + public object GetNamedParameter(string name) + { + Log.DebugFormat("GetNamedParameter: called. name = {0}", name); + + var command = _currentTask?.Command; + if (command == null) + { + Log.Warn("GetNamedParameter: command is not defined in task."); + return null; + } + + if (command.NamedParams == null) + { + Log.Warn("GetNamedParameter: NamedParams is not defined in command."); + return null; + } + + return command.NamedParams[name]; + } + + public bool ContainsNamedParameter(string name) + { + Log.DebugFormat("ContainsNamedParameter: called. name = {0}", name); + + var command = _currentTask?.Command; + if (command == null) + { + Log.Warn("ContainsNamedParameter: command is not defined in task."); + return false; + } + + if (command.NamedParams == null) + { + Log.Warn("ContainsNamedParameter: NamedParams is not defined in command."); + return false; + } + + return command.NamedParams.ContainsKey(name); + } + public void SendEvent(MtEvent mtEvent) { Log.DebugFormat("SendEvent: begin. event = {0}", mtEvent); diff --git a/MTApiService/Properties/AssemblyInfo.cs b/MTApiService/Properties/AssemblyInfo.cs index d89d1407..cb337916 100755 --- a/MTApiService/Properties/AssemblyInfo.cs +++ b/MTApiService/Properties/AssemblyInfo.cs @@ -32,5 +32,5 @@ using System.Runtime.InteropServices; // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.24.0")] -[assembly: AssemblyFileVersion("1.0.24.0")] +[assembly: AssemblyVersion("1.0.25.0")] +[assembly: AssemblyFileVersion("1.0.25.0")] diff --git a/MTConnector/MTConnector.cpp b/MTConnector/MTConnector.cpp index 7bb5762c..1d3046d6 100755 --- a/MTConnector/MTConnector.cpp +++ b/MTConnector/MTConnector.cpp @@ -45,7 +45,7 @@ _DLLAPI bool _stdcall initExpert(int expertHandle, int port, wchar_t* symbol, do { return Execute([&expertHandle, &port, symbol, &bid, &ask]() { MT4Handler^ mtHandler = gcnew MT4Handler(); - MtServerInstance::GetInstance()->InitExpert(expertHandle, port, gcnew String(symbol), bid, ask, mtHandler); + MtAdapter::GetInstance()->InitExpert(expertHandle, port, gcnew String(symbol), bid, ask, mtHandler); return true; }, err, false); @@ -54,7 +54,7 @@ _DLLAPI bool _stdcall initExpert(int expertHandle, int port, wchar_t* symbol, do _DLLAPI bool _stdcall deinitExpert(int expertHandle, wchar_t* err) { return Execute([&expertHandle]() { - MtServerInstance::GetInstance()->DeinitExpert(expertHandle); + MtAdapter::GetInstance()->DeinitExpert(expertHandle); return true; }, err, false); } @@ -62,7 +62,7 @@ _DLLAPI bool _stdcall deinitExpert(int expertHandle, wchar_t* err) _DLLAPI bool _stdcall updateQuote(int expertHandle, wchar_t* symbol, double bid, double ask, wchar_t* err) { return Execute([&expertHandle, symbol, &bid, &ask]() { - MtServerInstance::GetInstance()->SendQuote(expertHandle, gcnew String(symbol), bid, ask); + MtAdapter::GetInstance()->SendQuote(expertHandle, gcnew String(symbol), bid, ask); return true; }, err, false); } @@ -71,7 +71,7 @@ _DLLAPI bool _stdcall updateQuote(int expertHandle, wchar_t* symbol, double bid, _DLLAPI bool _stdcall sendEvent(int expertHandle, int eventType, wchar_t* payload, wchar_t* err) { return Execute([&expertHandle, &eventType, payload]() { - MtServerInstance::GetInstance()->SendEvent(expertHandle, eventType, gcnew String(payload)); + MtAdapter::GetInstance()->SendEvent(expertHandle, eventType, gcnew String(payload)); return true; }, err, false); } @@ -79,7 +79,7 @@ _DLLAPI bool _stdcall sendEvent(int expertHandle, int eventType, wchar_t* payloa _DLLAPI bool _stdcall sendIntResponse(int expertHandle, int response, wchar_t* err) { return Execute([&expertHandle, &response]() { - MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseInt(response)); + MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseInt(response)); return true; }, err, false); } @@ -87,7 +87,7 @@ _DLLAPI bool _stdcall sendIntResponse(int expertHandle, int response, wchar_t* e _DLLAPI bool _stdcall sendBooleanResponse(int expertHandle, bool response, wchar_t* err) { return Execute([&expertHandle, &response]() { - MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseBool(response)); + MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseBool(response)); return true; }, err, false); } @@ -95,7 +95,7 @@ _DLLAPI bool _stdcall sendBooleanResponse(int expertHandle, bool response, wchar _DLLAPI bool _stdcall sendDoubleResponse(int expertHandle, double response, wchar_t* err) { return Execute([&expertHandle, &response]() { - MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseDouble(response)); + MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseDouble(response)); return true; }, err, false); } @@ -103,7 +103,7 @@ _DLLAPI bool _stdcall sendDoubleResponse(int expertHandle, double response, wcha _DLLAPI bool _stdcall sendStringResponse(int expertHandle, wchar_t* response, wchar_t* err) { return Execute([&expertHandle, response]() { - MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseString(gcnew String(response))); + MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseString(gcnew String(response))); return true; }, err, false); } @@ -113,7 +113,7 @@ _DLLAPI bool _stdcall sendErrorResponse(int expertHandle, int code, wchar_t* mes return Execute([&expertHandle, &code, message]() { MtResponseString^ res = gcnew MtResponseString(gcnew String(message)); res->ErrorCode = code; - MtServerInstance::GetInstance()->SendResponse(expertHandle, res); + MtAdapter::GetInstance()->SendResponse(expertHandle, res); return true; }, err, false); } @@ -121,7 +121,7 @@ _DLLAPI bool _stdcall sendErrorResponse(int expertHandle, int code, wchar_t* mes _DLLAPI bool _stdcall sendVoidResponse(int expertHandle, wchar_t* err) { return Execute([&expertHandle]() { - MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseObject(nullptr)); + MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseObject(nullptr)); return true; }, err, false); } @@ -134,7 +134,7 @@ _DLLAPI bool _stdcall sendDoubleArrayResponse(int expertHandle, double* values, { list[i] = values[i]; } - MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseDoubleArray(list)); + MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseDoubleArray(list)); return true; }, err, false); } @@ -147,7 +147,7 @@ _DLLAPI bool _stdcall sendIntArrayResponse(int expertHandle, int* values, int si { list[i] = values[i]; } - MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseIntArray(list)); + MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseIntArray(list)); return true; }, err, false); } @@ -155,7 +155,7 @@ _DLLAPI bool _stdcall sendIntArrayResponse(int expertHandle, int* values, int si _DLLAPI bool _stdcall sendLongResponse(int expertHandle, __int64 response, wchar_t* err) { return Execute([&expertHandle, &response]() { - MtServerInstance::GetInstance()->SendResponse(expertHandle, gcnew MtResponseLong(response)); + MtAdapter::GetInstance()->SendResponse(expertHandle, gcnew MtResponseLong(response)); return true; }, err, false); } @@ -163,7 +163,7 @@ _DLLAPI bool _stdcall sendLongResponse(int expertHandle, __int64 response, wchar _DLLAPI bool _stdcall getCommandType(int expertHandle, int* res, wchar_t* err) { return Execute([&expertHandle, res]() { - *res = MtServerInstance::GetInstance()->GetCommandType(expertHandle); + *res = MtAdapter::GetInstance()->GetCommandType(expertHandle); return true; }, err, false); } @@ -171,15 +171,17 @@ _DLLAPI bool _stdcall getCommandType(int expertHandle, int* res, wchar_t* err) _DLLAPI bool _stdcall getIntValue(int expertHandle, int paramIndex, int* res, wchar_t* err) { return Execute([&expertHandle, ¶mIndex, res]() { - *res = (int)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex); + *res = (int)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex); return true; }, err, false); } +// --- index parameters + _DLLAPI bool _stdcall getDoubleValue(int expertHandle, int paramIndex, double* res, wchar_t* err) { return Execute([&expertHandle, ¶mIndex, res]() { - *res = (double)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex); + *res = (double)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex); return true; }, err, false); } @@ -187,7 +189,7 @@ _DLLAPI bool _stdcall getDoubleValue(int expertHandle, int paramIndex, double* r _DLLAPI bool _stdcall getStringValue(int expertHandle, int paramIndex, wchar_t* res, wchar_t* err) { return Execute([&expertHandle, ¶mIndex, res]() { - convertSystemString(res, (String^)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex)); + convertSystemString(res, (String^)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex)); return true; }, err, false); } @@ -195,7 +197,7 @@ _DLLAPI bool _stdcall getStringValue(int expertHandle, int paramIndex, wchar_t* _DLLAPI bool _stdcall getBooleanValue(int expertHandle, int paramIndex, int* res, wchar_t* err) { return Execute([&expertHandle, ¶mIndex, res]() { - *res = (bool)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex); + *res = (bool)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex); return true; }, err, false); } @@ -203,7 +205,63 @@ _DLLAPI bool _stdcall getBooleanValue(int expertHandle, int paramIndex, int* res _DLLAPI bool _stdcall getLongValue(int expertHandle, int paramIndex, __int64* res, wchar_t* err) { return Execute([&expertHandle, ¶mIndex, res]() { - *res = (__int64)MtServerInstance::GetInstance()->GetCommandParameter(expertHandle, paramIndex); + *res = (__int64)MtAdapter::GetInstance()->GetCommandParameter(expertHandle, paramIndex); + return true; + }, err, false); +} + +// --- named parameters + +_DLLAPI bool _stdcall containsNamedValue(int expertHandle, wchar_t* paramName) +{ + wchar_t err[1000]; + return Execute([&expertHandle, paramName]() { + return MtAdapter::GetInstance()->ContainsNamedParameter(expertHandle, gcnew String(paramName)); + }, err, false); +} + +_DLLAPI bool _stdcall getNamedIntValue(int expertHandle, wchar_t* paramName, int* res, wchar_t* err) +{ + return Execute([&expertHandle, paramName, res]() { + System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName)); + *res = (int)obj; + return true; + }, err, false); +} + +_DLLAPI bool _stdcall getNamedDoubleValue(int expertHandle, wchar_t* paramName, double* res, wchar_t* err) +{ + return Execute([&expertHandle, paramName, res]() { + System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName)); + *res = (double)obj; + return true; + }, err, false); +} + +_DLLAPI bool _stdcall getNamedStringValue(int expertHandle, wchar_t* paramName, wchar_t* res, wchar_t* err) +{ + return Execute([&expertHandle, paramName, res]() { + System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName)); + convertSystemString(res, (String^)obj); + return true; + }, err, false); +} + +_DLLAPI bool _stdcall getNamedBooleanValue(int expertHandle, wchar_t* paramName, int* res, wchar_t* err) +{ + return Execute([&expertHandle, paramName, res]() { + System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName)); + *res = (bool)obj; + return true; + }, err, false); +} + +_DLLAPI bool _stdcall getNamedLongValue(int expertHandle, wchar_t* paramName, __int64* res, wchar_t* err) +{ + return Execute([&expertHandle, paramName, res]() { + System::Object^ obj = MtAdapter::GetInstance()->GetNamedParameter(expertHandle, gcnew String(paramName)); + if (obj != nullptr) + *res = (__int64)obj; return true; }, err, false); } \ No newline at end of file diff --git a/MtApi/EnumObject.cs b/MtApi/EnumObject.cs new file mode 100644 index 00000000..c5420942 --- /dev/null +++ b/MtApi/EnumObject.cs @@ -0,0 +1,46 @@ +namespace MtApi +{ + public enum EnumObject + { + OBJ_VLINE = 0, //Vertical Line + OBJ_HLINE = 1, //Horizontal Line + OBJ_TREND = 2, //Trend Line + BJ_TRENDBYANGLE = 3, //Trend Line By Angle + OBJ_CYCLES = 20, //Cycle Lines + OBJ_CHANNEL = 5, //Equidistant Channel + OBJ_STDDEVCHANNEL = 6, //Standard Deviation Channel + OBJ_REGRESSION = 4, //Linear Regression Channel + OBJ_PITCHFORK = 19, //Andrews’ Pitchfork + OBJ_GANNLINE = 7, //Gann Line + OBJ_GANNFAN = 8, //Gann Fan + OBJ_GANNGRID = 9, //Gann Grid + OBJ_FIBO = 10, //Fibonacci Retracement + OBJ_FIBOTIMES = 11, //Fibonacci Time Zones + OBJ_FIBOFAN = 12, //Fibonacci Fan + OBJ_FIBOARC = 13, //Fibonacci Arcs + OBJ_FIBOCHANNEL = 15, //Fibonacci Channel + OBJ_EXPANSION = 14, //Fibonacci Expansion + OBJ_RECTANGLE = 16, //Rectangle + OBJ_TRIANGLE = 17, //Triangle + OBJ_ELLIPSE = 18, //Ellipse + OBJ_ARROW_THUMB_UP = 29, //Thumbs Up + OBJ_ARROW_THUMB_DOWN = 30, //Thumbs Down + OBJ_ARROW_UP = 31, //Arrow Up + OBJ_ARROW_DOWN = 32, //Arrow Down + OBJ_ARROW_STOP = 33, //Stop Sign + OBJ_ARROW_CHECK = 34, //Check Sign + OBJ_ARROW_LEFT_PRICE = 35, //Left Price Label + OBJ_ARROW_RIGHT_PRICE = 36, //Right Price Label + OBJ_ARROW_BUY = 37, //Buy Sign + OBJ_ARROW_SELL = 38, //Sell Sign + OBJ_ARROW = 22, //Arrow + OBJ_TEXT = 21, //Text + OBJ_LABEL = 23, //Label + OBJ_BUTTON = 25, //Button + OBJ_BITMAP = 26, //Bitmap + OBJ_BITMAP_LABEL = 24, //Bitmap Label + OBJ_EDIT = 27, //Edit + OBJ_EVENT = 42, //The "Event" object corresponding to an event in the economic calendar + OBJ_RECTANGLE_LABEL = 28 //The "Rectangle label" object for creating and designing the custom graphical interface. + } +} diff --git a/MtApi/MtApi.csproj b/MtApi/MtApi.csproj index 985c00c7..de7bfb08 100755 --- a/MtApi/MtApi.csproj +++ b/MtApi/MtApi.csproj @@ -59,6 +59,7 @@ + diff --git a/MtApi/MtApiClient.cs b/MtApi/MtApiClient.cs index 719749f1..15b138fe 100755 --- a/MtApi/MtApiClient.cs +++ b/MtApi/MtApiClient.cs @@ -16,8 +16,6 @@ namespace MtApi public sealed class MtApiClient { - private volatile bool _isBacktestingMode; - #region MetaTrader Constants //Special constant @@ -31,6 +29,8 @@ namespace MtApi private MtClient _client; private readonly object _locker = new object(); private MtConnectionState _connectionState = MtConnectionState.Disconnected; + private volatile bool _isBacktestingMode; + private int _executorHandle; #endregion #region ctor @@ -74,7 +74,8 @@ namespace MtApi /// public List GetQuotes() { - var quotes = _client.GetQuotes(); + var client = Client; + var quotes = client != null ? client.GetQuotes() : null; return quotes?.Select(q => new MtQuote(q)).ToList(); } #endregion @@ -95,7 +96,9 @@ namespace MtApi } } - private int _executorHandle; + /// + ///Connection status of MetaTrader API. + /// public int ExecutorHandle { get @@ -1746,14 +1749,32 @@ namespace MtApi ///The price coordinate of the first anchor point. ///The time coordinate of the second anchor point. ///The price coordinate of the second anchor point. - ///The price coordinate of the second anchor point. + ///The time coordinate of the third anchor point. + ///The price coordinate of the third anchor point. /// - ///Returns various data about securities listed in the "Market Watch" window. + ///Returns true or false depending on whether the object is created or not. /// - bool ObjectCreate(long chartId, string objectName, object objectType, int subWindow, - DateTime time1, double price1, DateTime? time2 = null, double price2 = 0, DateTime? time3 = null, double price3 = 0) + public bool ObjectCreate(long chartId, string objectName, EnumObject objectType, int subWindow, + DateTime? time1, double price1, DateTime? time2 = null, double? price2 = null, DateTime? time3 = null, double? price3 = null) { - return false; + var namedParams = new Dictionary(); + namedParams.Add(nameof(chartId), chartId); + namedParams.Add(nameof(objectName), objectName); + namedParams.Add(nameof(objectType), (int)objectType); + namedParams.Add(nameof(subWindow), subWindow); + namedParams.Add(nameof(time1), time1 == null ? 0 : MtApiTimeConverter.ConvertToMtTime(time1.Value)); + namedParams.Add(nameof(price1), price1); + + if (time2 != null) + namedParams.Add(nameof(time2), MtApiTimeConverter.ConvertToMtTime(time2.Value)); + if (price2 != null) + namedParams.Add(nameof(price2), price2.Value); + if (time3 != null) + namedParams.Add(nameof(time3), MtApiTimeConverter.ConvertToMtTime(time3.Value)); + if (price3 != null) + namedParams.Add(nameof(price3), price3.Value); + + return SendCommand(MtCommandType.ObjectCreate, null, namedParams); } #endregion @@ -1882,7 +1903,7 @@ namespace MtApi ConnectionStateChanged?.Invoke(this, new MtConnectionEventArgs(state, message)); } - private T SendCommand(MtCommandType commandType, ArrayList commandParameters) + private T SendCommand(MtCommandType commandType, ArrayList commandParameters, Dictionary namedParams = null) { MtResponse response; @@ -1894,7 +1915,7 @@ namespace MtApi try { - response = client.SendCommand((int)commandType, commandParameters, ExecutorHandle); + response = client.SendCommand((int)commandType, commandParameters, namedParams, ExecutorHandle); } catch (CommunicationException ex) { diff --git a/MtApi/MtConnectionEventArgs.cs b/MtApi/MtConnectionEventArgs.cs index 15baf021..9254d774 100755 --- a/MtApi/MtConnectionEventArgs.cs +++ b/MtApi/MtConnectionEventArgs.cs @@ -1,14 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace MtApi { public class MtConnectionEventArgs: EventArgs { public MtConnectionState Status { get; private set; } - public String ConnectionMessage { get; private set; } + public string ConnectionMessage { get; private set; } public MtConnectionEventArgs(MtConnectionState status, string message) { diff --git a/MtApi5/Mt5ConnectionEventArgs.cs b/MtApi5/Mt5ConnectionEventArgs.cs index 8f523a6e..14edb7e1 100755 --- a/MtApi5/Mt5ConnectionEventArgs.cs +++ b/MtApi5/Mt5ConnectionEventArgs.cs @@ -1,14 +1,11 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; namespace MtApi5 { public class Mt5ConnectionEventArgs: EventArgs { public Mt5ConnectionState Status { get; private set; } - public String ConnectionMessage { get; private set; } + public string ConnectionMessage { get; private set; } public Mt5ConnectionEventArgs(Mt5ConnectionState status, string message) { diff --git a/MtApi5/MtApi5Client.cs b/MtApi5/MtApi5Client.cs index cf3e527d..ba931151 100755 --- a/MtApi5/MtApi5Client.cs +++ b/MtApi5/MtApi5Client.cs @@ -7,6 +7,7 @@ using System.ServiceModel; using MtApi5.Requests; using MtApi5.Responses; using Newtonsoft.Json; +using System.Threading.Tasks; namespace MtApi5 { @@ -31,22 +32,17 @@ namespace MtApi5 #region Private Fields - private readonly MtClient _client = new MtClient(); + private MtClient _client; + private readonly object _locker = new object(); private volatile bool _isBacktestingMode = false; + private Mt5ConnectionState _connectionState = Mt5ConnectionState.Disconnected; + private int _executorHandle; #endregion #region Public Methods public MtApi5Client() { LogConfigurator.Setup(LogProfileName); - - ConnectionState = Mt5ConnectionState.Disconnected; - - _client.QuoteAdded += mClient_QuoteAdded; - _client.QuoteRemoved += mClient_QuoteRemoved; - _client.QuoteUpdated += mClient_QuoteUpdated; - _client.ServerDisconnected += mClient_ServerDisconnected; - _client.ServerFailed += mClient_ServerFailed; } /// @@ -56,15 +52,7 @@ namespace MtApi5 ///Port of host connection (default 8222) public void BeginConnect(string host, int port) { - //if (string.IsNullOrEmpty(host) == false && (host.Equals("localhost") || host.Equals("127.0.0.1"))) - //{ - // this.BeginConnect(port); - //} - //else - //{ - Action connectAction = Connect; - connectAction.BeginInvoke(host, port, null, null); - //} + Task.Factory.StartNew(() => Connect(host, port)); } /// @@ -73,8 +61,7 @@ namespace MtApi5 ///Port of host connection (default 8222) public void BeginConnect(int port) { - Action connectAction = Connect; - connectAction.BeginInvoke(port, null, null); + Task.Factory.StartNew(() => Connect(port)); } /// @@ -82,8 +69,7 @@ namespace MtApi5 /// public void BeginDisconnect() { - Action disconnectAction = Disconnect; - disconnectAction.BeginInvoke(null, null); + Task.Factory.StartNew(() => Disconnect(false)); } /// @@ -91,11 +77,8 @@ namespace MtApi5 /// public IEnumerable GetQuotes() { - IEnumerable quotes; - lock (_client) - { - quotes = _client.GetQuotes(); - } + var client = Client; + var quotes = client != null ? client.GetQuotes() : null; return quotes?.Select(q => q.Parse()); } @@ -1432,7 +1415,34 @@ namespace MtApi5 /// ///Connection status of MetaTrader API. /// - public Mt5ConnectionState ConnectionState { get; private set; } + public Mt5ConnectionState ConnectionState + { + get + { + lock (_locker) + { + return _connectionState; + } + } + } + + public int ExecutorHandle + { + get + { + lock (_locker) + { + return _executorHandle; + } + } + set + { + lock (_locker) + { + _executorHandle = value; + } + } + } #endregion #region Events @@ -1443,94 +1453,148 @@ namespace MtApi5 #endregion #region Private Methods - private void Connect(string host, int port) + private MtClient Client { - ConnectionState = Mt5ConnectionState.Connecting; - ConnectionStateChanged.FireEvent(this - , new Mt5ConnectionEventArgs(Mt5ConnectionState.Connecting, "Connecting to " + host + ":" + port)); - - try + get { - lock (_client) + lock (_locker) { - _client.Open(host, port); - _client.Connect(); + return _client; } } - catch (Exception e) + } + + private void Connect(MtClient client) + { + lock (_locker) { - ConnectionState = Mt5ConnectionState.Failed; - ConnectionStateChanged.FireEvent(this - , new Mt5ConnectionEventArgs(Mt5ConnectionState.Failed, "Failed connection to " + host + ":" + port + ". " + e.Message)); - return; + if (_connectionState == Mt5ConnectionState.Connected + || _connectionState == Mt5ConnectionState.Connecting) + { + return; + } + + _connectionState = Mt5ConnectionState.Connecting; } - ConnectionState = Mt5ConnectionState.Connected; - ConnectionStateChanged.FireEvent(this - , new Mt5ConnectionEventArgs(Mt5ConnectionState.Connected, "Connected to " + host + ":" + port)); + string message = string.IsNullOrEmpty(client.Host) ? $"Connecting to localhost:{client.Port}" : $"Connecting to {client.Host}:{client.Port}"; + ConnectionStateChanged?.Invoke(this, new Mt5ConnectionEventArgs(Mt5ConnectionState.Connecting, message)); - OnConnected(); + var state = Mt5ConnectionState.Failed; + + lock (_locker) + { + try + { + client.Connect(); + state = Mt5ConnectionState.Connected; + } + catch (Exception e) + { + client.Dispose(); + message = string.IsNullOrEmpty(client.Host) ? $"Failed connection to localhost:{client.Port}. {e.Message}" : $"Failed connection to {client.Host}:{client.Port}. {e.Message}"; + } + + if (state == Mt5ConnectionState.Connected) + { + _client = client; + _client.QuoteAdded += _client_QuoteAdded; + _client.QuoteRemoved += _client_QuoteRemoved; + _client.QuoteUpdated += _client_QuoteUpdated; + _client.ServerDisconnected += _client_ServerDisconnected; + _client.ServerFailed += _client_ServerFailed; + message = string.IsNullOrEmpty(client.Host) ? $"Connected to localhost:{client.Port}" : $"Connected to { client.Host}:{client.Port}"; + } + + _connectionState = state; + } + + ConnectionStateChanged?.Invoke(this, new Mt5ConnectionEventArgs(state, message)); + + if (state == Mt5ConnectionState.Connected) + { + OnConnected(); + } + } + + private void Connect(string host, int port) + { + var client = new MtClient(host, port); + Connect(client); } private void Connect(int port) { - ConnectionState = Mt5ConnectionState.Connecting; - ConnectionStateChanged.FireEvent(this - , new Mt5ConnectionEventArgs(Mt5ConnectionState.Connecting, "Connecting to 'localhost':" + port)); - - try - { - lock (_client) - { - _client.Open(port); - _client.Connect(); - } - } - catch (Exception e) - { - ConnectionState = Mt5ConnectionState.Failed; - ConnectionStateChanged.FireEvent(this - , new Mt5ConnectionEventArgs(Mt5ConnectionState.Failed, "Failed connection to 'localhost':" + port + ". " + e.Message)); - - return; - } - - ConnectionState = Mt5ConnectionState.Connected; - ConnectionStateChanged.FireEvent(this - , new Mt5ConnectionEventArgs(Mt5ConnectionState.Connected, "Connected to 'localhost':" + port)); - - OnConnected(); + var client = new MtClient(port); + Connect(client); } - private void Disconnect() + private void Disconnect(bool failed) { - lock (_client) + var state = failed ? Mt5ConnectionState.Failed : Mt5ConnectionState.Disconnected; + var message = failed ? "Connection Failed" : "Disconnected"; + + lock (_locker) { - _client.Disconnect(); - _client.Close(); + if (_connectionState == Mt5ConnectionState.Disconnected + || _connectionState == Mt5ConnectionState.Failed) + return; + + if (_client != null) + { + _client.QuoteAdded -= _client_QuoteAdded; + _client.QuoteRemoved -= _client_QuoteRemoved; + _client.QuoteUpdated -= _client_QuoteUpdated; + _client.ServerDisconnected -= _client_ServerDisconnected; + _client.ServerFailed -= _client_ServerFailed; + + if (!failed) + { + _client.Disconnect(); + } + + _client.Dispose(); + + _client = null; + } + + _connectionState = state; } - ConnectionState = Mt5ConnectionState.Disconnected; - ConnectionStateChanged.FireEvent(this, new Mt5ConnectionEventArgs(Mt5ConnectionState.Disconnected, "Disconnected")); + ConnectionStateChanged?.Invoke(this, new Mt5ConnectionEventArgs(state, message)); } - private T SendCommand(Mt5CommandType commandType, ArrayList commandParameters) + private T SendCommand(Mt5CommandType commandType, ArrayList commandParameters, Dictionary namedParams = null) { MtResponse response; + + var client = Client; + if (client == null) + { + throw new Exception("No connection"); + } + try { - lock (_client) - { - response = _client.SendCommand((int) commandType, commandParameters); - } + response = client.SendCommand((int)commandType, commandParameters, namedParams, ExecutorHandle); } catch (CommunicationException ex) { throw new Exception(ex.Message, ex); } - var responseValue = response?.GetValue(); - return responseValue != null ? (T) responseValue : default(T); + if (response == null) + { + throw new ExecutionException(ErrorCode.ErrCustom, "Response from MetaTrader is null"); + } + + if (response.ErrorCode != 0) + { + throw new ExecutionException((ErrorCode)response.ErrorCode, response.ToString()); + } + + var responseValue = response.GetValue(); + return responseValue != null ? (T)responseValue : default(T); } private T SendRequest(RequestBase request) where T : ResponseBase, new() @@ -1545,25 +1609,14 @@ namespace MtApi5 }); var commandParameters = new ArrayList { serializer }; - MtResponseString res; - try - { - lock (_client) - { - res = (MtResponseString)_client.SendCommand((int)Mt5CommandType.MtRequest, commandParameters); - } - } - catch (CommunicationException ex) - { - throw new Exception(ex.Message, ex); - } + var res = SendCommand(Mt5CommandType.MtRequest, commandParameters); if (res == null) { throw new ExecutionException(ErrorCode.ErrCustom, "Response from MetaTrader is null"); } - var response = JsonConvert.DeserializeObject(res.Value); + var response = JsonConvert.DeserializeObject(res); if (response.ErrorCode != 0) { throw new ExecutionException((ErrorCode)response.ErrorCode, response.ErrorMessage); @@ -1573,7 +1626,7 @@ namespace MtApi5 } - private void mClient_QuoteUpdated(MtQuote quote) + private void _client_QuoteUpdated(MtQuote quote) { if (quote != null) { @@ -1581,26 +1634,22 @@ namespace MtApi5 } } - private void mClient_ServerDisconnected(object sender, EventArgs e) + private void _client_ServerDisconnected(object sender, EventArgs e) { - ConnectionState = Mt5ConnectionState.Disconnected; - ConnectionStateChanged.FireEvent(this - , new Mt5ConnectionEventArgs(Mt5ConnectionState.Disconnected, "MtApi is disconnected")); + Disconnect(false); } - private void mClient_ServerFailed(object sender, EventArgs e) + private void _client_ServerFailed(object sender, EventArgs e) { - ConnectionState = Mt5ConnectionState.Failed; - ConnectionStateChanged.FireEvent(this - , new Mt5ConnectionEventArgs(Mt5ConnectionState.Failed, "Failed connection with MtApi")); + Disconnect(true); } - private void mClient_QuoteRemoved(MtQuote quote) + private void _client_QuoteRemoved(MtQuote quote) { QuoteRemoved.FireEvent(this, new Mt5QuoteEventArgs(quote.Parse())); } - private void mClient_QuoteAdded(MtQuote quote) + private void _client_QuoteAdded(MtQuote quote) { QuoteAdded.FireEvent(this, new Mt5QuoteEventArgs(quote.Parse())); } diff --git a/TestClients/TestApiClientUI/Form1.Designer.cs b/TestClients/TestApiClientUI/Form1.Designer.cs index 0e0f80c4..c7f09913 100644 --- a/TestClients/TestApiClientUI/Form1.Designer.cs +++ b/TestClients/TestApiClientUI/Form1.Designer.cs @@ -195,6 +195,10 @@ this.tabPage9 = new System.Windows.Forms.TabPage(); this.button38 = new System.Windows.Forms.Button(); this.button37 = new System.Windows.Forms.Button(); + this.tabPage10 = new System.Windows.Forms.TabPage(); + this.button4 = new System.Windows.Forms.Button(); + this.comboBox11 = new System.Windows.Forms.ComboBox(); + this.textBoxChartId = new System.Windows.Forms.TextBox(); this.groupBox1.SuspendLayout(); this.statusStrip1.SuspendLayout(); this.groupBox2.SuspendLayout(); @@ -212,6 +216,7 @@ this.tabPage7.SuspendLayout(); this.tabPage8.SuspendLayout(); this.tabPage9.SuspendLayout(); + this.tabPage10.SuspendLayout(); this.SuspendLayout(); // // textBoxServerName @@ -393,6 +398,7 @@ this.tabControl1.Controls.Add(this.tabPage7); this.tabControl1.Controls.Add(this.tabPage8); this.tabControl1.Controls.Add(this.tabPage9); + this.tabControl1.Controls.Add(this.tabPage10); this.tabControl1.Location = new System.Drawing.Point(324, 12); this.tabControl1.Multiline = true; this.tabControl1.Name = "tabControl1"; @@ -1951,6 +1957,7 @@ // // tabPage9 // + this.tabPage9.Controls.Add(this.textBoxChartId); this.tabPage9.Controls.Add(this.button38); this.tabPage9.Controls.Add(this.button37); this.tabPage9.Location = new System.Drawing.Point(4, 40); @@ -1980,6 +1987,44 @@ this.button37.UseVisualStyleBackColor = true; this.button37.Click += new System.EventHandler(this.button37_Click); // + // tabPage10 + // + this.tabPage10.Controls.Add(this.comboBox11); + this.tabPage10.Controls.Add(this.button4); + this.tabPage10.Location = new System.Drawing.Point(4, 40); + this.tabPage10.Name = "tabPage10"; + this.tabPage10.Padding = new System.Windows.Forms.Padding(3); + this.tabPage10.Size = new System.Drawing.Size(638, 383); + this.tabPage10.TabIndex = 10; + this.tabPage10.Text = "Object Functions"; + this.tabPage10.UseVisualStyleBackColor = true; + // + // button4 + // + this.button4.Location = new System.Drawing.Point(133, 3); + this.button4.Name = "button4"; + this.button4.Size = new System.Drawing.Size(97, 23); + this.button4.TabIndex = 0; + this.button4.Text = "ObjectCreate"; + this.button4.UseVisualStyleBackColor = true; + this.button4.Click += new System.EventHandler(this.button4_Click); + // + // comboBox11 + // + this.comboBox11.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; + this.comboBox11.FormattingEnabled = true; + this.comboBox11.Location = new System.Drawing.Point(6, 5); + this.comboBox11.Name = "comboBox11"; + this.comboBox11.Size = new System.Drawing.Size(121, 21); + this.comboBox11.TabIndex = 1; + // + // textBoxChartId + // + this.textBoxChartId.Location = new System.Drawing.Point(115, 8); + this.textBoxChartId.Name = "textBoxChartId"; + this.textBoxChartId.Size = new System.Drawing.Size(100, 20); + this.textBoxChartId.TabIndex = 2; + // // Form1 // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -2022,6 +2067,8 @@ this.tabPage8.ResumeLayout(false); this.tabPage8.PerformLayout(); this.tabPage9.ResumeLayout(false); + this.tabPage9.PerformLayout(); + this.tabPage10.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -2195,6 +2242,10 @@ private System.Windows.Forms.Button button65; private System.Windows.Forms.Button button66; private System.Windows.Forms.Button button67; + private System.Windows.Forms.TabPage tabPage10; + private System.Windows.Forms.Button button4; + private System.Windows.Forms.ComboBox comboBox11; + private System.Windows.Forms.TextBox textBoxChartId; } } diff --git a/TestClients/TestApiClientUI/Form1.cs b/TestClients/TestApiClientUI/Form1.cs index dfccb0ba..befbfd3f 100644 --- a/TestClients/TestApiClientUI/Form1.cs +++ b/TestClients/TestApiClientUI/Form1.cs @@ -33,6 +33,7 @@ namespace TestApiClientUI comboBox8.DataSource = Enum.GetNames(typeof(MarketInfoModeType)); comboBox9.DataSource = Enum.GetNames(typeof(EnumTerminalInfoInteger)); comboBox10.DataSource = Enum.GetNames(typeof(EnumTerminalInfoDouble)); + comboBox11.DataSource = Enum.GetNames(typeof(EnumObject)); comboBoxAccountInfoCmd.DataSource = Enum.GetNames(typeof(TradeOperation)); _apiClient.QuoteUpdated += apiClient_QuoteUpdated; @@ -49,6 +50,7 @@ namespace TestApiClientUI comboBox3.SelectedIndex = 0; comboBox4.SelectedIndex = 0; comboBox5.SelectedIndex = 0; + comboBox11.SelectedIndex = 0; comboBoxAccountInfoCmd.SelectedIndex = 0; _timerTradeMonitor = new TimerTradeMonitor(_apiClient) { Interval = 10000 }; // 10 sec @@ -1369,6 +1371,7 @@ namespace TestApiClientUI { var result = await Execute(_apiClient.ChartId); PrintLog($"CharID: result = {result}"); + RunOnUiThread(() => textBoxChartId.Text = result.ToString()); } //ChartRedraw @@ -1377,5 +1380,18 @@ namespace TestApiClientUI await Execute(() => _apiClient.ChartRedraw()); PrintLog($"ChartRedraw: called."); } + + //ObjectCreate + private async void button4_Click(object sender, EventArgs e) + { + long chartId = 0; + string objectName = "label_object"; + //long.TryParse(textBoxChartId.Text, ) + EnumObject objectType; + Enum.TryParse(comboBox11.Text, out objectType); + + var result = await Execute(() => _apiClient.ObjectCreate(chartId, objectName, objectType, 0, null, 0)); + PrintLog($"ObjectCreate result: {result}"); + } } } diff --git a/mq4/MtApi.ex4 b/mq4/MtApi.ex4 index 2f5e3b3d..4ae51c9e 100755 Binary files a/mq4/MtApi.ex4 and b/mq4/MtApi.ex4 differ diff --git a/mq4/MtApi.mq4 b/mq4/MtApi.mq4 index 75c3ff35..03d52d3e 100755 Binary files a/mq4/MtApi.mq4 and b/mq4/MtApi.mq4 differ