Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions MtApi5/Mt5ChartEventArgs.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
using System;

namespace MtApi5
{
public class Mt5ChartEventArgs: EventArgs
{
internal Mt5ChartEventArgs(int expertHandle, int eventId, long lparam, double dparam, string sparam)
{
ExpertHandle = expertHandle;
EventId = eventId;
Lparam = lparam;
Dparam = dparam;
Sparam = sparam;
}

public int ExpertHandle { get; }
public int EventId { get; }
public long Lparam { get; }
public double Dparam { get; }
public string Sparam { get; }
}
}
29 changes: 29 additions & 0 deletions MtApi5/MtApi5Client.cs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ public MtApi5Client(IMtLogger? log = null)
_mtEventHandlers[Mt5EventTypes.OnTradeTransaction] = ReceivedOnTradeTransactionEvent;
_mtEventHandlers[Mt5EventTypes.OnLastTimeBar] = ReceivedOnLastTimeBarEvent;
_mtEventHandlers[Mt5EventTypes.OnLockTicks] = ReceivedOnLockTicksEvent;
_mtEventHandlers[Mt5EventTypes.OnChartEvent] = ReceivedOnChartEvent;

Log = log ?? new StubMtLogger();
}
Expand Down Expand Up @@ -2212,6 +2213,25 @@ public bool ChartScreenShot(long chartId, string filename, int width, int height
{ "Width", width }, { "Height", height }, { "AlignMode", (int)alignMode } };
return SendCommand<bool>(ExecutorHandle, Mt5CommandType.ChartScreenShot, cmdParams);
}

///<summary>
///Generates a custom event for the specified chart. The event is delivered back through the
///OnChartEvent event with EventId = CHARTEVENT_CUSTOM (1000) + customEventId.
///</summary>
///<param name="chartId">Chart ID. 0 means the current chart.</param>
///<param name="customEventId">ID of the user's event. This event ID is added to CHARTEVENT_CUSTOM (1000) on delivery.</param>
///<param name="lparam">Event parameter of the long type.</param>
///<param name="dparam">Event parameter of the double type.</param>
///<param name="sparam">Event parameter of the string type.</param>
///<returns>
///Returns true if the custom event has been successfully placed in the events queue of the chart, otherwise false.
///</returns>
public bool EventChartCustom(long chartId, ushort customEventId, long lparam, double dparam, string sparam)
{
Dictionary<string, object> cmdParams = new() { { "ChartId", chartId }, { "CustomEventId", (int)customEventId },
{ "Lparam", lparam }, { "Dparam", dparam }, { "Sparam", sparam ?? string.Empty } };
return SendCommand<bool>(ExecutorHandle, Mt5CommandType.EventChartCustom, cmdParams);
}
#endregion

#region Commands of Terminal
Expand Down Expand Up @@ -3385,6 +3405,7 @@ public int ExecutorHandle
public event EventHandler<Mt5BookEventArgs>? OnBookEvent;
public event EventHandler<Mt5TimeBarArgs>? OnLastTimeBar;
public event EventHandler<Mt5LockTicksEventArgs>? OnLockTicks;
public event EventHandler<Mt5ChartEventArgs>? OnChartEvent;
public event EventHandler<Mt5QuotesEventArgs>? QuoteList;
#endregion

Expand Down Expand Up @@ -3549,6 +3570,14 @@ private void ReceivedOnLastTimeBarEvent(int expertHandle, string payload)
OnLastTimeBar?.Invoke(this, new Mt5TimeBarArgs(expertHandle, e.Instrument, e.Timeframe, e.Rates));
}

private void ReceivedOnChartEvent(int expertHandle, string payload)
{
var e = JsonConvert.DeserializeObject<MtProtocol.OnChartEvent>(payload);
if (e == null)
return;
OnChartEvent?.Invoke(this, new Mt5ChartEventArgs(expertHandle, e.EventId, e.Lparam, e.Dparam, e.Sparam ?? string.Empty));
}

