Issue #262: function GetQuotes load data from MT instead of using cached quotes

This commit is contained in:
Viacheslav Demydiuk
2025-12-05 23:43:27 +02:00
parent 0c874773e5
commit 249b0f9fbb
8 changed files with 88 additions and 60 deletions
+30 -20
View File
@@ -123,14 +123,7 @@ namespace MtApi5
throw new Exception($"Connection to {host}:{port} failed. Error: {errorMessage}");
}
// Load quotes
Dictionary<int, Mt5Quote> quotes = [];
foreach (var handle in experts)
{
var quote = GetQuote(client, handle);
if (quote != null)
quotes[handle] = quote;
}
var quotes = LoadQuotes(client, experts);
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>
///Disconnect from MetaTrader API. Async method.
///</summary>
@@ -183,10 +188,22 @@ namespace MtApi5
///</summary>
public IEnumerable<Mt5Quote> GetQuotes()
{
MtRpcClient? client;
HashSet<int> experts;
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>
@@ -3402,11 +3419,6 @@ namespace MtApi5
var quote = GetQuote(Client, handle);
if (quote != null)
{
lock (_locker)
{
_quotes[handle] = quote;
}
QuoteAdded?.Invoke(this, new Mt5QuoteEventArgs(quote));
}
else
@@ -3421,19 +3433,18 @@ namespace MtApi5
{
Log.Debug($"ProcessExpertRemoved: {handle}");
Mt5Quote? quote = null;
Mt5Quote? quote;
lock (_locker)
{
_quotes.TryGetValue(handle, out quote);
_quotes.Remove(handle);
_experts.Remove(handle);
if (_quotes.TryGetValue(handle, out quote))
_quotes.Remove(handle);
if (_executorHandle == handle)
_executorHandle = (_experts.Count > 0) ? _experts.ElementAt(0) : 0;
}
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)
@@ -3550,7 +3561,6 @@ namespace MtApi5
client = _client;
_client = null;
_quotes.Clear();
_experts.Clear();
_executorHandle = 0;
}
+7 -7
View File
@@ -8,12 +8,12 @@
ExpertList = 3,
ExpertAdded = 4,
ExpertRemoved = 5,
Notification = 6
ServiceRequest = 6
}
internal enum MtNotificationType
internal enum ServiceRequestType
{
ClientReady = 0
ExpertList = 0
}
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()
{
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
+11 -11
View File
@@ -89,19 +89,19 @@ namespace MtClient
public HashSet<int>? RequestExpertsList()
{
CommandTask<object> notificationTask = new();
lock (notification_tasks_)
CommandTask<object> requestTask = new();
lock (service_requests_)
{
notification_tasks_[MtNotificationType.ClientReady] = notificationTask;
service_requests_[ServiceRequestType.ExpertList] = requestTask;
}
MtNotification notification = new(MtNotificationType.ClientReady);
Send(notification);
MtServiceRequest request = new(ServiceRequestType.ExpertList);
Send(request);
var response = notificationTask.WaitResponse(10000); // 10 sec
lock (notification_tasks_)
var response = requestTask.WaitResponse(10000); // 10 sec
lock (service_requests_)
{
notification_tasks_.Remove(MtNotificationType.ClientReady);
service_requests_.Remove(ServiceRequestType.ExpertList);
}
return response as HashSet<int>;
@@ -277,9 +277,9 @@ namespace MtClient
{
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);
}
}
@@ -301,7 +301,7 @@ namespace MtClient
private int nextCommandId = 0;
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_;
}
+6 -6
View File
@@ -38,7 +38,7 @@ std::unique_ptr<MtCommand> MtCommand::Parse(const std::string& msg)
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(';');
@@ -47,17 +47,17 @@ std::unique_ptr<MtNotification> MtNotification::Parse(const std::string& msg)
using rule = qi::rule<std::string::const_iterator>;
std::string message_type;
std::string notification_type;
std::string request_type;
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 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);
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
View File
@@ -14,12 +14,12 @@ enum MessageType
EXPERT_LIST = 3,
EXPERT_ADDED = 4,
EXPERT_REMOVED = 5,
NOTIFICATION = 6
SERVICE_REQUEST = 6
};
enum NotificationType
enum ServiceRequestType
{
CLIENT_READY = 0
EXPERTS = 0
};
class MtMessage
@@ -111,25 +111,25 @@ private:
std::string payload_;
};
class MtNotification : public MtMessage
class MtServiceRequest : public MtMessage
{
public:
MtNotification(NotificationType type)
: notification_type_(type)
MtServiceRequest(ServiceRequestType 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:
MessageType GetType() const override
{
return MessageType::NOTIFICATION;
return MessageType::SERVICE_REQUEST;
}
std::string GetBody() const override
@@ -137,7 +137,7 @@ private:
return "";
}
NotificationType notification_type_;
ServiceRequestType request_type_;
};
class MtEvent : public MtMessage
+5 -5
View File
@@ -288,12 +288,12 @@ void MtServer::ProcessMessage(const std::string& msg, std::weak_ptr<MtConnection
else
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);
if (notification)
auto request = MtServiceRequest::Parse(msg);
if (request)
{
if (notification->GetNotificationType() == NotificationType::CLIENT_READY)
if (request->GetServiceRequestType() == ServiceRequestType::EXPERTS)
{
std::vector<int> expert_list;
for (const auto& e : experts_)
@@ -306,7 +306,7 @@ void MtServer::ProcessMessage(const std::string& msg, std::weak_ptr<MtConnection
}
}
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
{
@@ -166,6 +166,7 @@
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
<RowDefinition Height="Auto"/>
</Grid.RowDefinitions>
<Label Grid.Row="0" Content="Quotes" Background="LightYellow" />
<ListView Grid.Row="1" Margin="2"
@@ -181,6 +182,7 @@
</GridView>
</ListView.View>
</ListView>
<Button Grid.Row="2" Content="Refresh Quotes" Margin="2" Command="{Binding RefreshQuotesCommand}"/>
</Grid>
</Border>
</Grid>
+16
View File
@@ -162,6 +162,7 @@ namespace MtApi5TestClient
public DelegateCommand UnlockTicksCommand { get; private set; }
public DelegateCommand GetSymbolsCommand { get; private set; }
public DelegateCommand RefreshQuotesCommand { get; private set; }
#endregion
#region Properties
@@ -492,6 +493,7 @@ namespace MtApi5TestClient
UnlockTicksCommand = new DelegateCommand(ExecuteUnlockTicks);
GetSymbolsCommand = new DelegateCommand(ExecuteGetSymbols);
RefreshQuotesCommand = new DelegateCommand(ExecuteRefreshQuotes);
}
private bool CanExecuteConnect(object o)
@@ -1756,6 +1758,20 @@ namespace MtApi5TestClient
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)
{