diff --git a/MTApiService/MTApiService.csproj b/MTApiService/MTApiService.csproj index 44777da8..ed103b9b 100755 --- a/MTApiService/MTApiService.csproj +++ b/MTApiService/MTApiService.csproj @@ -60,6 +60,7 @@ + diff --git a/MTApiService/MtClient.cs b/MTApiService/MtClient.cs index b7cb090a..57bee4b4 100755 --- a/MTApiService/MtClient.cs +++ b/MTApiService/MtClient.cs @@ -224,20 +224,14 @@ namespace MTApiService { Debug.WriteLine("[INFO] MtClient::OnQuoteAdded"); - if (QuoteAdded != null) - { - QuoteAdded(quote); - } + QuoteAdded?.Invoke(quote); } public void OnQuoteRemoved(MtQuote quote) { Debug.WriteLine("[INFO] MtClient::OnQuoteRemoved"); - if (QuoteRemoved != null) - { - QuoteRemoved(quote); - } + QuoteRemoved?.Invoke(quote); } public void OnServerStopped() @@ -246,10 +240,13 @@ namespace MTApiService Close(); - if (ServerDisconnected != null) - { - ServerDisconnected(this, EventArgs.Empty); - } + ServerDisconnected?.Invoke(this, EventArgs.Empty); + } + + + public void OnMtEvent(MtEvent mtEvent) + { + MtEventReceived?.Invoke(this, new MtEventArgs(mtEvent)); } #endregion @@ -301,6 +298,7 @@ namespace MTApiService public event MtQuoteHandler QuoteUpdated; public event EventHandler ServerDisconnected; public event EventHandler ServerFailed; + public event EventHandler MtEventReceived; #endregion #region Fields diff --git a/MTApiService/MtEvent.cs b/MTApiService/MtEvent.cs new file mode 100644 index 00000000..80b38b38 --- /dev/null +++ b/MTApiService/MtEvent.cs @@ -0,0 +1,36 @@ +using System; +using System.Runtime.Serialization; + +namespace MTApiService +{ + [DataContract] + public class MtEvent + { + [DataMember] + public int EventType { get; private set; } + + [DataMember] + public string Payload { get; private set; } + + public MtEvent(int eventType, string payload) + { + EventType = eventType; + Payload = payload; + } + + public override string ToString() + { + return "MtEvent = " + EventType + ", Payload = " + Payload; + } + } + + public class MtEventArgs: EventArgs + { + public MtEventArgs(MtEvent e) + { + Event = e; + } + + public MtEvent Event { get; private set; } + } +} diff --git a/MTApiService/MtExpert.cs b/MTApiService/MtExpert.cs index 7ea328d8..2d98eefc 100755 --- a/MTApiService/MtExpert.cs +++ b/MTApiService/MtExpert.cs @@ -66,6 +66,7 @@ namespace MTApiService } } } + #endregion #region Public Methods @@ -114,6 +115,11 @@ namespace MTApiService return null; } + + public void SendEvent(MtEvent mtEvent) + { + FireOnMtEvent(mtEvent); + } #endregion #region IMtCommandExecutor @@ -134,29 +140,22 @@ namespace MTApiService private void FireOnQuoteChanged(MtQuote quote) { - var handler = QuoteChanged; - if (handler != null) - { - handler(this, quote); - } + QuoteChanged?.Invoke(this, quote); } private void FireOnDeinited() { - var handler = Deinited; - if (handler != null) - { - handler(this, EventArgs.Empty); - } + Deinited?.Invoke(this, EventArgs.Empty); } private void FireOnCommandExecuted() { - var handler = CommandExecuted; - if (handler != null) - { - handler(this, EventArgs.Empty); - } + CommandExecuted?.Invoke(this, EventArgs.Empty); + } + + private void FireOnMtEvent(MtEvent mtEvent) + { + OnMtEvent?.Invoke(this, new MtEventArgs(mtEvent)); } #endregion @@ -164,6 +163,7 @@ namespace MTApiService public event EventHandler Deinited; public event MtQuoteHandler QuoteChanged; public event EventHandler CommandExecuted; + public event EventHandler OnMtEvent; #endregion #region Private Fields diff --git a/MTApiService/MtServer.cs b/MTApiService/MtServer.cs index 6bef6ea9..15283c99 100755 --- a/MTApiService/MtServer.cs +++ b/MTApiService/MtServer.cs @@ -107,6 +107,7 @@ namespace MTApiService { expert.Deinited += expert_Deinited; expert.QuoteChanged += expert_QuoteChanged; + expert.OnMtEvent += Expert_OnMtEvent; lock (_experts) { @@ -308,13 +309,14 @@ namespace MTApiService _service.QuoteUpdate(quote); } + private void Expert_OnMtEvent(object sender, MtEventArgs e) + { + _service.OnMtEvent(e.Event); + } + private void FireOnStopped() { - var handler = Stopped; - if (handler != null) - { - handler(this, EventArgs.Empty); - } + Stopped?.Invoke(this, EventArgs.Empty); } #endregion diff --git a/MTApiService/MtServerInstance.cs b/MTApiService/MtServerInstance.cs index 2eb06b38..b30f50fd 100755 --- a/MTApiService/MtServerInstance.cs +++ b/MTApiService/MtServerInstance.cs @@ -101,6 +101,24 @@ namespace MTApiService Debug.WriteLine("MtApiServerInstance::SendQuote: finish."); } + public void SendEvent(int expertHandle, int eventType, string payload) + { + Debug.WriteLine("MtApiServerInstance::SendEvent called. eventType = {0}, payload = {1}", eventType, payload); + + MtExpert expert = null; + lock (mExpertsDictionary) + { + expert = mExpertsDictionary[expertHandle]; + } + + if (expert != null) + { + expert.SendEvent(new MtEvent(eventType, payload)); + } + + Debug.WriteLine("MtApiServerInstance::SendEvent: finished"); + } + public void SendResponse(int expertHandle, MtResponse response) { Debug.WriteLine("MtApiServerInstance::SendResponse: id = {0}, response = {1}", expertHandle, response); diff --git a/MTApiService/MtService.cs b/MTApiService/MtService.cs index 56dfa4d4..5b5b9ee5 100755 --- a/MTApiService/MtService.cs +++ b/MTApiService/MtService.cs @@ -35,6 +35,9 @@ namespace MTApiService [OperationContract(IsOneWay = true)] void OnQuoteRemoved(MtQuote quote); + + [OperationContract(IsOneWay = true)] + void OnMtEvent(MtEvent ntEvent); } [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, @@ -123,171 +126,74 @@ namespace MTApiService #region Public Methods public void OnStopServer() { - try - { - mClientsLocker.AcquireReaderLock(2000); - - try - { - foreach (var callback in mClientCallbacks) - { - try - { - callback.OnServerStopped(); - } - catch (Exception) - { - } - } - } - finally - { - mClientsLocker.ReleaseReaderLock(); - } - } - catch (ApplicationException) - { - } + ExecuteCallbackAction(a => a.OnServerStopped()); } public void QuoteUpdate(MtQuote quote) { - try - { - mClientsLocker.AcquireReaderLock(200); - - List crashedClientsCallback = null; - - try - { - foreach (var callback in mClientCallbacks) - { - try - { - callback.OnQuoteUpdate(quote); - } - catch (Exception) - { - if (crashedClientsCallback == null) - crashedClientsCallback = new List(); - - crashedClientsCallback.Add(callback); - } - } - } - finally - { - mClientsLocker.ReleaseReaderLock(); - } - - removeCrashedClientCallbacks(crashedClientsCallback); - } - catch (ApplicationException) - { - } + ExecuteCallbackAction(a => a.OnQuoteUpdate(quote)); } public void OnQuoteAdded(MtQuote quote) { - try - { - mClientsLocker.AcquireReaderLock(2000); - - List crashedClientsCallback = null; - - try - { - foreach (var callback in mClientCallbacks) - { - try - { - callback.OnQuoteAdded(quote); - } - catch (Exception) - { - if (crashedClientsCallback == null) - crashedClientsCallback = new List(); - - crashedClientsCallback.Add(callback); - } - } - } - finally - { - mClientsLocker.ReleaseReaderLock(); - } - - removeCrashedClientCallbacks(crashedClientsCallback); - } - catch (ApplicationException) - { - } + ExecuteCallbackAction(a => a.OnQuoteAdded(quote)); } public void OnQuoteRemoved(MtQuote quote) { - try - { - mClientsLocker.AcquireReaderLock(2000); + ExecuteCallbackAction(a => a.OnQuoteRemoved(quote)); + } - List crashedClientsCallback = null; - - try - { - foreach (var callback in mClientCallbacks) - { - try - { - callback.OnQuoteRemoved(quote); - } - catch (Exception) - { - if (crashedClientsCallback == null) - crashedClientsCallback = new List(); - - crashedClientsCallback.Add(callback); - } - } - } - finally - { - mClientsLocker.ReleaseReaderLock(); - } - - removeCrashedClientCallbacks(crashedClientsCallback); - } - catch (ApplicationException) - { - } + public void OnMtEvent(MtEvent mtEvent) + { + ExecuteCallbackAction(a => a.OnMtEvent(mtEvent)); } #endregion #region Private Methods - private void removeCrashedClientCallbacks(List crashedClientCallbacks) + private void ExecuteCallbackAction(Action action) { - if (crashedClientCallbacks != null) + try { + mClientsLocker.AcquireReaderLock(2000); + + List crashedClientCallbacks = null; + try { - mClientsLocker.AcquireWriterLock(200); + foreach (var callback in mClientCallbacks) + { + try + { + action(callback); + } + catch (Exception) + { + if (crashedClientCallbacks == null) + crashedClientCallbacks = new List(); - try + crashedClientCallbacks.Add(callback); + } + } + + if (crashedClientCallbacks != null) { foreach (var crashedCallback in crashedClientCallbacks) { mClientCallbacks.Remove(crashedCallback); } } - finally - { - mClientsLocker.ReleaseWriterLock(); - } } - catch (ApplicationException) + finally { + mClientsLocker.ReleaseReaderLock(); } } + catch (ApplicationException) + { + //TODO: add logging + } } #endregion diff --git a/MTConnector/MTConnector.cpp b/MTConnector/MTConnector.cpp index 83e799ef..0e0805b7 100755 --- a/MTConnector/MTConnector.cpp +++ b/MTConnector/MTConnector.cpp @@ -71,6 +71,22 @@ int _stdcall updateQuote(int expertHandle, wchar_t* symbol, double bid, double a return 1; } + +int _stdcall sendEvent(int expertHandle, int eventType, wchar_t* payload, wchar_t* err) +{ + try + { + MtServerInstance::GetInstance()->SendEvent(expertHandle, eventType, gcnew String(payload)); + } + catch (Exception^ e) + { + convertSystemString(err, e->Message); + Debug::WriteLine("[ERROR] MTConnector:updateTimeBar(): " + e->Message); + return 0; + } + return 1; +} + int _stdcall sendIntResponse(int expertHandle, int response) { try diff --git a/MTConnector/MTConnector.def b/MTConnector/MTConnector.def index c3078fdd..4957da7b 100755 --- a/MTConnector/MTConnector.def +++ b/MTConnector/MTConnector.def @@ -3,6 +3,7 @@ LIBRARY MTConnector EXPORTS initExpert deinitExpert updateQuote + sendEvent sendIntResponse sendBooleanResponse sendDoubleResponse diff --git a/MtApi/ExtensionMethods.cs b/MtApi/ExtensionMethods.cs index 64f33ad1..fa33115c 100755 --- a/MtApi/ExtensionMethods.cs +++ b/MtApi/ExtensionMethods.cs @@ -1,14 +1,8 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace MtApi +namespace MtApi { static class ExtensionMethods { - public static MtQuote Parse(this MTApiService.MtQuote quote) + public static MtQuote Convert(this MTApiService.MtQuote quote) { return (quote != null) ? new MtQuote(quote.Instrument, quote.Bid, quote.Ask) : null; } diff --git a/MtApi/MtApi.csproj b/MtApi/MtApi.csproj index 0cc71a28..0d0ef21b 100755 --- a/MtApi/MtApi.csproj +++ b/MtApi/MtApi.csproj @@ -66,10 +66,12 @@ + + @@ -95,6 +97,7 @@ + diff --git a/MtApi/MtApiClient.cs b/MtApi/MtApiClient.cs index 47be5034..04b06838 100755 --- a/MtApi/MtApiClient.cs +++ b/MtApi/MtApiClient.cs @@ -29,11 +29,12 @@ namespace MtApi public MtApiClient() { - _client.QuoteAdded +=new MtClient.MtQuoteHandler(mClient_QuoteAdded); - _client.QuoteRemoved += new MtClient.MtQuoteHandler(mClient_QuoteRemoved); - _client.QuoteUpdated += new MtClient.MtQuoteHandler(mClient_QuoteUpdated); - _client.ServerDisconnected += new EventHandler(mClient_ServerDisconnected); - _client.ServerFailed += new EventHandler(mClient_ServerFailed); + _client.QuoteAdded += mClient_QuoteAdded; + _client.QuoteRemoved += mClient_QuoteRemoved; + _client.QuoteUpdated += mClient_QuoteUpdated; + _client.ServerDisconnected += mClient_ServerDisconnected; + _client.ServerFailed += mClient_ServerFailed; + _client.MtEventReceived += _client_MtEventReceived; } #endregion @@ -81,7 +82,7 @@ namespace MtApi public IEnumerable GetQuotes() { var quotes = _client.GetQuotes(); - return quotes != null ? (from q in quotes select q.Parse()) : null; + return quotes != null ? (from q in quotes select q.Convert()) : null; } #endregion @@ -1597,14 +1598,35 @@ namespace MtApi private void mClient_QuoteRemoved(MTApiService.MtQuote quote) { var handler = QuoteRemoved; - handler?.BeginInvoke(this, new MtQuoteEventArgs(quote.Parse()), (a) => handler.EndInvoke(a), null); + handler?.BeginInvoke(this, new MtQuoteEventArgs(quote.Convert()), (a) => handler.EndInvoke(a), null); } private void mClient_QuoteAdded(MTApiService.MtQuote quote) { var handler = QuoteAdded; - handler?.BeginInvoke(this, new MtQuoteEventArgs(quote.Parse()), (a) => handler.EndInvoke(a), null); + handler?.BeginInvoke(this, new MtQuoteEventArgs(quote.Convert()), (a) => handler.EndInvoke(a), null); } + + private void _client_MtEventReceived(object sender, MtEventArgs e) + { + MtEventTypes eventType = (MtEventTypes) e.Event.EventType; + + switch(eventType) + { + case MtEventTypes.LastTimeBar: + { + FireOnLastTimeBar(JsonConvert.DeserializeObject(e.Event.Payload)); + } + break; + } + } + + private void FireOnLastTimeBar(MtTimeBar timeBar) + { + var handler = OnLastTimeBar; + handler?.BeginInvoke(this, new TimeBarArgs(timeBar), (a) => handler.EndInvoke(a), null); + } + #endregion #region Events @@ -1612,6 +1634,7 @@ namespace MtApi public event EventHandler QuoteAdded; public event EventHandler QuoteRemoved; public event EventHandler ConnectionStateChanged; + public event EventHandler OnLastTimeBar; #endregion #region Private Fields diff --git a/MtApi/MtEventTypes.cs b/MtApi/MtEventTypes.cs new file mode 100644 index 00000000..28616df7 --- /dev/null +++ b/MtApi/MtEventTypes.cs @@ -0,0 +1,7 @@ +namespace MtApi +{ + public enum MtEventTypes + { + LastTimeBar = 1 + } +} diff --git a/MtApi/MtTimeBar.cs b/MtApi/MtTimeBar.cs new file mode 100644 index 00000000..dfb1208a --- /dev/null +++ b/MtApi/MtTimeBar.cs @@ -0,0 +1,26 @@ +using System; + +namespace MtApi +{ + public class MtTimeBar + { + public string Symbol { get; set; } + public int MtOpenTime { get; set; } + public int MtCloseTime { get; set; } + public double Open { get; set; } + public double Close { get; set; } + public double High { get; set; } + public double Low { get; set; } + + public DateTime OpenTime + { + get { return MtApiTimeConverter.ConvertFromMtTime(MtOpenTime); } + } + + public DateTime CloseTime + { + get { return MtApiTimeConverter.ConvertFromMtTime(MtCloseTime); } + } + + } +} diff --git a/MtApi/TimeBarArgs.cs b/MtApi/TimeBarArgs.cs new file mode 100644 index 00000000..e8ae32ba --- /dev/null +++ b/MtApi/TimeBarArgs.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MtApi +{ + public class TimeBarArgs: EventArgs + { + public TimeBarArgs(MtTimeBar timeBar) + { + TimeBar = timeBar; + } + + public MtTimeBar TimeBar { get; private set; } + } +} diff --git a/TestClients/TestApiClientUI/Form1.cs b/TestClients/TestApiClientUI/Form1.cs index f62aa984..9c8f4d50 100644 --- a/TestClients/TestApiClientUI/Form1.cs +++ b/TestClients/TestApiClientUI/Form1.cs @@ -23,6 +23,7 @@ namespace TestApiClientUI _apiClient.QuoteAdded += apiClient_QuoteAdded; _apiClient.QuoteRemoved += apiClient_QuoteRemoved; _apiClient.ConnectionStateChanged += apiClient_ConnectionStateChanged; + _apiClient.OnLastTimeBar += _apiClient_OnLastTimeBar; initOrderCommandsGroup(); @@ -95,6 +96,14 @@ namespace TestApiClientUI RunOnUiThread(() => AddNewQuote(e.Quote)); } + private void _apiClient_OnLastTimeBar(object sender, TimeBarArgs e) + { + string msg = string.Format("TimeBar: Symbol = {0}, OpenTime = {1}, CloseTime = {2}, Open = {3}, Close = {4}, High = {5}, Low = {6}", + e.TimeBar.Symbol, e.TimeBar.OpenTime, e.TimeBar.CloseTime, e.TimeBar.Open, e.TimeBar.Close, e.TimeBar.High, e.TimeBar.Low); + Console.WriteLine(msg); + AddToLog(msg); + } + private volatile bool IsUiQuoteUpdateReady = true; private void apiClient_QuoteUpdated(object sender, string symbol, double bid, double ask) diff --git a/mq4/MtApi.ex4 b/mq4/MtApi.ex4 index d1be98bb..d1dd3be7 100755 Binary files a/mq4/MtApi.ex4 and b/mq4/MtApi.ex4 differ diff --git a/mq4/MtApi.mq4 b/mq4/MtApi.mq4 index 6b8aeda7..2fae8c67 100755 Binary files a/mq4/MtApi.mq4 and b/mq4/MtApi.mq4 differ