mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-07-27 18:47:55 +00:00
Issue #262: function GetQuotes load data from MT instead of using cached quotes
This commit is contained in:
+30
-20
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user