mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-07-27 18:47:55 +00:00
Issue #122: Implemented UnlockTicks function in MtApi (MT5)
This commit is contained in:
+41
-12
@@ -4,35 +4,51 @@ namespace MTApiService
|
||||
{
|
||||
public class Mt5Expert : MtExpert
|
||||
{
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(MtExpert));
|
||||
private static readonly ILog Log = LogManager.GetLogger(typeof(Mt5Expert));
|
||||
private const int StopExpertInterval = 2000; // 2 sec for testing mode
|
||||
private readonly System.Timers.Timer _stopTimer = new System.Timers.Timer();
|
||||
private System.Timers.Timer _stopTimer;
|
||||
|
||||
|
||||
public Mt5Expert(int handle, string symbol, double bid, double ask, IMetaTraderHandler mtHandler, bool isTestMode) :
|
||||
base(handle, symbol, bid, ask, mtHandler)
|
||||
{
|
||||
IsTestMode = isTestMode;
|
||||
_stopTimer.Interval = StopExpertInterval;
|
||||
_stopTimer.Elapsed += _stopTimer_Elapsed;
|
||||
}
|
||||
|
||||
public bool IsTestMode { get; }
|
||||
|
||||
public override void UpdateQuote(MtQuote quote)
|
||||
public override int GetCommandType()
|
||||
{
|
||||
Log.Debug("UpdateQuote: begin.");
|
||||
|
||||
base.UpdateQuote(quote);
|
||||
Log.Debug("GetCommandType: called.");
|
||||
|
||||
if (IsTestMode)
|
||||
{
|
||||
//reset timer
|
||||
_stopTimer.Stop();
|
||||
_stopTimer.Start();
|
||||
ResetTestModeTimer();
|
||||
}
|
||||
|
||||
Log.Debug("UpdateQuote: end.");
|
||||
return base.GetCommandType();
|
||||
}
|
||||
|
||||
public override void SendEvent(MtEvent mtEvent)
|
||||
{
|
||||
Log.DebugFormat("SendEvent: begin. event = {0}", mtEvent);
|
||||
|
||||
if (IsTestMode)
|
||||
{
|
||||
if (_stopTimer == null)
|
||||
{
|
||||
_stopTimer = new System.Timers.Timer
|
||||
{
|
||||
Interval = StopExpertInterval,
|
||||
AutoReset = false
|
||||
};
|
||||
_stopTimer.Elapsed += _stopTimer_Elapsed;
|
||||
}
|
||||
}
|
||||
|
||||
base.SendEvent(mtEvent);
|
||||
|
||||
Log.Debug("SendEvent: end.");
|
||||
}
|
||||
|
||||
private void _stopTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e)
|
||||
@@ -42,7 +58,20 @@ namespace MTApiService
|
||||
Log.Warn("Mt5Expert has received new tick during 2 sec in testing mode. The possible cause: user has stopped the tester manually in MetaTrader 5.");
|
||||
Deinit();
|
||||
|
||||
_stopTimer.Elapsed -= _stopTimer_Elapsed;
|
||||
_stopTimer = null;
|
||||
|
||||
Log.Debug("_stopTimer_Elapsed: end.");
|
||||
}
|
||||
|
||||
private void ResetTestModeTimer()
|
||||
{
|
||||
if (_stopTimer == null)
|
||||
return;
|
||||
|
||||
//reset timer
|
||||
_stopTimer.Stop();
|
||||
_stopTimer.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +66,7 @@ namespace MTApiService
|
||||
}
|
||||
|
||||
server.AddExpert(expert);
|
||||
expert.Deinited += ExpertOnDeinited;
|
||||
|
||||
Log.Info("AddExpert: end");
|
||||
}
|
||||
@@ -81,7 +82,6 @@ namespace MTApiService
|
||||
if (_experts.ContainsKey(expertHandle))
|
||||
{
|
||||
expert = _experts[expertHandle];
|
||||
_experts.Remove(expertHandle);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -270,6 +270,31 @@ namespace MTApiService
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void ExpertOnDeinited(object sender, EventArgs eventArgs)
|
||||
{
|
||||
Log.Debug("ExpertOnDeinited: begin.");
|
||||
|
||||
var expert = sender as MtExpert;
|
||||
if (expert == null)
|
||||
{
|
||||
Log.Warn("expert_Deinited: end. Expert is not defined.");
|
||||
return;
|
||||
}
|
||||
|
||||
lock (_experts)
|
||||
{
|
||||
if (_experts.ContainsKey(expert.Handle))
|
||||
{
|
||||
_experts.Remove(expert.Handle);
|
||||
}
|
||||
}
|
||||
|
||||
Log.DebugFormat("ExpertOnDeinited: removed expert {0}", expert.Handle);
|
||||
|
||||
Log.Debug("ExpertOnDeinited: end.");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ namespace MTApiService
|
||||
Log.Debug("SendResponse: end.");
|
||||
}
|
||||
|
||||
public int GetCommandType()
|
||||
public virtual int GetCommandType()
|
||||
{
|
||||
Log.Debug("GetCommandType: called.");
|
||||
|
||||
@@ -110,7 +110,7 @@ namespace MTApiService
|
||||
return command.NamedParams.ContainsKey(name);
|
||||
}
|
||||
|
||||
public void SendEvent(MtEvent mtEvent)
|
||||
public virtual void SendEvent(MtEvent mtEvent)
|
||||
{
|
||||
Log.DebugFormat("SendEvent: begin. event = {0}", mtEvent);
|
||||
|
||||
@@ -119,7 +119,7 @@ namespace MTApiService
|
||||
Log.Debug("SendEvent: end.");
|
||||
}
|
||||
|
||||
public virtual void UpdateQuote(MtQuote quote)
|
||||
public void UpdateQuote(MtQuote quote)
|
||||
{
|
||||
Log.DebugFormat("UpdateQuote: begin. quote = {0}", quote);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
OnTradeTransaction = 1,
|
||||
OnBookEvent = 2,
|
||||
OnTick = 3
|
||||
OnTick = 3,
|
||||
OnLastTimeBar = 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace MtApi5.Events
|
||||
{
|
||||
public class OnLastTimeBarEvent
|
||||
{
|
||||
public MqlRates Rates { get; set; }
|
||||
public string Instrument { get; set; }
|
||||
public int ExpertHandle { get; set; }
|
||||
}
|
||||
}
|
||||
+1
-3
@@ -7,7 +7,6 @@ namespace MtApi5
|
||||
{
|
||||
public MqlRates(DateTime time, double open, double high, double low, double close, long tick_volume, int spread, long real_volume)
|
||||
{
|
||||
this.time = time;
|
||||
mt_time = Mt5TimeConverter.ConvertToMtTime(time);
|
||||
this.open = open;
|
||||
this.high = high;
|
||||
@@ -20,7 +19,6 @@ namespace MtApi5
|
||||
|
||||
internal MqlRates(long time, double open, double high, double low, double close, long tick_volume, int spread, long real_volume)
|
||||
{
|
||||
this.time = Mt5TimeConverter.ConvertFromMtTime(time);
|
||||
mt_time = time;
|
||||
this.open = open;
|
||||
this.high = high;
|
||||
@@ -35,7 +33,7 @@ namespace MtApi5
|
||||
{
|
||||
}
|
||||
|
||||
public DateTime time { get; set; } // Period start time
|
||||
public DateTime time => Mt5TimeConverter.ConvertFromMtTime(mt_time); // Period start time
|
||||
public long mt_time { get; set; } // Period start time (original MT time)
|
||||
public double open { get; set; } // Open price
|
||||
public double high { get; set; } // The highest price of the period
|
||||
|
||||
@@ -248,6 +248,8 @@ namespace MtApi5
|
||||
GlobalVariableTemp = 154,
|
||||
GlobalVariableSetOnCondition = 156,
|
||||
GlobalVariablesDeleteAll = 157,
|
||||
GlobalVariablesTotal = 158
|
||||
GlobalVariablesTotal = 158,
|
||||
|
||||
UnlockTicks = 159
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
using System;
|
||||
|
||||
namespace MtApi5
|
||||
{
|
||||
public class Mt5TimeBarArgs: EventArgs
|
||||
{
|
||||
internal Mt5TimeBarArgs(int expertHandle, string symbol, MqlRates rates)
|
||||
{
|
||||
ExpertHandle = expertHandle;
|
||||
Rates = rates;
|
||||
Symbol = symbol;
|
||||
}
|
||||
|
||||
public int ExpertHandle { get; }
|
||||
public string Symbol { get; }
|
||||
public MqlRates Rates { get; }
|
||||
}
|
||||
}
|
||||
@@ -49,8 +49,10 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="CopyTicksFlag.cs" />
|
||||
<Compile Include="Events\OnBookEvent.cs" />
|
||||
<Compile Include="Events\OnLastTimeBarEvent.cs" />
|
||||
<Compile Include="Events\OnTickEvent.cs" />
|
||||
<Compile Include="Events\OnTradeTransactionEvent.cs" />
|
||||
<Compile Include="Mt5TimeBarArgs.cs" />
|
||||
<Compile Include="MqlBookInfo.cs" />
|
||||
<Compile Include="MqlParam.cs" />
|
||||
<Compile Include="MqlRates.cs" />
|
||||
|
||||
@@ -53,6 +53,7 @@ namespace MtApi5
|
||||
_mtEventHandlers[Mt5EventTypes.OnBookEvent] = ReceivedOnBookEvent;
|
||||
_mtEventHandlers[Mt5EventTypes.OnTick] = ReceivedOnTickEvent;
|
||||
_mtEventHandlers[Mt5EventTypes.OnTradeTransaction] = ReceivedOnTradeTransaction;
|
||||
_mtEventHandlers[Mt5EventTypes.OnLastTimeBar] = ReceivedOnLastTimeBar;
|
||||
}
|
||||
|
||||
///<summary>
|
||||
@@ -3057,6 +3058,18 @@ namespace MtApi5
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Backtesting functions
|
||||
|
||||
///<summary>
|
||||
///The function unlock ticks in backtesting mode.
|
||||
///</summary>
|
||||
public void UnlockTicks()
|
||||
{
|
||||
SendCommand<object>(Mt5CommandType.UnlockTicks, null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion // Public Methods
|
||||
|
||||
#region Properties
|
||||
@@ -3104,6 +3117,7 @@ namespace MtApi5
|
||||
public event EventHandler<Mt5ConnectionEventArgs> ConnectionStateChanged;
|
||||
public event EventHandler<Mt5TradeTransactionEventArgs> OnTradeTransaction;
|
||||
public event EventHandler<Mt5BookEventArgs> OnBookEvent;
|
||||
public event EventHandler<Mt5TimeBarArgs> OnLastTimeBar;
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
@@ -3216,6 +3230,12 @@ namespace MtApi5
|
||||
QuoteUpdate?.Invoke(this, new Mt5QuoteEventArgs(quote));
|
||||
}
|
||||
|
||||
private void ReceivedOnLastTimeBar(int expertHandler, string payload)
|
||||
{
|
||||
var e = JsonConvert.DeserializeObject<OnLastTimeBarEvent>(payload);
|
||||
OnLastTimeBar?.Invoke(this, new Mt5TimeBarArgs(expertHandler, e.Instrument, e.Rates));
|
||||
}
|
||||
|
||||
private void Connect(string host, int port)
|
||||
{
|
||||
var client = new MtClient(host, port);
|
||||
|
||||
@@ -574,6 +574,7 @@
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="*" />
|
||||
<RowDefinition Height="Auto" />
|
||||
<RowDefinition Height="Auto" />
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<WrapPanel VerticalAlignment="Top" Margin="5">
|
||||
@@ -581,7 +582,9 @@
|
||||
<Button Command="{Binding ResetLastErrorCommand}" Content="ResetLastError" Margin="2"/>
|
||||
</WrapPanel>
|
||||
|
||||
<Grid Margin="10" Grid.Row="1">
|
||||
<Button Grid.Row="1" Content="UnlockTicks" HorizontalAlignment="Left" Margin="5" Command="{Binding UnlockTicksCommand}"/>
|
||||
|
||||
<Grid Margin="10" Grid.Row="2">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
|
||||
@@ -121,6 +121,8 @@ namespace MtApi5TestClient
|
||||
public DelegateCommand GlobalVariableSetOnConditionCommand { get; private set; }
|
||||
public DelegateCommand GlobalVariablesDeleteAllCommand { get; private set; }
|
||||
public DelegateCommand GlobalVariablesTotalCommand { get; private set; }
|
||||
|
||||
public DelegateCommand UnlockTicksCommand { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
@@ -289,6 +291,7 @@ namespace MtApi5TestClient
|
||||
_mtApiClient.QuoteUpdate += mMtApiClient_QuoteUpdate;
|
||||
_mtApiClient.OnTradeTransaction += mMtApiClient_OnTradeTransaction;
|
||||
_mtApiClient.OnBookEvent += _mtApiClient_OnBookEvent;
|
||||
_mtApiClient.OnLastTimeBar += _mtApiClient_OnOnLastTimeBar;
|
||||
|
||||
ConnectionState = _mtApiClient.ConnectionState;
|
||||
ConnectionMessage = "Disconnected";
|
||||
@@ -424,6 +427,8 @@ namespace MtApi5TestClient
|
||||
GlobalVariableSetOnConditionCommand = new DelegateCommand(ExecuteGlobalVariableSetOnCondition);
|
||||
GlobalVariablesDeleteAllCommand = new DelegateCommand(ExecuteGlobalVariablesDeleteAll);
|
||||
GlobalVariablesTotalCommand = new DelegateCommand(ExecuteGlobalVariablesTotal);
|
||||
|
||||
UnlockTicksCommand = new DelegateCommand(ExecuteUnlockTicks);
|
||||
}
|
||||
|
||||
private bool CanExecuteConnect(object o)
|
||||
@@ -1564,6 +1569,11 @@ namespace MtApi5TestClient
|
||||
}
|
||||
#endregion
|
||||
|
||||
private void ExecuteUnlockTicks(object o)
|
||||
{
|
||||
_mtApiClient.UnlockTicks();
|
||||
}
|
||||
|
||||
private static void RunOnUiThread(Action action)
|
||||
{
|
||||
Application.Current?.Dispatcher.Invoke(action);
|
||||
@@ -1640,6 +1650,11 @@ namespace MtApi5TestClient
|
||||
AddLog($"OnBookEvent: ExpertHandle = {e.ExpertHandle}, Symbol = {e.Symbol}");
|
||||
}
|
||||
|
||||
private void _mtApiClient_OnOnLastTimeBar(object sender, Mt5TimeBarArgs e)
|
||||
{
|
||||
AddLog($"OnBookEvent: 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}");
|
||||
}
|
||||
|
||||
private void AddQuote(Mt5Quote quote)
|
||||
{
|
||||
if (_quotesMap.ContainsKey(quote.ExpertHandle))
|
||||
|
||||
Binary file not shown.
+108
-8
@@ -41,9 +41,17 @@
|
||||
bool getBooleanValue(int expertHandle, int paramIndex, bool& res, string& err);
|
||||
#import
|
||||
|
||||
//#define __DEBUG_LOG__
|
||||
#define __DEBUG_LOG__
|
||||
|
||||
enum LockTickType
|
||||
{
|
||||
NO_LOCK,
|
||||
LOCK_EVERY_TICK,
|
||||
LOCK_EVERY_CANDLE
|
||||
};
|
||||
|
||||
input int Port = 8228;
|
||||
input LockTickType BacktestingLockTicks = NO_LOCK;
|
||||
|
||||
int ExpertHandle;
|
||||
|
||||
@@ -53,6 +61,9 @@ bool isCrashed = false;
|
||||
|
||||
bool IsRemoteReadyForTesting = false;
|
||||
|
||||
long _last_bar_open_time = 0;
|
||||
bool _is_ticks_locked = false;
|
||||
|
||||
string PARAM_SEPARATOR = ";";
|
||||
|
||||
int OnInit()
|
||||
@@ -74,14 +85,44 @@ void OnDeinit(const int reason)
|
||||
|
||||
void OnTick()
|
||||
{
|
||||
string symbol = Symbol();
|
||||
|
||||
bool lastbar_time_changed = false;
|
||||
long lastbar_time = SeriesInfoInteger(symbol, Period(), SERIES_LASTBAR_DATE);
|
||||
if (_last_bar_open_time != lastbar_time)
|
||||
{
|
||||
if (_last_bar_open_time != 0)
|
||||
{
|
||||
MqlRates rates_array[];
|
||||
CopyRates(symbol, Period(), 1, 1, rates_array);
|
||||
|
||||
MtTimeBar* timeBar = new MtTimeBar(symbol, rates_array[0]);
|
||||
SendMtEvent(LAST_TIME_BAR_EVENT, timeBar);
|
||||
delete timeBar;
|
||||
|
||||
lastbar_time_changed = true;
|
||||
}
|
||||
|
||||
_last_bar_open_time = lastbar_time;
|
||||
}
|
||||
|
||||
MqlTick last_tick;
|
||||
SymbolInfoTick(Symbol(),last_tick);
|
||||
|
||||
MtOnTickEvent * tick_event = new MtOnTickEvent(Symbol(), last_tick);
|
||||
MtOnTickEvent * tick_event = new MtOnTickEvent(symbol, last_tick);
|
||||
SendMtEvent(ON_TICK_EVENT, tick_event);
|
||||
delete tick_event;
|
||||
delete tick_event;
|
||||
|
||||
if (IsTesting()) OnTimer();
|
||||
if (IsTesting())
|
||||
{
|
||||
if (BacktestingLockTicks == LOCK_EVERY_TICK ||
|
||||
(BacktestingLockTicks == LOCK_EVERY_CANDLE && lastbar_time_changed))
|
||||
{
|
||||
_is_ticks_locked = true;
|
||||
}
|
||||
|
||||
OnTimer();
|
||||
}
|
||||
}
|
||||
|
||||
void OnTradeTransaction(
|
||||
@@ -224,10 +265,12 @@ void OnTimer()
|
||||
while(true)
|
||||
{
|
||||
int executedCommand = executeCommand();
|
||||
|
||||
if (_is_ticks_locked)
|
||||
continue;
|
||||
|
||||
if (executedCommand == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -741,6 +784,9 @@ int executeCommand()
|
||||
case 158: //GlobalVariablesTotal
|
||||
Execute_GlobalVariablesTotal();
|
||||
break;
|
||||
case 159: //UnlockTiks
|
||||
Execute_UnlockTicks();
|
||||
break;
|
||||
case 204: //TerminalInfoInteger
|
||||
Execute_TerminalInfoInteger();
|
||||
break;
|
||||
@@ -6461,6 +6507,22 @@ void Execute_GlobalVariablesTotal()
|
||||
SEND_INT_RESPONSE(res)
|
||||
}
|
||||
|
||||
void Execute_UnlockTicks()
|
||||
{
|
||||
if (!IsTesting())
|
||||
{
|
||||
Print("WARNING: function UnlockTicks can be used only for backtesting");
|
||||
return;
|
||||
}
|
||||
|
||||
_is_ticks_locked = false;
|
||||
|
||||
if (!sendVoidResponse(ExpertHandle, _response_error))
|
||||
{
|
||||
PrintResponseError("UnlockTicks", _response_error);
|
||||
}
|
||||
}
|
||||
|
||||
void Execute_TerminalInfoInteger()
|
||||
{
|
||||
int propertyId;
|
||||
@@ -7220,7 +7282,8 @@ enum MtEventTypes
|
||||
{
|
||||
ON_TRADE_TRANSACTION_EVENT = 1,
|
||||
ON_BOOK_EVENT = 2,
|
||||
ON_TICK_EVENT = 3
|
||||
ON_TICK_EVENT = 3,
|
||||
LAST_TIME_BAR_EVENT = 4
|
||||
};
|
||||
|
||||
class MtEvent
|
||||
@@ -7296,6 +7359,29 @@ private:
|
||||
MqlTick _tick;
|
||||
};
|
||||
|
||||
class MtTimeBar: public MtEvent
|
||||
{
|
||||
public:
|
||||
MtTimeBar(string symbol, const MqlRates& rates)
|
||||
{
|
||||
_symbol = symbol;
|
||||
_rates = rates;
|
||||
}
|
||||
|
||||
virtual JSONObject* CreateJson()
|
||||
{
|
||||
JSONObject *jo = new JSONObject();
|
||||
jo.put("Rates", MqlRatesToJson(_rates));
|
||||
jo.put("Instrument", new JSONString(_symbol));
|
||||
jo.put("ExpertHandle", new JSONNumber(ExpertHandle));
|
||||
return jo;
|
||||
}
|
||||
|
||||
private:
|
||||
string _symbol;
|
||||
MqlRates _rates;
|
||||
};
|
||||
|
||||
void SendMtEvent(MtEventTypes eventType, MtEvent* mtEvent)
|
||||
{
|
||||
#ifdef __DEBUG_LOG__
|
||||
@@ -7478,4 +7564,18 @@ JSONObject* MqlTradeRequestToJson(MqlTradeRequest& request)
|
||||
jo.put("MtExpiration", new JSONNumber((int)request.expiration));
|
||||
jo.put("Comment", new JSONString(request.comment));
|
||||
return jo;
|
||||
}
|
||||
|
||||
JSONObject* MqlRatesToJson(MqlRates& rates)
|
||||
{
|
||||
JSONObject *jo = new JSONObject();
|
||||
jo.put("mt_time", new JSONNumber((int)rates.time));
|
||||
jo.put("open", new JSONNumber(rates.open));
|
||||
jo.put("high", new JSONNumber(rates.high));
|
||||
jo.put("low", new JSONNumber(rates.low));
|
||||
jo.put("close", new JSONNumber(rates.close));
|
||||
jo.put("tick_volume", new JSONNumber(rates.tick_volume));
|
||||
jo.put("spread", new JSONNumber(rates.spread));
|
||||
jo.put("real_volume", new JSONNumber(rates.real_volume));
|
||||
return jo;
|
||||
}
|
||||
Reference in New Issue
Block a user