mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-18 05:08:11 +00:00
Merge branch 'dev'
This commit is contained in:
+30
-20
@@ -123,14 +123,7 @@ namespace MtApi5
|
|||||||
throw new Exception($"Connection to {host}:{port} failed. Error: {errorMessage}");
|
throw new Exception($"Connection to {host}:{port} failed. Error: {errorMessage}");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load quotes
|
var quotes = LoadQuotes(client, experts);
|
||||||
Dictionary<int, Mt5Quote> quotes = [];
|
|
||||||
foreach (var handle in experts)
|
|
||||||
{
|
|
||||||
var quote = GetQuote(client, handle);
|
|
||||||
if (quote != null)
|
|
||||||
quotes[handle] = quote;
|
|
||||||
}
|
|
||||||
|
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
@@ -160,6 +153,18 @@ namespace MtApi5
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Dictionary<int, Mt5Quote> LoadQuotes(MtRpcClient client, HashSet<int> experts)
|
||||||
|
{
|
||||||
|
Dictionary<int, Mt5Quote> quotes = [];
|
||||||
|
foreach (var handle in experts)
|
||||||
|
{
|
||||||
|
var quote = GetQuote(client, handle);
|
||||||
|
if (quote != null)
|
||||||
|
quotes[handle] = quote;
|
||||||
|
}
|
||||||
|
return quotes;
|
||||||
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
///Disconnect from MetaTrader API. Async method.
|
///Disconnect from MetaTrader API. Async method.
|
||||||
///</summary>
|
///</summary>
|
||||||
@@ -183,10 +188,22 @@ namespace MtApi5
|
|||||||
///</summary>
|
///</summary>
|
||||||
public IEnumerable<Mt5Quote> GetQuotes()
|
public IEnumerable<Mt5Quote> GetQuotes()
|
||||||
{
|
{
|
||||||
|
MtRpcClient? client;
|
||||||
|
HashSet<int> experts;
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
return _quotes.Values.ToList();
|
client = _client;
|
||||||
|
experts = new HashSet<int>(_experts);
|
||||||
}
|
}
|
||||||
|
if (client == null)
|
||||||
|
{
|
||||||
|
Log.Warn("GetQuotes: No connection");
|
||||||
|
throw new Exception("No connection");
|
||||||
|
}
|
||||||
|
|
||||||
|
var quotes = LoadQuotes(client, experts);
|
||||||
|
|
||||||
|
return quotes.Values.ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
///<summary>
|
///<summary>
|
||||||
@@ -3402,11 +3419,6 @@ namespace MtApi5
|
|||||||
var quote = GetQuote(Client, handle);
|
var quote = GetQuote(Client, handle);
|
||||||
if (quote != null)
|
if (quote != null)
|
||||||
{
|
{
|
||||||
lock (_locker)
|
|
||||||
{
|
|
||||||
_quotes[handle] = quote;
|
|
||||||
}
|
|
||||||
|
|
||||||
QuoteAdded?.Invoke(this, new Mt5QuoteEventArgs(quote));
|
QuoteAdded?.Invoke(this, new Mt5QuoteEventArgs(quote));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -3421,19 +3433,18 @@ namespace MtApi5
|
|||||||
{
|
{
|
||||||
Log.Debug($"ProcessExpertRemoved: {handle}");
|
Log.Debug($"ProcessExpertRemoved: {handle}");
|
||||||
|
|
||||||
Mt5Quote? quote = null;
|
Mt5Quote? quote;
|
||||||
lock (_locker)
|
lock (_locker)
|
||||||
{
|
{
|
||||||
|
_quotes.TryGetValue(handle, out quote);
|
||||||
|
_quotes.Remove(handle);
|
||||||
_experts.Remove(handle);
|
_experts.Remove(handle);
|
||||||
if (_quotes.TryGetValue(handle, out quote))
|
|
||||||
_quotes.Remove(handle);
|
|
||||||
if (_executorHandle == handle)
|
if (_executorHandle == handle)
|
||||||
_executorHandle = (_experts.Count > 0) ? _experts.ElementAt(0) : 0;
|
_executorHandle = (_experts.Count > 0) ? _experts.ElementAt(0) : 0;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quote != null)
|
if (quote != null)
|
||||||
QuoteRemoved?.Invoke(this, new Mt5QuoteEventArgs(quote));
|
QuoteRemoved?.Invoke(this, new Mt5QuoteEventArgs(new Mt5Quote { Instrument = quote.Instrument, ExpertHandle = quote.ExpertHandle }));
|
||||||
}
|
}
|
||||||
|
|
||||||
private Mt5Quote? GetQuote(MtRpcClient? client, int expertHandle)
|
private Mt5Quote? GetQuote(MtRpcClient? client, int expertHandle)
|
||||||
@@ -3550,7 +3561,6 @@ namespace MtApi5
|
|||||||
client = _client;
|
client = _client;
|
||||||
_client = null;
|
_client = null;
|
||||||
|
|
||||||
_quotes.Clear();
|
|
||||||
_experts.Clear();
|
_experts.Clear();
|
||||||
_executorHandle = 0;
|
_executorHandle = 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,12 +8,12 @@
|
|||||||
ExpertList = 3,
|
ExpertList = 3,
|
||||||
ExpertAdded = 4,
|
ExpertAdded = 4,
|
||||||
ExpertRemoved = 5,
|
ExpertRemoved = 5,
|
||||||
Notification = 6
|
ServiceRequest = 6
|
||||||
}
|
}
|
||||||
|
|
||||||
internal enum MtNotificationType
|
internal enum ServiceRequestType
|
||||||
{
|
{
|
||||||
ClientReady = 0
|
ExpertList = 0
|
||||||
}
|
}
|
||||||
|
|
||||||
internal abstract class MtMessage
|
internal abstract class MtMessage
|
||||||
@@ -43,16 +43,16 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MtNotification(MtNotificationType notificationType) : MtMessage
|
internal class MtServiceRequest(ServiceRequestType requestType) : MtMessage
|
||||||
{
|
{
|
||||||
public override MessageType MsgType => MessageType.Notification;
|
public override MessageType MsgType => MessageType.ServiceRequest;
|
||||||
|
|
||||||
protected override string GetMessageBody()
|
protected override string GetMessageBody()
|
||||||
{
|
{
|
||||||
return $"{(int)NotificationType}";
|
return $"{(int)ServiceRequestType}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public MtNotificationType NotificationType { private set; get; } = notificationType;
|
public ServiceRequestType ServiceRequestType { private set; get; } = requestType;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal class MtEvent(int expertHandle, int eventType, string payload) : MtMessage
|
internal class MtEvent(int expertHandle, int eventType, string payload) : MtMessage
|
||||||
|
|||||||
+11
-11
@@ -89,19 +89,19 @@ namespace MtClient
|
|||||||
|
|
||||||
public HashSet<int>? RequestExpertsList()
|
public HashSet<int>? RequestExpertsList()
|
||||||
{
|
{
|
||||||
CommandTask<object> notificationTask = new();
|
CommandTask<object> requestTask = new();
|
||||||
lock (notification_tasks_)
|
lock (service_requests_)
|
||||||
{
|
{
|
||||||
notification_tasks_[MtNotificationType.ClientReady] = notificationTask;
|
service_requests_[ServiceRequestType.ExpertList] = requestTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
MtNotification notification = new(MtNotificationType.ClientReady);
|
MtServiceRequest request = new(ServiceRequestType.ExpertList);
|
||||||
Send(notification);
|
Send(request);
|
||||||
|
|
||||||
var response = notificationTask.WaitResponse(10000); // 10 sec
|
var response = requestTask.WaitResponse(10000); // 10 sec
|
||||||
lock (notification_tasks_)
|
lock (service_requests_)
|
||||||
{
|
{
|
||||||
notification_tasks_.Remove(MtNotificationType.ClientReady);
|
service_requests_.Remove(ServiceRequestType.ExpertList);
|
||||||
}
|
}
|
||||||
|
|
||||||
return response as HashSet<int>;
|
return response as HashSet<int>;
|
||||||
@@ -277,9 +277,9 @@ namespace MtClient
|
|||||||
{
|
{
|
||||||
logger_.Debug($"MtRpcClient.ProcessExpertList: experts count - {experts.Count}");
|
logger_.Debug($"MtRpcClient.ProcessExpertList: experts count - {experts.Count}");
|
||||||
|
|
||||||
lock (notification_tasks_)
|
lock (service_requests_)
|
||||||
{
|
{
|
||||||
if (notification_tasks_.TryGetValue(MtNotificationType.ClientReady, out CommandTask<object>? value))
|
if (service_requests_.TryGetValue(ServiceRequestType.ExpertList, out CommandTask<object>? value))
|
||||||
value.SetResponse(experts);
|
value.SetResponse(experts);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -301,7 +301,7 @@ namespace MtClient
|
|||||||
|
|
||||||
private int nextCommandId = 0;
|
private int nextCommandId = 0;
|
||||||
private readonly Dictionary<int, CommandTask<string>> tasks_ = [];
|
private readonly Dictionary<int, CommandTask<string>> tasks_ = [];
|
||||||
private readonly Dictionary<MtNotificationType, CommandTask<object>> notification_tasks_ = [];
|
private readonly Dictionary<ServiceRequestType, CommandTask<object>> service_requests_ = [];
|
||||||
|
|
||||||
private readonly IRpcLogger logger_;
|
private readonly IRpcLogger logger_;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ std::unique_ptr<MtCommand> MtCommand::Parse(const std::string& msg)
|
|||||||
return command;
|
return command;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<MtNotification> MtNotification::Parse(const std::string& msg)
|
std::unique_ptr<MtServiceRequest> MtServiceRequest::Parse(const std::string& msg)
|
||||||
{
|
{
|
||||||
std::string::size_type pos = msg.find(';');
|
std::string::size_type pos = msg.find(';');
|
||||||
|
|
||||||
@@ -47,17 +47,17 @@ std::unique_ptr<MtNotification> MtNotification::Parse(const std::string& msg)
|
|||||||
using rule = qi::rule<std::string::const_iterator>;
|
using rule = qi::rule<std::string::const_iterator>;
|
||||||
|
|
||||||
std::string message_type;
|
std::string message_type;
|
||||||
std::string notification_type;
|
std::string request_type;
|
||||||
|
|
||||||
rule_s word_p = qi::as_string[+(qi::char_ - qi::char_(';'))];
|
rule_s word_p = qi::as_string[+(qi::char_ - qi::char_(';'))];
|
||||||
rule message_type_p = word_p[boost::phoenix::ref(message_type) = qi::_1];
|
rule message_type_p = word_p[boost::phoenix::ref(message_type) = qi::_1];
|
||||||
rule notification_type_p = +qi::char_(';') >> word_p[boost::phoenix::ref(notification_type) = qi::_1];
|
rule notification_type_p = +qi::char_(';') >> word_p[boost::phoenix::ref(request_type) = qi::_1];
|
||||||
|
|
||||||
std::unique_ptr<MtNotification> command;
|
std::unique_ptr<MtServiceRequest> request;
|
||||||
|
|
||||||
bool ok = qi::parse(msg.begin(), msg.end(), message_type_p >> -notification_type_p);
|
bool ok = qi::parse(msg.begin(), msg.end(), message_type_p >> -notification_type_p);
|
||||||
if (ok)
|
if (ok)
|
||||||
command = std::make_unique<MtNotification>((NotificationType)std::stoi(notification_type));
|
request = std::make_unique<MtServiceRequest>((ServiceRequestType)std::stoi(request_type));
|
||||||
|
|
||||||
return command;
|
return request;
|
||||||
}
|
}
|
||||||
+11
-11
@@ -14,12 +14,12 @@ enum MessageType
|
|||||||
EXPERT_LIST = 3,
|
EXPERT_LIST = 3,
|
||||||
EXPERT_ADDED = 4,
|
EXPERT_ADDED = 4,
|
||||||
EXPERT_REMOVED = 5,
|
EXPERT_REMOVED = 5,
|
||||||
NOTIFICATION = 6
|
SERVICE_REQUEST = 6
|
||||||
};
|
};
|
||||||
|
|
||||||
enum NotificationType
|
enum ServiceRequestType
|
||||||
{
|
{
|
||||||
CLIENT_READY = 0
|
EXPERTS = 0
|
||||||
};
|
};
|
||||||
|
|
||||||
class MtMessage
|
class MtMessage
|
||||||
@@ -111,25 +111,25 @@ private:
|
|||||||
std::string payload_;
|
std::string payload_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MtNotification : public MtMessage
|
class MtServiceRequest : public MtMessage
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MtNotification(NotificationType type)
|
MtServiceRequest(ServiceRequestType type)
|
||||||
: notification_type_(type)
|
: request_type_(type)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationType GetNotificationType() const
|
ServiceRequestType GetServiceRequestType() const
|
||||||
{
|
{
|
||||||
return notification_type_;
|
return request_type_;
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::unique_ptr<MtNotification> Parse(const std::string& msg);
|
static std::unique_ptr<MtServiceRequest> Parse(const std::string& msg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MessageType GetType() const override
|
MessageType GetType() const override
|
||||||
{
|
{
|
||||||
return MessageType::NOTIFICATION;
|
return MessageType::SERVICE_REQUEST;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string GetBody() const override
|
std::string GetBody() const override
|
||||||
@@ -137,7 +137,7 @@ private:
|
|||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
|
||||||
NotificationType notification_type_;
|
ServiceRequestType request_type_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class MtEvent : public MtMessage
|
class MtEvent : public MtMessage
|
||||||
|
|||||||
@@ -288,12 +288,12 @@ void MtServer::ProcessMessage(const std::string& msg, std::weak_ptr<MtConnection
|
|||||||
else
|
else
|
||||||
log_.Warning("%s: Failed to parse command from message: %s", __FUNCTION__, msg.c_str());
|
log_.Warning("%s: Failed to parse command from message: %s", __FUNCTION__, msg.c_str());
|
||||||
}
|
}
|
||||||
else if (msg_type == MessageType::NOTIFICATION)
|
else if (msg_type == MessageType::SERVICE_REQUEST)
|
||||||
{
|
{
|
||||||
auto notification = MtNotification::Parse(msg);
|
auto request = MtServiceRequest::Parse(msg);
|
||||||
if (notification)
|
if (request)
|
||||||
{
|
{
|
||||||
if (notification->GetNotificationType() == NotificationType::CLIENT_READY)
|
if (request->GetServiceRequestType() == ServiceRequestType::EXPERTS)
|
||||||
{
|
{
|
||||||
std::vector<int> expert_list;
|
std::vector<int> expert_list;
|
||||||
for (const auto& e : experts_)
|
for (const auto& e : experts_)
|
||||||
@@ -306,7 +306,7 @@ void MtServer::ProcessMessage(const std::string& msg, std::weak_ptr<MtConnection
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
log_.Warning("%s: Failed to parse notification from message: %s", __FUNCTION__, msg.c_str());
|
log_.Warning("%s: Failed to parse service request from message: %s", __FUNCTION__, msg.c_str());
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -166,6 +166,7 @@
|
|||||||
<Grid.RowDefinitions>
|
<Grid.RowDefinitions>
|
||||||
<RowDefinition Height="Auto"/>
|
<RowDefinition Height="Auto"/>
|
||||||
<RowDefinition Height="*"/>
|
<RowDefinition Height="*"/>
|
||||||
|
<RowDefinition Height="Auto"/>
|
||||||
</Grid.RowDefinitions>
|
</Grid.RowDefinitions>
|
||||||
<Label Grid.Row="0" Content="Quotes" Background="LightYellow" />
|
<Label Grid.Row="0" Content="Quotes" Background="LightYellow" />
|
||||||
<ListView Grid.Row="1" Margin="2"
|
<ListView Grid.Row="1" Margin="2"
|
||||||
@@ -181,6 +182,7 @@
|
|||||||
</GridView>
|
</GridView>
|
||||||
</ListView.View>
|
</ListView.View>
|
||||||
</ListView>
|
</ListView>
|
||||||
|
<Button Grid.Row="2" Content="Refresh Quotes" Margin="2" Command="{Binding RefreshQuotesCommand}"/>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Border>
|
</Border>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
@@ -162,6 +162,7 @@ namespace MtApi5TestClient
|
|||||||
public DelegateCommand UnlockTicksCommand { get; private set; }
|
public DelegateCommand UnlockTicksCommand { get; private set; }
|
||||||
|
|
||||||
public DelegateCommand GetSymbolsCommand { get; private set; }
|
public DelegateCommand GetSymbolsCommand { get; private set; }
|
||||||
|
public DelegateCommand RefreshQuotesCommand { get; private set; }
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Properties
|
#region Properties
|
||||||
@@ -492,6 +493,7 @@ namespace MtApi5TestClient
|
|||||||
UnlockTicksCommand = new DelegateCommand(ExecuteUnlockTicks);
|
UnlockTicksCommand = new DelegateCommand(ExecuteUnlockTicks);
|
||||||
|
|
||||||
GetSymbolsCommand = new DelegateCommand(ExecuteGetSymbols);
|
GetSymbolsCommand = new DelegateCommand(ExecuteGetSymbols);
|
||||||
|
RefreshQuotesCommand = new DelegateCommand(ExecuteRefreshQuotes);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanExecuteConnect(object o)
|
private bool CanExecuteConnect(object o)
|
||||||
@@ -1756,6 +1758,20 @@ namespace MtApi5TestClient
|
|||||||
Symbols = result;
|
Symbols = result;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
private async void ExecuteRefreshQuotes(object o)
|
||||||
|
{
|
||||||
|
_quotesMap.Clear();
|
||||||
|
Quotes.Clear();
|
||||||
|
|
||||||
|
var quotes = await Execute(() => _mtApiClient.GetQuotes());
|
||||||
|
if (quotes != null)
|
||||||
|
{
|
||||||
|
foreach (var quote in quotes)
|
||||||
|
{
|
||||||
|
AddQuote(quote);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static void RunOnUiThread(Action action)
|
private static void RunOnUiThread(Action action)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user