private void ReceivedOnLockTicksEvent(int expertHandle, string payload)
{
var e = JsonConvert.DeserializeObject<OnLockTicksEvent>(payload);
Expand Down
4 changes: 3 additions & 1 deletion MtApi5/MtProtocol/Mt5CommandType.cs
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,8 @@ internal enum Mt5CommandType
OrderCheck = 303,
Buy = 304,
Sell = 305,
GetSymbols = 306
GetSymbols = 306,

EventChartCustom = 385
}
}
3 changes: 2 additions & 1 deletion MtApi5/MtProtocol/Mt5EventTypes.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ internal enum Mt5EventTypes
OnBookEvent = 2,
OnTick = 3,
OnLastTimeBar = 4,
OnLockTicks = 5
OnLockTicks = 5,
OnChartEvent = 6
}
}
10 changes: 10 additions & 0 deletions MtApi5/MtProtocol/OnChartEvent.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
namespace MtApi5.MtProtocol
{
internal class OnChartEvent
{
public int EventId { get; set; }
public long Lparam { get; set; }
public double Dparam { get; set; }
public string? Sparam { get; set; }
}
}
1 change: 1 addition & 0 deletions TestClients/MtApi5TestClient/MainWindow.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -590,6 +590,7 @@
<Button Command="{Binding ChartYOnDroppedCommand}" Content="ChartYOnDropped" Margin="2"/>
<Button Command="{Binding ChartSetSymbolPeriodCommand}" Content="ChartSetSymbolPeriod" Margin="2"/>
<Button Command="{Binding ChartScreenShotCommand}" Content="ChartScreenShot" Margin="2"/>
<Button Command="{Binding EventChartCustomCommand}" Content="EventChartCustom" Margin="2"/>
</WrapPanel>
</Grid>
</TabItem>
Expand Down
16 changes: 16 additions & 0 deletions TestClients/MtApi5TestClient/ViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ public class ViewModel : INotifyPropertyChanged
public DelegateCommand ChartYOnDroppedCommand { get; private set; }
public DelegateCommand ChartSetSymbolPeriodCommand { get; private set; }
public DelegateCommand ChartScreenShotCommand { get; private set; }
public DelegateCommand EventChartCustomCommand { get; private set; }

public DelegateCommand TimeTradeServerCommand { get; private set; }
public DelegateCommand TimeLocalCommand { get; private set; }
Expand Down Expand Up @@ -359,6 +360,7 @@ public ViewModel()
_mtApiClient.OnBookEvent += _mtApiClient_OnBookEvent;
_mtApiClient.OnLastTimeBar += _mtApiClient_OnLastTimeBar;
_mtApiClient.OnLockTicks += _mtApiClient_OnLockTicks;
_mtApiClient.OnChartEvent += _mtApiClient_OnChartEvent;

ConnectionState = _mtApiClient.ConnectionState;
ConnectionMessage = "Disconnected";
Expand Down Expand Up @@ -486,6 +488,7 @@ private void InitCommands()
ChartYOnDroppedCommand = new DelegateCommand(ExecuteChartYOnDropped);
ChartSetSymbolPeriodCommand = new DelegateCommand(ExecuteChartSetSymbolPeriod);
ChartScreenShotCommand = new DelegateCommand(ExecuteChartScreenShot);
EventChartCustomCommand = new DelegateCommand(ExecuteEventChartCustom);

TimeCurrentCommand = new DelegateCommand(ExecuteTimeCurrent);
TimeTradeServerCommand = new DelegateCommand(ExecuteTimeTradeServer);
Expand Down Expand Up @@ -1755,6 +1758,14 @@ private async void ExecuteChartScreenShot(object obj)
var result = await Execute(() => _mtApiClient.ChartScreenShot(chartId, filename, width, height));
AddLog($"ChartScreenShot: result {result} for chartid {chartId}. Filename {filename}");
}

private async void ExecuteEventChartCustom(object obj)
{
var chartId = ChartFunctionsChartIdValue;
const ushort customEventId = 1;
var result = await Execute(() => _mtApiClient.EventChartCustom(chartId, customEventId, 42, 3.14, "Test custom event from MtApi5"));
AddLog($"EventChartCustom: result {result} for chartid {chartId}; expect OnChartEvent with EventId = {1000 + customEventId} (CHARTEVENT_CUSTOM + {customEventId})");
}
#endregion

private void ExecuteUnlockTicks(object o)
Expand Down Expand Up @@ -1881,6 +1892,11 @@ private void _mtApiClient_OnLockTicks(object sender, Mt5LockTicksEventArgs e)
AddLog($"OnLockTicksEvent: Symbol = {e.Symbol}");
}

private void _mtApiClient_OnChartEvent(object sender, Mt5ChartEventArgs e)
{
AddLog($"OnChartEvent: ExpertHandle = {e.ExpertHandle}, EventId = {e.EventId}, Lparam = {e.Lparam}, Dparam = {e.Dparam}, Sparam = {e.Sparam}");
}

