mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-07-30 12:07:51 +00:00
Compare commits
36 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| a77c08258e | |||
| 3699b73cf7 | |||
| 9d178280d0 | |||
| a8bb1a3af9 | |||
| fa1e6b6c62 | |||
| 9e26a1d262 | |||
| 249b0f9fbb | |||
| 0a1ec30b84 | |||
| 0c874773e5 | |||
| 2c4d9b130b | |||
| a900ea8968 | |||
| a5b8e33611 | |||
| b5309fb9a4 | |||
| 2f0ccae238 | |||
| 371c14b4b7 | |||
| 565735c3c5 | |||
| 1bb9c7b418 | |||
| 7b5f740a77 | |||
| 22b0f695c6 | |||
| c536585cd2 | |||
| 29ce645975 | |||
| 92322e866e | |||
| 34072905d2 | |||
| 96674d8763 | |||
| 5808756113 | |||
| fe8ac339b6 | |||
| 972df57028 | |||
| 6c1cb92f1b | |||
| ff871e5e06 | |||
| c7b8956c99 | |||
| db3db18391 | |||
| 143cc33c60 | |||
| 541fc2364b | |||
| 78b4b7039b | |||
| 4a61ab079c | |||
| 69d98b66b3 |
@@ -89,7 +89,7 @@ namespace MtApi.Monitors
|
||||
{
|
||||
//get closed orders from history with actual values
|
||||
var historyOrders = ApiClient.GetOrders(OrderSelectSource.MODE_HISTORY) ?? [];
|
||||
closedOrders = closeOrdersTemp.Where(cot => historyOrders.Find(a => a.Ticket == cot.Ticket) != null).ToList();
|
||||
closedOrders = historyOrders.Where(cot => closeOrdersTemp.Find(a => a.Ticket == cot.Ticket) != null).ToList();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -502,6 +502,12 @@ namespace MtApi
|
||||
Dictionary<string, object> cmdParams = new() { { "Pool", (int)pool} };
|
||||
return SendCommand<List<MtOrder>>(ExecutorHandle, MtCommandType.GetOrders, cmdParams);
|
||||
}
|
||||
|
||||
public List<string>? GetSymbols(bool selected)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Selected", selected } };
|
||||
return SendCommand<List<string>>(ExecutorHandle, MtCommandType.GetSymbols, cmdParams);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Checkup
|
||||
@@ -1131,7 +1137,7 @@ namespace MtApi
|
||||
return SendCommand<double>(ExecutorHandle, MtCommandType.iBearsPower, cmdParams);
|
||||
}
|
||||
|
||||
public double iBands(string symbol, int timeframe, int period, int deviation, int bandsShift, int appliedPrice, int mode, int shift)
|
||||
public double iBands(string symbol, int timeframe, int period, double deviation, int bandsShift, int appliedPrice, int mode, int shift)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Symbol", symbol },
|
||||
{ "Timeframe", timeframe }, { "Period", period },
|
||||
@@ -1141,7 +1147,7 @@ namespace MtApi
|
||||
return SendCommand<double>(ExecutorHandle, MtCommandType.iBands, cmdParams);
|
||||
}
|
||||
|
||||
public double iBandsOnArray(double[] array, int total, int period, int deviation, int bandsShift, int mode, int shift)
|
||||
public double iBandsOnArray(double[] array, int total, int period, double deviation, int bandsShift, int mode, int shift)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Total", total },
|
||||
{ "Period", period }, { "Deviation", deviation },
|
||||
|
||||
@@ -287,6 +287,7 @@
|
||||
SymbolInfoTick = 288,
|
||||
SymbolInfoDouble = 289,
|
||||
|
||||
GetQuote = 290
|
||||
GetQuote = 290,
|
||||
GetSymbols = 291
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,12 +5,18 @@ namespace MtApi5
|
||||
public class MqlTick
|
||||
{
|
||||
public MqlTick(DateTime time, double bid, double ask, double last, ulong volume)
|
||||
: this(time, bid, ask, last, volume, 0)
|
||||
{
|
||||
}
|
||||
|
||||
public MqlTick(DateTime time, double bid, double ask, double last, ulong volume, ENUM_TICK_FLAGS flags)
|
||||
{
|
||||
MtTime = Mt5TimeConverter.ConvertToMtTime(time);
|
||||
this.bid = bid;
|
||||
this.ask = ask;
|
||||
this.last = last;
|
||||
this.volume = volume;
|
||||
this.flags = flags;
|
||||
}
|
||||
|
||||
public MqlTick()
|
||||
@@ -27,6 +33,7 @@ namespace MtApi5
|
||||
last = tick.Last;
|
||||
volume = tick.Volume;
|
||||
volume_real = tick.VolumeReal;
|
||||
flags = (ENUM_TICK_FLAGS)tick.Flags;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -37,6 +44,7 @@ namespace MtApi5
|
||||
public double last { get; set; } // Price of the last deal (Last)
|
||||
public ulong volume { get; set; } // Volume for the current Last price
|
||||
public double volume_real { get; set; } // Volume for the current Last price with greater accuracy
|
||||
public ENUM_TICK_FLAGS flags { get; set; } // Tick flags (used for analyzing to find out what data have been changed)
|
||||
|
||||
public DateTime time => Mt5TimeConverter.ConvertFromMtTime(MtTime);
|
||||
}
|
||||
|
||||
@@ -862,6 +862,18 @@ namespace MtApi5
|
||||
STO_CLOSECLOSE = 1 //Calculation is based on Close/Close prices
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ENUM_TICK_FLAGS
|
||||
{
|
||||
TICK_FLAG_NONE = 0,
|
||||
TICK_FLAG_BID = 2, // Tick has changed a Bid price
|
||||
TICK_FLAG_ASK = 4, // Tick has changed an Ask price
|
||||
TICK_FLAG_LAST = 8, // Tick has changed the last deal price
|
||||
TICK_FLAG_VOLUME = 16, // Tick has changed a volume
|
||||
TICK_FLAG_BUY = 32, // Tick is a result of a buy deal
|
||||
TICK_FLAG_SELL = 64 // Tick is a result of a sell deal
|
||||
}
|
||||
|
||||
#endregion //Price Constants
|
||||
|
||||
#region Smoothing Methods
|
||||
|
||||
@@ -4,15 +4,17 @@ namespace MtApi5
|
||||
{
|
||||
public class Mt5TimeBarArgs: EventArgs
|
||||
{
|
||||
internal Mt5TimeBarArgs(int expertHandle, string symbol, MqlRates rates)
|
||||
internal Mt5TimeBarArgs(int expertHandle, string symbol, ENUM_TIMEFRAMES timeframe, MqlRates rates)
|
||||
{
|
||||
ExpertHandle = expertHandle;
|
||||
Rates = rates;
|
||||
Symbol = symbol;
|
||||
Timeframe = timeframe;
|
||||
}
|
||||
|
||||
public int ExpertHandle { get; }
|
||||
public string Symbol { get; }
|
||||
public ENUM_TIMEFRAMES Timeframe { get; }
|
||||
public MqlRates Rates { get; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<VersionPrefix>2.0.0</VersionPrefix>
|
||||
<VersionPrefix>2.0.1</VersionPrefix>
|
||||
<BaseOutputPath>$(SolutionDir)build\products</BaseOutputPath>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
</PropertyGroup>
|
||||
|
||||
+68
-35
@@ -2,6 +2,7 @@
|
||||
using MtClient;
|
||||
using MtApi5.MtProtocol;
|
||||
using MtApi5.MtProtocol.ICustomRequest;
|
||||
using System.Data;
|
||||
|
||||
namespace MtApi5
|
||||
{
|
||||
@@ -31,11 +32,24 @@ namespace MtApi5
|
||||
|
||||
private HashSet<int> _experts = [];
|
||||
private Dictionary<int, Mt5Quote> _quotes = [];
|
||||
private volatile int _command_timeout = 30000; // 30 seconds
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
private IMtLogger Log { get; }
|
||||
|
||||
// Time in milliseconds to wait for a response from MetaTrader for a command. Default is 30 seconds.
|
||||
public int CommandTimeout
|
||||
{
|
||||
get => _command_timeout;
|
||||
set
|
||||
{
|
||||
if (value <= 0)
|
||||
throw new ArgumentException("Command timeout must be greater than zero.");
|
||||
_command_timeout = value;
|
||||
}
|
||||
}
|
||||
|
||||
public MtApi5Client(IMtLogger? log = null)
|
||||
{
|
||||
_mtEventHandlers[Mt5EventTypes.OnBookEvent] = ReceivedOnBookEvent;
|
||||
@@ -122,14 +136,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)
|
||||
{
|
||||
@@ -159,6 +166,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>
|
||||
@@ -182,10 +201,31 @@ 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>
|
||||
///Load symbols
|
||||
///</summary>
|
||||
public List<string>? GetSymbols(bool selected)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Selected", selected } };
|
||||
return SendCommand<List<string>>(ExecutorHandle, Mt5CommandType.GetSymbols, cmdParams);
|
||||
}
|
||||
|
||||
///<summary>
|
||||
@@ -2664,7 +2704,7 @@ namespace MtApi5
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Symbol", symbol ?? string.Empty }, { "Period", (int)period },
|
||||
{ "FastMaPeriod", fastMaPeriod }, { "SlowMaPeriod", slowMaPeriod},
|
||||
{ "MaMethod", (int)maMethod }, { "appliedVolume", (int)appliedVolume } };
|
||||
{ "MaMethod", (int)maMethod }, { "AppliedVolume", (int)appliedVolume } };
|
||||
return SendCommand<int>(ExecutorHandle, Mt5CommandType.iChaikin, cmdParams);
|
||||
}
|
||||
|
||||
@@ -2730,14 +2770,14 @@ namespace MtApi5
|
||||
}
|
||||
|
||||
///<summary>
|
||||
///The function returns the handle of the Force Index indicator.
|
||||
///The function returns the handle of the Fractals indicator.
|
||||
///</summary>
|
||||
///<param name="symbol">The symbol name of the security, the data of which should be used to calculate the indicator.</param>
|
||||
///<param name="period">The value of the period can be one of the ENUM_TIMEFRAMES enumeration values, 0 means the current timeframe.</param>
|
||||
public int iForce(string symbol, ENUM_TIMEFRAMES period)
|
||||
public int iFractals(string symbol, ENUM_TIMEFRAMES period)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Symbol", symbol ?? string.Empty }, { "Period", (int)period } };
|
||||
return SendCommand<int>(ExecutorHandle, Mt5CommandType.iForce, cmdParams);
|
||||
return SendCommand<int>(ExecutorHandle, Mt5CommandType.iFractals, cmdParams);
|
||||
}
|
||||
|
||||
///<summary>
|
||||
@@ -2969,7 +3009,7 @@ namespace MtApi5
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Symbol", symbol ?? string.Empty }, { "Period", (int)period },
|
||||
{ "Kperiod", Kperiod }, { "Dperiod", Dperiod }, { "Slowing", slowing },
|
||||
{ "MaMethod", (int)maMethod }, { "priceField", (int)priceField } };
|
||||
{ "MaMethod", (int)maMethod }, { "PriceField", (int)priceField } };
|
||||
return SendCommand<int>(ExecutorHandle, Mt5CommandType.iStochastic, cmdParams);
|
||||
}
|
||||
|
||||
@@ -3055,8 +3095,8 @@ namespace MtApi5
|
||||
public int iCustom(string symbol, ENUM_TIMEFRAMES period, string name, double[] parameters)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Symbol", symbol ?? string.Empty },
|
||||
{ "Period", (int)period }, { "Name", name ?? string.Empty},
|
||||
{ "Parameters", parameters }, { "ParamsType", ParametersType.Double} };
|
||||
{ "Timeframe", (int)period }, { "Name", name ?? string.Empty},
|
||||
{ "Params", parameters }, { "ParamsType", ParametersType.Double} };
|
||||
return SendCommand<int>(ExecutorHandle, Mt5CommandType.iCustom, cmdParams);
|
||||
}
|
||||
|
||||
@@ -3070,8 +3110,8 @@ namespace MtApi5
|
||||
public int iCustom(string symbol, ENUM_TIMEFRAMES period, string name, int[] parameters)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Symbol", symbol ?? string.Empty },
|
||||
{ "Period", (int)period }, { "Name", name ?? string.Empty }, { "Parameters", parameters },
|
||||
{ "ParamsType", ParametersType.Int } };
|
||||
{ "Timeframe", (int)period }, { "Name", name ?? string.Empty }, { "Parameters", parameters },
|
||||
{ "Params", ParametersType.Int } };
|
||||
return SendCommand<int>(ExecutorHandle, Mt5CommandType.iCustom, cmdParams);
|
||||
}
|
||||
|
||||
@@ -3085,8 +3125,8 @@ namespace MtApi5
|
||||
public int iCustom(string symbol, ENUM_TIMEFRAMES period, string name, string[] parameters)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Symbol", symbol ?? string.Empty },
|
||||
{ "Period", (int)period }, { "Name", name ?? string.Empty },
|
||||
{ "Parameters", parameters }, { "ParamsType", ParametersType.String } };
|
||||
{ "Timeframe", (int)period }, { "Name", name ?? string.Empty },
|
||||
{ "Params", parameters }, { "ParamsType", ParametersType.String } };
|
||||
return SendCommand<int>(ExecutorHandle, Mt5CommandType.iCustom, cmdParams);
|
||||
}
|
||||
|
||||
@@ -3100,8 +3140,8 @@ namespace MtApi5
|
||||
public int iCustom(string symbol, ENUM_TIMEFRAMES period, string name, bool[] parameters)
|
||||
{
|
||||
Dictionary<string, object> cmdParams = new() { { "Symbol", symbol ?? string.Empty },
|
||||
{ "Period", (int)period }, { "Name", name ?? string.Empty },
|
||||
{ "Parameters", parameters }, { "ParamsType", ParametersType.Boolean } };
|
||||
{ "Timeframe", (int)period }, { "Name", name ?? string.Empty },
|
||||
{ "Params", parameters }, { "ParamsType", ParametersType.Boolean } };
|
||||
return SendCommand<int>(ExecutorHandle, Mt5CommandType.iCustom, cmdParams);
|
||||
}
|
||||
|
||||
@@ -3392,11 +3432,6 @@ namespace MtApi5
|
||||
var quote = GetQuote(Client, handle);
|
||||
if (quote != null)
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
_quotes[handle] = quote;
|
||||
}
|
||||
|
||||
QuoteAdded?.Invoke(this, new Mt5QuoteEventArgs(quote));
|
||||
}
|
||||
else
|
||||
@@ -3411,19 +3446,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)
|
||||
@@ -3512,7 +3546,7 @@ namespace MtApi5
|
||||
var e = JsonConvert.DeserializeObject<OnLastTimeBarEvent>(payload);
|
||||
if (e == null || string.IsNullOrEmpty(e.Instrument) || e.Rates == null)
|
||||
return;
|
||||
OnLastTimeBar?.Invoke(this, new Mt5TimeBarArgs(expertHandle, e.Instrument, e.Rates));
|
||||
OnLastTimeBar?.Invoke(this, new Mt5TimeBarArgs(expertHandle, e.Instrument, e.Timeframe, e.Rates));
|
||||
}
|
||||
|
||||
private void ReceivedOnLockTicksEvent(int expertHandle, string payload)
|
||||
@@ -3540,7 +3574,6 @@ namespace MtApi5
|
||||
client = _client;
|
||||
_client = null;
|
||||
|
||||
_quotes.Clear();
|
||||
_experts.Clear();
|
||||
_executorHandle = 0;
|
||||
}
|
||||
@@ -3568,7 +3601,7 @@ namespace MtApi5
|
||||
var payloadJson = payload == null ? string.Empty : JsonConvert.SerializeObject(payload);
|
||||
Log.Debug($"SendCommand: sending '{payloadJson}' ...");
|
||||
|
||||
var responseJson = client.SendCommand(expertHandle, (int)commandType, payloadJson);
|
||||
var responseJson = client.SendCommand(expertHandle, (int)commandType, payloadJson, CommandTimeout);
|
||||
|
||||
Log.Debug($"SendCommand: received response JSON [{responseJson}]");
|
||||
|
||||
|
||||
@@ -8,5 +8,6 @@
|
||||
public double Last { get; set; } // Price of the last deal (Last)
|
||||
public ulong Volume { get; set; } // Volume for the current Last price
|
||||
public double VolumeReal { get; set; } // Volume for the current Last price with greater accuracy
|
||||
public uint Flags { get; set; } // Tick flags
|
||||
}
|
||||
}
|
||||
@@ -259,6 +259,7 @@ namespace MtApi5.MtProtocol
|
||||
OrderSendAsync = 302,
|
||||
OrderCheck = 303,
|
||||
Buy = 304,
|
||||
Sell = 305
|
||||
Sell = 305,
|
||||
GetSymbols = 306
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
public MqlRates? Rates { get; set; }
|
||||
public string? Instrument { get; set; }
|
||||
public ENUM_TIMEFRAMES Timeframe { get; set; }
|
||||
public int ExpertHandle { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
+13
-13
@@ -65,7 +65,7 @@ namespace MtClient
|
||||
logger_.Debug($"MtRpcClient.Disconnect: success");
|
||||
}
|
||||
|
||||
public string? SendCommand(int expertHandle, int commandType, string payload)
|
||||
public string? SendCommand(int expertHandle, int commandType, string payload, int timeout = 10000) // 10 sec
|
||||
{
|
||||
CommandTask<string> commandTask = new();
|
||||
int commandId;
|
||||
@@ -78,7 +78,7 @@ namespace MtClient
|
||||
MtCommand command = new(expertHandle, commandType, commandId, payload);
|
||||
Send(command);
|
||||
|
||||
var response = commandTask.WaitResponse(10000); // 10 sec
|
||||
var response = commandTask.WaitResponse(timeout);
|
||||
lock (tasks_)
|
||||
{
|
||||
tasks_.Remove(commandId);
|
||||
@@ -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_;
|
||||
}
|
||||
|
||||
@@ -107,6 +107,12 @@ void MtConnection::OnRead(
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec == boost::asio::error::eof)
|
||||
{
|
||||
log_.Info("%s: %s. Remote peer gracefully closes the connection.", __FUNCTION__, ec.message().c_str());
|
||||
return;
|
||||
}
|
||||
|
||||
if (ec)
|
||||
{
|
||||
log_.Error("%s: %s", __FUNCTION__, ec.message().c_str());
|
||||
|
||||
@@ -44,6 +44,5 @@ private:
|
||||
std::string host_;
|
||||
std::string read_text_;
|
||||
boost::beast::flat_buffer read_buffer_;
|
||||
//std::string send_text_;
|
||||
std::queue<std::string> send_queue_;
|
||||
};
|
||||
|
||||
@@ -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,8 @@
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Label Grid.Row="0" Content="Quotes" Background="LightYellow" />
|
||||
<ListView Grid.Row="1" Margin="2"
|
||||
@@ -181,6 +183,12 @@
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
<Button Grid.Row="2" Content="Refresh Quotes" Margin="2" Command="{Binding RefreshQuotesCommand}"/>
|
||||
<StackPanel Grid.Row="3" Orientation="Horizontal" HorizontalAlignment="Left" Margin="2">
|
||||
<TextBlock Text="Command timeout (ms):" VerticalAlignment="Center" Margin="2"/>
|
||||
<TextBox Text="{Binding CommandTimeout}" Width="100" Margin="2"/>
|
||||
<Button Content="Apply" Margin="2" Command="{Binding TimeoutApplyCommand}"/>
|
||||
</StackPanel>
|
||||
</Grid>
|
||||
</Border>
|
||||
</Grid>
|
||||
@@ -473,22 +481,34 @@
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="Market Info">
|
||||
<WrapPanel VerticalAlignment="Top" Margin="5">
|
||||
<Button Command="{Binding SymbolsTotalCommand}" Content="SymbolsTotal" Margin="2"/>
|
||||
<Button Command="{Binding SymbolNameCommand}" Content="SymbolName" Margin="2"/>
|
||||
<Button Command="{Binding SymbolSelectCommand}" Content="SymbolSelect" Margin="2"/>
|
||||
<Button Command="{Binding SymbolIsSynchronizedCommand}" Content="SymbolIsSynchronized" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoDoubleCommand}" Content="SymbolInfoDouble" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoIntegerCommand}" Content="SymbolInfoInteger" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoStringCommand}" Content="SymbolInfoString" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoString2Command}" Content="SymbolInfoString-2" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoTickCommand}" Content="SymbolInfoTick" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoSessionQuoteCommand}" Content="SymbolInfoSessionQuote" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoSessionTradeCommand}" Content="SymbolInfoSessionTrade" Margin="2"/>
|
||||
<Button Command="{Binding MarketBookAddCommand}" Content="MarketBookAdd" Margin="2"/>
|
||||
<Button Command="{Binding MarketBookReleaseCommand}" Content="MarketBookRelease" Margin="2"/>
|
||||
<Button Command="{Binding MarketBookGetCommand}" Content="MarketBookGet" Margin="2"/>
|
||||
</WrapPanel>
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<WrapPanel Grid.Row="0" VerticalAlignment="Top" Margin="5">
|
||||
<Button Command="{Binding SymbolsTotalCommand}" Content="SymbolsTotal" Margin="2"/>
|
||||
<Button Command="{Binding SymbolNameCommand}" Content="SymbolName" Margin="2"/>
|
||||
<Button Command="{Binding SymbolSelectCommand}" Content="SymbolSelect" Margin="2"/>
|
||||
<Button Command="{Binding SymbolIsSynchronizedCommand}" Content="SymbolIsSynchronized" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoDoubleCommand}" Content="SymbolInfoDouble" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoIntegerCommand}" Content="SymbolInfoInteger" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoStringCommand}" Content="SymbolInfoString" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoString2Command}" Content="SymbolInfoString-2" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoTickCommand}" Content="SymbolInfoTick" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoSessionQuoteCommand}" Content="SymbolInfoSessionQuote" Margin="2"/>
|
||||
<Button Command="{Binding SymbolInfoSessionTradeCommand}" Content="SymbolInfoSessionTrade" Margin="2"/>
|
||||
<Button Command="{Binding MarketBookAddCommand}" Content="MarketBookAdd" Margin="2"/>
|
||||
<Button Command="{Binding MarketBookReleaseCommand}" Content="MarketBookRelease" Margin="2"/>
|
||||
<Button Command="{Binding MarketBookGetCommand}" Content="MarketBookGet" Margin="2"/>
|
||||
</WrapPanel>
|
||||
<StackPanel Grid.Row="1" Orientation="Horizontal">
|
||||
<Button Content="GetSymbols" Command="{Binding GetSymbolsCommand}" Width="100" Margin="2"/>
|
||||
<CheckBox Content="Selected" IsChecked="{Binding GetSymbolsSelected}" Margin="5" />
|
||||
</StackPanel>
|
||||
<ListBox Grid.Row="2" ItemsSource="{Binding Symbols}" Height="Auto" Width="200" HorizontalAlignment="Left"/>
|
||||
</Grid>
|
||||
</TabItem>
|
||||
|
||||
<TabItem Header="CTrade (Positions)">
|
||||
|
||||
@@ -160,6 +160,11 @@ namespace MtApi5TestClient
|
||||
public DelegateCommand GlobalVariablesTotalCommand { get; private set; }
|
||||
|
||||
public DelegateCommand UnlockTicksCommand { get; private set; }
|
||||
|
||||
public DelegateCommand GetSymbolsCommand { get; private set; }
|
||||
public DelegateCommand RefreshQuotesCommand { get; private set; }
|
||||
|
||||
public DelegateCommand TimeoutApplyCommand { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -207,6 +212,17 @@ namespace MtApi5TestClient
|
||||
}
|
||||
}
|
||||
|
||||
private int _command_timeout;
|
||||
public int CommandTimeout
|
||||
{
|
||||
get { return _command_timeout; }
|
||||
set
|
||||
{
|
||||
_command_timeout = value;
|
||||
OnPropertyChanged("CommandTimeout");
|
||||
}
|
||||
}
|
||||
|
||||
public ObservableCollection<QuoteViewModel> Quotes { get; } = new ObservableCollection<QuoteViewModel>();
|
||||
|
||||
private QuoteViewModel _selectedQuote;
|
||||
@@ -314,6 +330,19 @@ namespace MtApi5TestClient
|
||||
OnPropertyChanged("PositionTicketValue");
|
||||
}
|
||||
}
|
||||
|
||||
public bool GetSymbolsSelected { get; set; } = false;
|
||||
|
||||
private List<string> _symbols;
|
||||
public List<string> Symbols
|
||||
{
|
||||
get { return _symbols; }
|
||||
set
|
||||
{
|
||||
_symbols = value;
|
||||
OnPropertyChanged("Symbols");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
@@ -334,6 +363,7 @@ namespace MtApi5TestClient
|
||||
ConnectionState = _mtApiClient.ConnectionState;
|
||||
ConnectionMessage = "Disconnected";
|
||||
Port = 8228; //default local port
|
||||
CommandTimeout = _mtApiClient.CommandTimeout;
|
||||
|
||||
InitCommands();
|
||||
|
||||
@@ -475,6 +505,11 @@ namespace MtApi5TestClient
|
||||
GlobalVariablesTotalCommand = new DelegateCommand(ExecuteGlobalVariablesTotal);
|
||||
|
||||
UnlockTicksCommand = new DelegateCommand(ExecuteUnlockTicks);
|
||||
|
||||
GetSymbolsCommand = new DelegateCommand(ExecuteGetSymbols);
|
||||
RefreshQuotesCommand = new DelegateCommand(ExecuteRefreshQuotes);
|
||||
|
||||
TimeoutApplyCommand = new DelegateCommand(ExecuteTimeoutApply);
|
||||
}
|
||||
|
||||
private bool CanExecuteConnect(object o)
|
||||
@@ -1054,7 +1089,7 @@ namespace MtApi5TestClient
|
||||
{
|
||||
foreach (var v in result)
|
||||
{
|
||||
var tickStr = $"time = {v.time}, bid = {v.bid}, ask = {v.ask}, last = {v.last}, volume = {v.volume}";
|
||||
var tickStr = $"time = {v.time}, bid = {v.bid}, ask = {v.ask}, last = {v.last}, volume = {v.volume}, flags = {v.flags}";
|
||||
TimeSeriesResults.Add(tickStr);
|
||||
}
|
||||
});
|
||||
@@ -1142,6 +1177,7 @@ namespace MtApi5TestClient
|
||||
AddLog($"SymbolInfoTick(EURUSD) tick.last = {result.last}");
|
||||
AddLog($"SymbolInfoTick(EURUSD) tick.volume = {result.volume}");
|
||||
AddLog($"SymbolInfoTick(EURUSD) tick.volume_real = {result.volume_real}");
|
||||
AddLog($"SymbolInfoTick(EURUSD) tick.flags = {result.flags}");
|
||||
}
|
||||
|
||||
private async void ExecuteSymbolInfoSessionQuote(object o)
|
||||
@@ -1726,6 +1762,39 @@ namespace MtApi5TestClient
|
||||
_mtApiClient.UnlockTicks();
|
||||
}
|
||||
|
||||
private async void ExecuteGetSymbols(object o)
|
||||
{
|
||||
var result = await Execute(() => _mtApiClient.GetSymbols(GetSymbolsSelected));
|
||||
if (result == null)
|
||||
return;
|
||||
|
||||
AddLog($"ChartScreenShot: {result.Count} count of symbols");
|
||||
RunOnUiThread(() =>
|
||||
{
|
||||
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 void ExecuteTimeoutApply(object o)
|
||||
{
|
||||
_mtApiClient.CommandTimeout = CommandTimeout;
|
||||
AddLog($"TimeoutApply: timeout = {CommandTimeout} milliseconds");
|
||||
}
|
||||
|
||||
private static void RunOnUiThread(Action action)
|
||||
{
|
||||
Application.Current?.Dispatcher.Invoke(action);
|
||||
@@ -1804,7 +1873,7 @@ namespace MtApi5TestClient
|
||||
|
||||
private void _mtApiClient_OnLastTimeBar(object sender, Mt5TimeBarArgs e)
|
||||
{
|
||||
AddLog($"OnLastTimeBarEvent: ExpertHandle = {e.ExpertHandle}, Symbol = {e.Symbol}, open = {e.Rates.open}, close = {e.Rates.close}, time = {e.Rates.time}, high = {e.Rates.high}, low = {e.Rates.low}");
|
||||
AddLog($"OnLastTimeBarEvent: ExpertHandle = {e.ExpertHandle}, Symbol = {e.Symbol}, Timeframe = {e.Timeframe}, open = {e.Rates.open}, close = {e.Rates.close}, time = {e.Rates.time}, high = {e.Rates.high}, low = {e.Rates.low}");
|
||||
}
|
||||
|
||||
private void _mtApiClient_OnLockTicks(object sender, Mt5LockTicksEventArgs e)
|
||||
|
||||
+1860
-1724
File diff suppressed because it is too large
Load Diff
@@ -1,51 +1,16 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Drawing;
|
||||
using System.Globalization;
|
||||
using MtApi;
|
||||
using MtApi;
|
||||
using MtApi.Monitors;
|
||||
using System.Globalization;
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace TestApiClientUI
|
||||
{
|
||||
class MtLogger : IMtLogger
|
||||
{
|
||||
public void Debug(object message)
|
||||
{
|
||||
Write("DEBUG", message);
|
||||
}
|
||||
|
||||
public void Error(object message)
|
||||
{
|
||||
Write("ERROR", message);
|
||||
}
|
||||
|
||||
public void Fatal(object message)
|
||||
{
|
||||
Write("FATAL", message);
|
||||
}
|
||||
|
||||
public void Info(object message)
|
||||
{
|
||||
Write("INFO", message);
|
||||
}
|
||||
|
||||
public void Warn(object message)
|
||||
{
|
||||
Write("WARN", message);
|
||||
}
|
||||
private void Write(string level, object message)
|
||||
{
|
||||
Console.WriteLine($"[{Environment.CurrentManagedThreadId}] [{level}] {message}");
|
||||
}
|
||||
}
|
||||
|
||||
public partial class Form1 : Form
|
||||
{
|
||||
#region Fields
|
||||
|
||||
private readonly List<Action> _groupOrderCommands = [];
|
||||
private readonly MtApiClient _apiClient = new (new MtLogger());
|
||||
private readonly MtApiClient _apiClient = new(new MtLogger());
|
||||
private readonly TimerTradeMonitor _timerTradeMonitor;
|
||||
private readonly TimeframeTradeMonitor _timeframeTradeMonitor;
|
||||
|
||||
@@ -742,7 +707,7 @@ namespace TestApiClientUI
|
||||
|
||||
var symbol = textBoxOrderSymbol.Text;
|
||||
|
||||
var cmd = (TradeOperation) comboBoxOrderCommand.SelectedIndex;
|
||||
var cmd = (TradeOperation)comboBoxOrderCommand.SelectedIndex;
|
||||
|
||||
double volume;
|
||||
double.TryParse(textBoxOrderVolume.Text, out volume);
|
||||
@@ -750,7 +715,7 @@ namespace TestApiClientUI
|
||||
double price;
|
||||
double.TryParse(textBoxOrderPrice.Text, out price);
|
||||
|
||||
var slippage = (int) numericOrderSlippage.Value;
|
||||
var slippage = (int)numericOrderSlippage.Value;
|
||||
|
||||
double stoploss;
|
||||
double.TryParse(textBoxOrderStoploss.Text, out stoploss);
|
||||
@@ -805,8 +770,8 @@ namespace TestApiClientUI
|
||||
private async void button16_Click(object sender, EventArgs e)
|
||||
{
|
||||
var ticket = int.Parse(textBoxIndexTicket.Text);
|
||||
var selectMode = (OrderSelectMode) comboBox1.SelectedIndex;
|
||||
var selectSource = (OrderSelectSource) comboBox2.SelectedIndex;
|
||||
var selectMode = (OrderSelectMode)comboBox1.SelectedIndex;
|
||||
var selectSource = (OrderSelectSource)comboBox2.SelectedIndex;
|
||||
|
||||
var order = await Execute(() => _apiClient.GetOrder(ticket, selectMode, selectSource));
|
||||
|
||||
@@ -1120,12 +1085,12 @@ namespace TestApiClientUI
|
||||
{
|
||||
if (e.Opened != null)
|
||||
{
|
||||
PrintLog($"{sender.GetType()}: Opened orders - {string.Join(", ", e.Opened.Select(o => o.Ticket).ToList())}");
|
||||
PrintLog($"{sender.GetType()}: Opened orders - {string.Join(", ", e.Opened.Select(o => new { o.Ticket, o.Symbol, o.OpenPrice, o.OpenTime }).ToList())}");
|
||||
}
|
||||
|
||||
if (e.Closed != null)
|
||||
{
|
||||
PrintLog($"{sender.GetType()}: Closed orders - {string.Join(", ", e.Closed.Select(o => o.Ticket).ToList())}");
|
||||
PrintLog($"{sender.GetType()}: Closed orders - {string.Join(", ", e.Closed.Select(o => new { o.Ticket, o.Symbol, o.ClosePrice, o.CloseTime }).ToList())}");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1544,5 +1509,19 @@ namespace TestApiClientUI
|
||||
PrintLog($"iBarShift result1 = {result1}, time = {time1}");
|
||||
PrintLog($"iBarShift result2 = {result2}, time = {time2}");
|
||||
}
|
||||
|
||||
private async void button74_Click(object sender, EventArgs e)
|
||||
{
|
||||
listBoxAllSymbols.Items.Clear();
|
||||
|
||||
var result = await Execute(() => _apiClient.GetSymbols(checkBox3.Checked));
|
||||
if (result != null)
|
||||
{
|
||||
foreach (string? r in result)
|
||||
listBoxAllSymbols.Items.Add(r);
|
||||
}
|
||||
int count = result != null ? result.Count : 0;
|
||||
PrintLog($"GetSymbols: {count}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
|
||||
Example:
|
||||
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
@@ -26,36 +26,36 @@
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
using MtApi;
|
||||
|
||||
namespace TestApiClientUI
|
||||
{
|
||||
class MtLogger : IMtLogger
|
||||
{
|
||||
public void Debug(object message)
|
||||
{
|
||||
Write("DEBUG", message);
|
||||
}
|
||||
|
||||
public void Error(object message)
|
||||
{
|
||||
Write("ERROR", message);
|
||||
}
|
||||
|
||||
public void Fatal(object message)
|
||||
{
|
||||
Write("FATAL", message);
|
||||
}
|
||||
|
||||
public void Info(object message)
|
||||
{
|
||||
Write("INFO", message);
|
||||
}
|
||||
|
||||
public void Warn(object message)
|
||||
{
|
||||
Write("WARN", message);
|
||||
}
|
||||
private void Write(string level, object message)
|
||||
{
|
||||
Console.WriteLine($"[{Environment.CurrentManagedThreadId}] [{level}] {message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
+47
-9
@@ -1330,6 +1330,9 @@ int ExecuteCommand()
|
||||
case 290: //GetQuote
|
||||
response = Execute_GetQuote();
|
||||
break;
|
||||
case 291: //GetSymbols
|
||||
response = Execute_GetSymbols();
|
||||
break;
|
||||
|
||||
default:
|
||||
Print("WARNING: Unknown command type = ", command_type);
|
||||
@@ -2269,7 +2272,7 @@ string Execute_iBands()
|
||||
GET_STRING_JSON_VALUE(jo, "Symbol", symbol);
|
||||
GET_INT_JSON_VALUE(jo, "Timeframe", timeframe);
|
||||
GET_INT_JSON_VALUE(jo, "Period", period);
|
||||
GET_INT_JSON_VALUE(jo, "Deviation", deviation);
|
||||
GET_DOUBLE_JSON_VALUE(jo, "Deviation", deviation);
|
||||
GET_INT_JSON_VALUE(jo, "BandsShift", bands_shift);
|
||||
GET_INT_JSON_VALUE(jo, "AppliedPrice", applied_price);
|
||||
GET_INT_JSON_VALUE(jo, "Mode", mode);
|
||||
@@ -2284,7 +2287,7 @@ string Execute_iBandsOnArray()
|
||||
GET_JSON_PAYLOAD(jo);
|
||||
GET_INT_JSON_VALUE(jo, "Total", total);
|
||||
GET_INT_JSON_VALUE(jo, "Period", period);
|
||||
GET_INT_JSON_VALUE(jo, "Deviation", deviation);
|
||||
GET_DOUBLE_JSON_VALUE(jo, "Deviation", deviation);
|
||||
GET_INT_JSON_VALUE(jo, "BandsShift", bands_shift);
|
||||
GET_INT_JSON_VALUE(jo, "Mode", mode);
|
||||
GET_INT_JSON_VALUE(jo, "Shift", shift);
|
||||
@@ -3792,16 +3795,25 @@ bool OrderCloseAll()
|
||||
{
|
||||
if (OrderSelect(i, SELECT_BY_POS))
|
||||
{
|
||||
int type = OrderType();
|
||||
int type = OrderType();
|
||||
bool order_closed = true;
|
||||
switch(type)
|
||||
{
|
||||
//Close opened long positions
|
||||
case OP_BUY: OrderClose( OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_BID), 5, Red );
|
||||
case OP_BUY:
|
||||
{
|
||||
order_closed = OrderClose( OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_BID), 5, Red );
|
||||
break;
|
||||
}
|
||||
//Close opened short positions
|
||||
case OP_SELL: OrderClose( OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_ASK), 5, Red );
|
||||
case OP_SELL:
|
||||
{
|
||||
order_closed = OrderClose( OrderTicket(), OrderLots(), MarketInfo(OrderSymbol(), MODE_ASK), 5, Red );
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (order_closed == false)
|
||||
Print("Failed to close order ", OrderTicket());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3961,10 +3973,8 @@ string Execute_iCustom()
|
||||
{
|
||||
int intParams[];
|
||||
ArrayResize(intParams, size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
intParams[i] = jaParams.getInt(i);
|
||||
}
|
||||
for (int it_i = 0; it_i < size; it_i++)
|
||||
intParams[it_i] = jaParams.getInt(it_i);
|
||||
result = iCustomT(symbol, timeframe, name, intParams, size, mode, shift);
|
||||
}
|
||||
break;
|
||||
@@ -3972,6 +3982,8 @@ string Execute_iCustom()
|
||||
{
|
||||
double doubleParams[];
|
||||
ArrayResize(doubleParams, size);
|
||||
for (int it_d = 0; it_d < size; it_d++)
|
||||
doubleParams[it_d] = jaParams.getDouble(it_d);
|
||||
result = iCustomT(symbol, timeframe, name, doubleParams, size, mode, shift);
|
||||
}
|
||||
break;
|
||||
@@ -3979,6 +3991,8 @@ string Execute_iCustom()
|
||||
{
|
||||
string stringParams[];
|
||||
ArrayResize(stringParams, size);
|
||||
for (int it_s = 0; it_s < size; it_s++)
|
||||
stringParams[it_s] = jaParams.getString(it_s);
|
||||
result = iCustomT(symbol, timeframe, name, stringParams, size, mode, shift);
|
||||
}
|
||||
break;
|
||||
@@ -3986,6 +4000,8 @@ string Execute_iCustom()
|
||||
{
|
||||
bool boolParams[];
|
||||
ArrayResize(boolParams, size);
|
||||
for (int it_b = 0; it_b < size; it_b++)
|
||||
boolParams[it_b] = jaParams.getBool(it_b);
|
||||
result = iCustomT(symbol, timeframe, name, boolParams, size, mode, shift);
|
||||
}
|
||||
break;
|
||||
@@ -4183,3 +4199,25 @@ string Execute_GetQuote()
|
||||
MtQuote quote(Symbol(), tick);
|
||||
return CreateSuccessResponse(quote.CreateJson());
|
||||
}
|
||||
|
||||
string Execute_GetSymbols()
|
||||
{
|
||||
GET_JSON_PAYLOAD(jo);
|
||||
GET_BOOL_JSON_VALUE(jo, "Selected", selected);
|
||||
|
||||
const int symbolsCount = SymbolsTotal(selected);
|
||||
JSONArray* jaSymbols = new JSONArray();
|
||||
int idx = 0;
|
||||
for(int idxSymbol = 0; idxSymbol < symbolsCount; idxSymbol++)
|
||||
{
|
||||
string symbol = SymbolName(idxSymbol, selected);
|
||||
string firstChar = StringSubstr(symbol, 0, 1);
|
||||
if(firstChar != "#" && StringLen(symbol) == 6)
|
||||
{
|
||||
jaSymbols.put(idx, new JSONString(symbol));
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return CreateSuccessResponse(jaSymbols);
|
||||
}
|
||||
Regular → Executable
BIN
Binary file not shown.
+60
-13
@@ -1,7 +1,7 @@
|
||||
#property copyright "Vyacheslav Demidyuk"
|
||||
#property link ""
|
||||
|
||||
#property version "2.0"
|
||||
#property version "2.1"
|
||||
#property description "MtApi (MT5) connection expert"
|
||||
|
||||
#include <json.mqh>
|
||||
@@ -82,7 +82,7 @@ void OnTick()
|
||||
MqlRates rates_array[];
|
||||
CopyRates(symbol, Period(), 1, 1, rates_array);
|
||||
|
||||
MtTimeBarEvent time_bar(symbol, rates_array[0]);
|
||||
MtTimeBarEvent time_bar(symbol, (int)Period(), rates_array[0]);
|
||||
SendMtEvent(ON_LAST_TIME_BAR_EVENT, time_bar);
|
||||
}
|
||||
lastbar_time_changed = true;
|
||||
@@ -380,6 +380,7 @@ int preinit()
|
||||
ADD_EXECUTOR(303, OrderCheck);
|
||||
ADD_EXECUTOR(304, Buy);
|
||||
ADD_EXECUTOR(305, Sell);
|
||||
ADD_EXECUTOR(306, GetSymbols);
|
||||
|
||||
return (0);
|
||||
}
|
||||
@@ -408,14 +409,14 @@ int init()
|
||||
isCrashed = true;
|
||||
return (1);
|
||||
}
|
||||
if (MQL5InfoInteger(MQL5_DLLS_ALLOWED) == false)
|
||||
if (MQLInfoInteger(MQL_DLLS_ALLOWED) == false)
|
||||
{
|
||||
MessageBox("Libraries not allowed.", "MtApi", 0);
|
||||
isCrashed = true;
|
||||
return (1);
|
||||
}
|
||||
|
||||
if (MQL5InfoInteger(MQL5_TRADE_ALLOWED) == false)
|
||||
if (MQLInfoInteger(MQL_TRADE_ALLOWED) == false)
|
||||
{
|
||||
MessageBox("Trade not allowed.", "MtApi", 0);
|
||||
isCrashed = true;
|
||||
@@ -1829,7 +1830,7 @@ string Execute_ObjectCreate()
|
||||
|
||||
datetime times[30];
|
||||
double prices[30];
|
||||
ArrayInitialize(times, EMPTY_VALUE);
|
||||
ArrayInitialize(times, 0);
|
||||
ArrayInitialize(prices, EMPTY_VALUE);
|
||||
|
||||
JSONArray* times_jo = jo.p.getArray("Times");
|
||||
@@ -2168,10 +2169,10 @@ string Execute_iChaikin()
|
||||
GET_INT_JSON_VALUE(jo, "Period", period);
|
||||
GET_INT_JSON_VALUE(jo, "FastMaPeriod", fast_ma_period);
|
||||
GET_INT_JSON_VALUE(jo, "SlowMaPeriod", slow_ma_period);
|
||||
GET_INT_JSON_VALUE(jo, "MaPeriod", ma_period);
|
||||
GET_INT_JSON_VALUE(jo, "MaMethod", ma_method);
|
||||
GET_INT_JSON_VALUE(jo, "AppliedVolume", applied_volume);
|
||||
|
||||
int result = iChaikin(symbol, (ENUM_TIMEFRAMES)period, fast_ma_period, slow_ma_period, (ENUM_MA_METHOD)ma_period, (ENUM_APPLIED_VOLUME) applied_volume);
|
||||
int result = iChaikin(symbol, (ENUM_TIMEFRAMES)period, fast_ma_period, slow_ma_period, (ENUM_MA_METHOD)ma_method, (ENUM_APPLIED_VOLUME) applied_volume);
|
||||
return CreateSuccessResponse(new JSONNumber(result));
|
||||
}
|
||||
|
||||
@@ -2377,7 +2378,7 @@ string Execute_iSAR()
|
||||
GET_STRING_JSON_VALUE(jo, "Symbol", symbol);
|
||||
GET_INT_JSON_VALUE(jo, "Period", period);
|
||||
GET_DOUBLE_JSON_VALUE(jo, "Step", step);
|
||||
GET_DOUBLE_JSON_VALUE(jo, "Mamimum", maximum);
|
||||
GET_DOUBLE_JSON_VALUE(jo, "Maximum", maximum);
|
||||
|
||||
int result = iSAR(symbol, (ENUM_TIMEFRAMES)period, step, maximum);
|
||||
return CreateSuccessResponse(new JSONNumber(result));
|
||||
@@ -2959,6 +2960,7 @@ string Execute_UnlockTicks()
|
||||
return CreateErrorResponse(-1, "UnlockTicks can be used only for backtesting");
|
||||
}
|
||||
|
||||
_is_ticks_locked = false;
|
||||
return CreateSuccessResponse();
|
||||
}
|
||||
|
||||
@@ -3065,15 +3067,15 @@ string Execute_iCustom()
|
||||
int intParams[];
|
||||
ArrayResize(intParams, size);
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
intParams[i] = jaParams.getInt(i);
|
||||
}
|
||||
result = iCustomT(symbol, (ENUM_TIMEFRAMES)timeframe, name, intParams, size);
|
||||
}
|
||||
break;
|
||||
case 1: //Double
|
||||
{
|
||||
int doubleParams[];
|
||||
double doubleParams[];
|
||||
for (int i = 0; i < size; i++)
|
||||
doubleParams[i] = jaParams.getDouble(i);
|
||||
ArrayResize(doubleParams, size);
|
||||
result = iCustomT(symbol, (ENUM_TIMEFRAMES)timeframe, name, doubleParams, size);
|
||||
}
|
||||
@@ -3082,13 +3084,17 @@ string Execute_iCustom()
|
||||
{
|
||||
string stringParams[];
|
||||
ArrayResize(stringParams, size);
|
||||
for (int i = 0; i < size; i++)
|
||||
stringParams[i] = jaParams.getString(i);
|
||||
result = iCustomT(symbol, (ENUM_TIMEFRAMES)timeframe, name, stringParams, size);
|
||||
}
|
||||
break;
|
||||
case 3: //Boolean
|
||||
{
|
||||
string boolParams[];
|
||||
bool boolParams[];
|
||||
ArrayResize(boolParams, size);
|
||||
for (int i = 0; i < size; i++)
|
||||
boolParams[i] = jaParams.getBool(i);
|
||||
result = iCustomT(symbol, (ENUM_TIMEFRAMES)timeframe, name, boolParams, size);
|
||||
}
|
||||
break;
|
||||
@@ -3446,6 +3452,28 @@ string Execute_Sell()
|
||||
return CreateSuccessResponse(result_value_jo);
|
||||
}
|
||||
|
||||
string Execute_GetSymbols()
|
||||
{
|
||||
GET_JSON_PAYLOAD(jo);
|
||||
GET_BOOL_JSON_VALUE(jo, "Selected", selected);
|
||||
|
||||
const int symbolsCount = SymbolsTotal(selected);
|
||||
JSONArray* jaSymbols = new JSONArray();
|
||||
int idx = 0;
|
||||
for(int idxSymbol = 0; idxSymbol < symbolsCount; idxSymbol++)
|
||||
{
|
||||
string symbol = SymbolName(idxSymbol, selected);
|
||||
string firstChar = StringSubstr(symbol, 0, 1);
|
||||
if(firstChar != "#" && StringLen(symbol) == 6)
|
||||
{
|
||||
jaSymbols.put(idx, new JSONString(symbol));
|
||||
idx++;
|
||||
}
|
||||
}
|
||||
|
||||
return CreateSuccessResponse(jaSymbols);
|
||||
}
|
||||
|
||||
int PositionCloseAll()
|
||||
{
|
||||
CTrade trade;
|
||||
@@ -3635,9 +3663,10 @@ private:
|
||||
class MtTimeBarEvent: public MtObject
|
||||
{
|
||||
public:
|
||||
MtTimeBarEvent(string symbol, const MqlRates& rates)
|
||||
MtTimeBarEvent(string symbol, int timeframe, const MqlRates& rates)
|
||||
{
|
||||
_symbol = symbol;
|
||||
_timeframe = timeframe;
|
||||
_rates = rates;
|
||||
}
|
||||
|
||||
@@ -3646,12 +3675,14 @@ public:
|
||||
JSONObject *jo = new JSONObject();
|
||||
jo.put("Rates", MqlRatesToJson(_rates));
|
||||
jo.put("Instrument", new JSONString(_symbol));
|
||||
jo.put("Timeframe", new JSONNumber(_timeframe));
|
||||
jo.put("ExpertHandle", new JSONNumber(ExpertHandle));
|
||||
return jo;
|
||||
}
|
||||
|
||||
private:
|
||||
string _symbol;
|
||||
int _timeframe;
|
||||
MqlRates _rates;
|
||||
};
|
||||
|
||||
@@ -3774,6 +3805,21 @@ bool JsonToMqlTradeRequest(JSONObject *jo, MqlTradeRequest& request)
|
||||
|
||||
JSONObject* MqlTickToJson(const MqlTick& tick)
|
||||
{
|
||||
// MT5 can add some additional non-documented flags, so we need to filter required documented flags only
|
||||
int summ=0;
|
||||
if((tick.flags & TICK_FLAG_BID)==TICK_FLAG_BID)
|
||||
summ+=TICK_FLAG_BID;
|
||||
if((tick.flags & TICK_FLAG_ASK)==TICK_FLAG_ASK)
|
||||
summ+=TICK_FLAG_ASK;
|
||||
if((tick.flags & TICK_FLAG_LAST)==TICK_FLAG_LAST)
|
||||
summ+=TICK_FLAG_LAST;
|
||||
if((tick.flags & TICK_FLAG_VOLUME)==TICK_FLAG_VOLUME)
|
||||
summ+=TICK_FLAG_VOLUME;
|
||||
if((tick.flags & TICK_FLAG_BUY)==TICK_FLAG_BUY)
|
||||
summ+=TICK_FLAG_BUY;
|
||||
if((tick.flags & TICK_FLAG_SELL)==TICK_FLAG_SELL)
|
||||
summ+=TICK_FLAG_SELL;
|
||||
|
||||
JSONObject *jo = new JSONObject();
|
||||
jo.put("Time", new JSONNumber((long)tick.time));
|
||||
jo.put("Bid", new JSONNumber(tick.bid));
|
||||
@@ -3781,6 +3827,7 @@ JSONObject* MqlTickToJson(const MqlTick& tick)
|
||||
jo.put("Last", new JSONNumber(tick.last));
|
||||
jo.put("Volume", new JSONNumber(tick.volume));
|
||||
jo.put("VolumeReal", new JSONNumber(tick.volume_real));
|
||||
jo.put("Flags", new JSONNumber(summ));
|
||||
return jo;
|
||||
}
|
||||
|
||||
|
||||
+10
-10
@@ -324,27 +324,27 @@ public:
|
||||
/// Lookup key and get associated string value, return false if failure.
|
||||
bool getString(string key,string &out)
|
||||
{
|
||||
return getString(getValue(key),out);
|
||||
return JSONValue::getString(getValue(key),out);
|
||||
}
|
||||
/// Lookup key and get associated bool value, return false if failure.
|
||||
bool getBool(string key,bool &out)
|
||||
{
|
||||
return getBool(getValue(key),out);
|
||||
return JSONValue::getBool(getValue(key),out);
|
||||
}
|
||||
/// Lookup key and get associated double value, return false if failure.
|
||||
bool getDouble(string key,double &out)
|
||||
{
|
||||
return getDouble(getValue(key),out);
|
||||
return JSONValue::getDouble(getValue(key),out);
|
||||
}
|
||||
/// Lookup key and get associated long value, return false if failure.
|
||||
bool getLong(string key,long &out)
|
||||
{
|
||||
return getLong(getValue(key),out);
|
||||
return JSONValue::getLong(getValue(key),out);
|
||||
}
|
||||
/// Lookup key and get associated int value, return false if failure.
|
||||
bool getInt(string key,int &out)
|
||||
{
|
||||
return getInt(getValue(key),out);
|
||||
return JSONValue::getInt(getValue(key),out);
|
||||
}
|
||||
|
||||
/// Lookup key and get associated array, NULL if not present. Cast failure if not an Array.
|
||||
@@ -451,27 +451,27 @@ public:
|
||||
/// Lookup JSONString by array index. NULL if not present. Cast failure if not an Object.
|
||||
bool getString(int index,string &out)
|
||||
{
|
||||
return getString(getValue(index),out);
|
||||
return JSONValue::getString(getValue(index),out);
|
||||
}
|
||||
/// Lookup JSONBool by array index. NULL if not present. Cast failure if not an Object.
|
||||
bool getBool(int index,bool &out)
|
||||
{
|
||||
return getBool(getValue(index),out);
|
||||
return JSONValue::getBool(getValue(index),out);
|
||||
}
|
||||
/// Lookup JSONNumber by array index. NULL if not present. Cast failure if not an Object.
|
||||
bool getDouble(int index,double &out)
|
||||
{
|
||||
return getDouble(getValue(index),out);
|
||||
return JSONValue::getDouble(getValue(index),out);
|
||||
}
|
||||
/// Lookup JSONNumber by array index. NULL if not present. Cast failure if not an Object.
|
||||
bool getLong(int index,long &out)
|
||||
{
|
||||
return getLong(getValue(index),out);
|
||||
return JSONValue::getLong(getValue(index),out);
|
||||
}
|
||||
/// Lookup JSONNumber by array index. NULL if not present. Cast failure if not an Object.
|
||||
bool getInt(int index,int &out)
|
||||
{
|
||||
return getInt(getValue(index),out);
|
||||
return JSONValue::getInt(getValue(index),out);
|
||||
}
|
||||
|
||||
/// Lookup array child by index, NULL if not present. Cast failure if not an Array.
|
||||
|
||||
Reference in New Issue
Block a user