private void AddQuote(Mt5Quote quote)
{
if (_quotesMap.ContainsKey(quote.ExpertHandle))
Expand Down
Binary file modified mq5/MtApi5.ex5
Binary file not shown.
76 changes: 72 additions & 4 deletions mq5/MtApi5.mq5
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@ input LockTickType BacktestingLockTicks = NO_LOCK;
input group "Disable Events "
input bool Enable_OnBookEvent = true;
input bool Enable_OnTickEvent = false;
input bool Enable_OnTradeTransactionEvent = true;
input bool Enable_OnLastBarEvent = true;
input bool Enable_OnTradeTransactionEvent = true;
input bool Enable_OnLastBarEvent = true;
input bool Enable_OnChartEvent = true;


int ExpertHandle;
Expand Down Expand Up @@ -144,6 +145,28 @@ void OnBookEvent(const string& symbol)
SendMtEvent(ON_BOOK_EVENT, book_event);
}

void OnChartEvent(const int id, const long& lparam, const double& dparam, const string& sparam)
{
if(!Enable_OnChartEvent) return;

#ifdef __DEBUG_LOG__
PrintFormat("%s: id = %d", __FUNCTION__, id);
#endif

// JSONString::toString() in json.mqh performs no escaping, so escape sparam manually
// (backslash first, then quote and control characters) to keep the event JSON valid.
string escaped_sparam = sparam;
StringReplace(escaped_sparam, "\\", "\\\\");
StringReplace(escaped_sparam, "\"", "\\\"");
StringReplace(escaped_sparam, "\n", "\\n");
StringReplace(escaped_sparam, "\r", "\\r");
StringReplace(escaped_sparam, "\t", "\\t");

// Custom events sent with EventChartCustom arrive with id = CHARTEVENT_CUSTOM (1000) + custom event id.
MtOnChartEvent chart_event(id, lparam, dparam, escaped_sparam);
SendMtEvent(ON_CHART_EVENT, chart_event);
}

typedef string (*TExecutor)();

class CExecutorWrapper
Expand Down Expand Up @@ -381,7 +404,9 @@ int preinit()
ADD_EXECUTOR(304, Buy);
ADD_EXECUTOR(305, Sell);
ADD_EXECUTOR(306, GetSymbols);


ADD_EXECUTOR(385, EventChartCustom);

return (0);
}

Expand Down Expand Up @@ -3474,6 +3499,20 @@ string Execute_GetSymbols()
return CreateSuccessResponse(jaSymbols);
}

string Execute_EventChartCustom()
{
GET_JSON_PAYLOAD(jo);
GET_LONG_JSON_VALUE(jo, "ChartId", chart_id);
GET_INT_JSON_VALUE(jo, "CustomEventId", custom_event_id);
GET_LONG_JSON_VALUE(jo, "Lparam", lparam);
GET_DOUBLE_JSON_VALUE(jo, "Dparam", dparam);
GET_STRING_JSON_VALUE(jo, "Sparam", sparam);

// The event is delivered back through OnChartEvent with id = CHARTEVENT_CUSTOM (1000) + custom_event_id.
bool result = EventChartCustom(chart_id, (ushort)custom_event_id, lparam, dparam, sparam);
return CreateSuccessResponse(new JSONBool(result));
}

int PositionCloseAll()
{
CTrade trade;
Expand Down Expand Up @@ -3584,7 +3623,8 @@ enum MtEventTypes
ON_BOOK_EVENT = 2,
ON_TICK_EVENT = 3,
ON_LAST_TIME_BAR_EVENT = 4,
ON_LOCK_TICKS_EVENT = 5
ON_LOCK_TICKS_EVENT = 5,
ON_CHART_EVENT = 6
};

class MtObject
Expand Down Expand Up @@ -3686,6 +3726,34 @@ private:
MqlRates _rates;
};

class MtOnChartEvent: public MtObject
{
public:
MtOnChartEvent(int id, long lparam, double dparam, string sparam)
{
_id = id;
_lparam = lparam;
_dparam = dparam;
_sparam = sparam;
}

virtual JSONObject* CreateJson() const
{
JSONObject *jo = new JSONObject();
jo.put("EventId", new JSONNumber(_id));
jo.put("Lparam", new JSONNumber(_lparam));
jo.put("Dparam", new JSONNumber(_dparam));
jo.put("Sparam", new JSONString(_sparam));
return jo;
}

private:
int _id;
long _lparam;
double _dparam;
string _sparam;
};

class MtLockTickEvent: public MtObject
{
public:
Expand Down