mirror of
https://github.com/vdemydiuk/mtapi.git
synced 2026-08-06 15:37:52 +00:00
Added trade monitors (by timer and by timeframe) to check orders and make event about opened/closed orders
This commit is contained in:
@@ -1,7 +1,39 @@
|
||||
namespace MtApi
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MtApi
|
||||
{
|
||||
static class ExtensionMethods
|
||||
{
|
||||
#region Event Methods
|
||||
|
||||
public static Task FireEventAsync(this MtApiQuoteHandler evenHandler, object sender, string symbol, double bid, double ask)
|
||||
{
|
||||
return Task.Factory.StartNew(() =>
|
||||
{
|
||||
evenHandler?.Invoke(sender, symbol, bid, ask);
|
||||
});
|
||||
}
|
||||
|
||||
public static Task FireEventAsync(this EventHandler eventHandler, object sender)
|
||||
{
|
||||
return Task.Factory.StartNew(() =>
|
||||
{
|
||||
eventHandler?.Invoke(sender, EventArgs.Empty);
|
||||
});
|
||||
}
|
||||
|
||||
public static Task FireEventAsync<T>(this EventHandler<T> eventHandler, object sender, T e)
|
||||
where T : EventArgs
|
||||
{
|
||||
return Task.Factory.StartNew(() =>
|
||||
{
|
||||
eventHandler?.Invoke(sender, e);
|
||||
});
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
public static MtQuote Convert(this MTApiService.MtQuote quote)
|
||||
{
|
||||
return (quote != null) ? new MtQuote(quote.Instrument, quote.Bid, quote.Ask) : null;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace MtApi.Monitors
|
||||
{
|
||||
public class AvailabilityOrdersEventArgs : EventArgs
|
||||
{
|
||||
public AvailabilityOrdersEventArgs(List<MtOrder> opened, List<MtOrder> closed)
|
||||
{
|
||||
Opened = opened;
|
||||
Closed = closed;
|
||||
}
|
||||
|
||||
public List<MtOrder> Opened { get; private set; }
|
||||
public List<MtOrder> Closed { get; private set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
|
||||
namespace MtApi.Monitors
|
||||
{
|
||||
public class TimeframeTradeMonitor : TradeMonitor
|
||||
{
|
||||
#region Fields
|
||||
private volatile bool _isStarted = false;
|
||||
#endregion
|
||||
|
||||
#region ctor
|
||||
public TimeframeTradeMonitor(MtApiClient apiClient)
|
||||
: base(apiClient)
|
||||
{
|
||||
apiClient.OnLastTimeBar += ApiClient_OnLastTimeBar;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
//
|
||||
// Summary:
|
||||
// Gets a value indicating whether the TimeframeTradeMonitor should raise checking orders
|
||||
//
|
||||
// Returns:
|
||||
// true if PositionMonitor should check orders
|
||||
// otherwise, false.
|
||||
public override bool IsStarted
|
||||
{
|
||||
get
|
||||
{
|
||||
return _isStarted;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
protected override void OnMtConnected() {}
|
||||
|
||||
protected override void OnMtDisconnected() {}
|
||||
|
||||
protected override void OnStart()
|
||||
{
|
||||
_isStarted = true;
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
_isStarted = false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void ApiClient_OnLastTimeBar(object sender, TimeBarArgs e)
|
||||
{
|
||||
if (_isStarted)
|
||||
{
|
||||
Check();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
using System.Timers;
|
||||
|
||||
namespace MtApi.Monitors
|
||||
{
|
||||
public class TimerTradeMonitor : TradeMonitor
|
||||
{
|
||||
#region Fields
|
||||
private readonly Timer _timer = new Timer();
|
||||
#endregion
|
||||
|
||||
#region ctor
|
||||
public TimerTradeMonitor(MtApiClient apiClient)
|
||||
: base(apiClient)
|
||||
{
|
||||
_timer.Interval = 10000; //default interval 10 sec
|
||||
_timer.Elapsed += _timer_Elapsed;
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
//
|
||||
// Summary:
|
||||
// Gets or sets the interval, expressed in milliseconds, at which to check orders
|
||||
//
|
||||
// Returns:
|
||||
// The time, in milliseconds, between checking events. The value
|
||||
// must be greater than zero, and less than or equal to System.Int32.MaxValue.
|
||||
// The default is 10000 milliseconds.
|
||||
//
|
||||
// Exceptions:
|
||||
// T:System.ArgumentException:
|
||||
// The interval is less than or equal to zero.-or-The interval is greater than System.Int32.MaxValue,
|
||||
// and the PositionMonitor is currently started.
|
||||
public double Interval
|
||||
{
|
||||
get { return _timer.Interval; }
|
||||
set { _timer.Interval = value; }
|
||||
}
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Gets a value indicating whether the PositionMonitor should raise checking orders
|
||||
//
|
||||
// Returns:
|
||||
// true if TimerTradeMonitor should check orders
|
||||
// otherwise, false.
|
||||
public override bool IsStarted
|
||||
{
|
||||
get { return _timer.Enabled; }
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
protected override void OnStart()
|
||||
{
|
||||
if (IsMtConnected)
|
||||
{
|
||||
_timer.Start();
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnStop()
|
||||
{
|
||||
_timer.Stop();
|
||||
}
|
||||
|
||||
protected override void OnMtConnected()
|
||||
{
|
||||
_timer.Start();
|
||||
}
|
||||
|
||||
protected override void OnMtDisconnected()
|
||||
{
|
||||
_timer.Stop();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void _timer_Elapsed(object sender, ElapsedEventArgs e)
|
||||
{
|
||||
_timer.Elapsed -= _timer_Elapsed; //unregister from events to prevent rise condition during work with orders
|
||||
|
||||
Check();
|
||||
|
||||
_timer.Elapsed += _timer_Elapsed; //register again
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,190 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace MtApi.Monitors
|
||||
{
|
||||
public abstract class TradeMonitor
|
||||
{
|
||||
#region Fields
|
||||
private readonly MtApiClient _apiClient;
|
||||
private List<MtOrder> _prevOrders;
|
||||
private readonly object _locker = new object();
|
||||
#endregion
|
||||
|
||||
#region ctor
|
||||
public TradeMonitor(MtApiClient apiClient)
|
||||
{
|
||||
if (apiClient == null)
|
||||
throw new ArgumentNullException(nameof(apiClient));
|
||||
|
||||
_apiClient = apiClient;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Public Methods
|
||||
//
|
||||
// Summary:
|
||||
// Gets a value indicating whether the TradeMonitor should raise checking orders
|
||||
//
|
||||
// Returns:
|
||||
// true if TradeMonitor should check orders
|
||||
// otherwise, false.
|
||||
public abstract bool IsStarted { get; }
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Start checking orders.
|
||||
//
|
||||
public void Start()
|
||||
{
|
||||
_apiClient.ConnectionStateChanged += _apiClient_ConnectionStateChanged;
|
||||
if (IsMtConnected)
|
||||
{
|
||||
InitialCheck();
|
||||
}
|
||||
|
||||
OnStart();
|
||||
}
|
||||
|
||||
//
|
||||
// Summary:
|
||||
// Stop checking orders.
|
||||
//
|
||||
public void Stop()
|
||||
{
|
||||
_apiClient.ConnectionStateChanged -= _apiClient_ConnectionStateChanged;
|
||||
OnStop();
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
//
|
||||
// Summary:
|
||||
// Occurs when orders are opened or closed.
|
||||
public event EventHandler<AvailabilityOrdersEventArgs> AvailabilityOrdersChanged;
|
||||
#endregion
|
||||
|
||||
#region Protected Methods
|
||||
|
||||
protected abstract void OnStart();
|
||||
protected abstract void OnStop();
|
||||
|
||||
protected abstract void OnMtConnected();
|
||||
protected abstract void OnMtDisconnected();
|
||||
|
||||
public bool IsMtConnected
|
||||
{
|
||||
get
|
||||
{
|
||||
return _apiClient.ConnectionState == MtConnectionState.Connected;
|
||||
}
|
||||
}
|
||||
|
||||
protected void Check()
|
||||
{
|
||||
try
|
||||
{
|
||||
CheckOrders();
|
||||
}
|
||||
catch (MtConnectionException)
|
||||
{
|
||||
//TODO: write error to log
|
||||
}
|
||||
catch (MtExecutionException)
|
||||
{
|
||||
//TODO: write error to log
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Private Methods
|
||||
private void CheckOrders()
|
||||
{
|
||||
var openedOrders = new List<MtOrder>();
|
||||
var closedOrders = new List<MtOrder>();
|
||||
|
||||
// get current orders from MetaTrader
|
||||
var tradesOrders = _apiClient.GetOrders(OrderSelectSource.MODE_TRADES);
|
||||
|
||||
List<MtOrder> prevOrders;
|
||||
lock(_locker)
|
||||
{
|
||||
prevOrders = _prevOrders;
|
||||
}
|
||||
|
||||
if (_prevOrders != null) //skip checking on first load orders
|
||||
{
|
||||
//check open orders
|
||||
foreach (var order in tradesOrders)
|
||||
{
|
||||
if (_prevOrders.Find(a => a.Ticket == order.Ticket) == null)
|
||||
{
|
||||
openedOrders.Add(order);
|
||||
}
|
||||
}
|
||||
|
||||
//check closed orders
|
||||
var closeOrdersTemp = new List<MtOrder>();
|
||||
foreach (var order in _prevOrders)
|
||||
{
|
||||
if (tradesOrders.Find(a => a.Ticket == order.Ticket) == null)
|
||||
{
|
||||
closeOrdersTemp.Add(order);
|
||||
}
|
||||
}
|
||||
|
||||
if (closeOrdersTemp.Count > 0)
|
||||
{
|
||||
//get closed orders from history with actual values
|
||||
var historyOrders = _apiClient.GetOrders(OrderSelectSource.MODE_HISTORY);
|
||||
foreach (var order in closeOrdersTemp)
|
||||
{
|
||||
var closedOrder = historyOrders.Find(a => a.Ticket == order.Ticket);
|
||||
if (closedOrder != null)
|
||||
{
|
||||
closedOrders.Add(closedOrder);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lock(_locker)
|
||||
{
|
||||
_prevOrders = tradesOrders;
|
||||
}
|
||||
|
||||
if (openedOrders.Count > 0 || closedOrders.Count > 0)
|
||||
{
|
||||
AvailabilityOrdersChanged?.Invoke(this, new AvailabilityOrdersEventArgs(openedOrders, closedOrders));
|
||||
}
|
||||
}
|
||||
|
||||
private void _apiClient_ConnectionStateChanged(object sender, MtConnectionEventArgs e)
|
||||
{
|
||||
if (e.Status == MtConnectionState.Connected)
|
||||
{
|
||||
InitialCheck();
|
||||
OnMtConnected();
|
||||
}
|
||||
else if (e.Status == MtConnectionState.Failed || e.Status == MtConnectionState.Disconnected)
|
||||
{
|
||||
OnMtDisconnected();
|
||||
}
|
||||
}
|
||||
|
||||
private void InitialCheck()
|
||||
{
|
||||
lock (_locker)
|
||||
{
|
||||
_prevOrders = null;
|
||||
}
|
||||
|
||||
Task.Factory.StartNew(() =>
|
||||
{
|
||||
Check();
|
||||
});
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -60,6 +60,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="ChartPeriod.cs" />
|
||||
<Compile Include="ExtensionMethods.cs" />
|
||||
<Compile Include="Monitors\AvailabilityOrdersEventArgs.cs" />
|
||||
<Compile Include="MqlRates.cs" />
|
||||
<Compile Include="MtConnectionEventArgs.cs" />
|
||||
<Compile Include="MtConnectionException.cs" />
|
||||
@@ -73,6 +74,7 @@
|
||||
<Compile Include="MtQuoteEventArgs.cs" />
|
||||
<Compile Include="MtTimeBar.cs" />
|
||||
<Compile Include="MtTypes.cs" />
|
||||
<Compile Include="Monitors\TradeMonitor.cs" />
|
||||
<Compile Include="PriceConstantsType.cs" />
|
||||
<Compile Include="MarketInfoModeType.cs" />
|
||||
<Compile Include="MtApiClient.cs" />
|
||||
@@ -98,6 +100,8 @@
|
||||
<Compile Include="Responses\ResponseBase.cs" />
|
||||
<Compile Include="SeriesIdentifier.cs" />
|
||||
<Compile Include="TimeBarArgs.cs" />
|
||||
<Compile Include="Monitors\TimeframeTradeMonitor.cs" />
|
||||
<Compile Include="Monitors\TimerTradeMonitor.cs" />
|
||||
<Compile Include="TradeOperation.cs" />
|
||||
<Compile Include="MtApiTimeConverter.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
|
||||
+8
-13
@@ -485,10 +485,10 @@ namespace MtApi
|
||||
return response != null ? response.Order : null;
|
||||
}
|
||||
|
||||
public IEnumerable<MtOrder> GetOrders(OrderSelectSource pool)
|
||||
public List<MtOrder> GetOrders(OrderSelectSource pool)
|
||||
{
|
||||
var response = SendRequest<GetOrdersResponse>(new GetOrdersRequest { Pool = (int)pool });
|
||||
return response != null ? response.Orders : null;
|
||||
return response != null ? response.Orders : new List<MtOrder>();
|
||||
}
|
||||
#endregion
|
||||
|
||||
@@ -1490,8 +1490,7 @@ namespace MtApi
|
||||
|
||||
if (changed)
|
||||
{
|
||||
var handler = ConnectionStateChanged;
|
||||
handler?.BeginInvoke(this, new MtConnectionEventArgs(state, message), (a) => handler.EndInvoke(a), null);
|
||||
ConnectionStateChanged.FireEventAsync(this, new MtConnectionEventArgs(state, message));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1573,14 +1572,13 @@ namespace MtApi
|
||||
{
|
||||
if (quote != null)
|
||||
{
|
||||
var handler = QuoteUpdated;
|
||||
if (IsBacktestingMode)
|
||||
{
|
||||
handler?.Invoke(this, quote.Instrument, quote.Bid, quote.Ask);
|
||||
QuoteUpdated?.Invoke(this, quote.Instrument, quote.Bid, quote.Ask);
|
||||
}
|
||||
else
|
||||
{
|
||||
handler?.BeginInvoke(this, quote.Instrument, quote.Bid, quote.Ask, (a) => handler.EndInvoke(a), null);
|
||||
QuoteUpdated.FireEventAsync(this, quote.Instrument, quote.Bid, quote.Ask);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1597,14 +1595,12 @@ namespace MtApi
|
||||
|
||||
private void mClient_QuoteRemoved(MTApiService.MtQuote quote)
|
||||
{
|
||||
var handler = QuoteRemoved;
|
||||
handler?.BeginInvoke(this, new MtQuoteEventArgs(quote.Convert()), (a) => handler.EndInvoke(a), null);
|
||||
QuoteRemoved.FireEventAsync(this, new MtQuoteEventArgs(quote.Convert()));
|
||||
}
|
||||
|
||||
private void mClient_QuoteAdded(MTApiService.MtQuote quote)
|
||||
{
|
||||
var handler = QuoteAdded;
|
||||
handler?.BeginInvoke(this, new MtQuoteEventArgs(quote.Convert()), (a) => handler.EndInvoke(a), null);
|
||||
QuoteAdded.FireEventAsync(this, new MtQuoteEventArgs(quote.Convert()));
|
||||
}
|
||||
|
||||
private void _client_MtEventReceived(object sender, MtEventArgs e)
|
||||
@@ -1623,8 +1619,7 @@ namespace MtApi
|
||||
|
||||
private void FireOnLastTimeBar(MtTimeBar timeBar)
|
||||
{
|
||||
var handler = OnLastTimeBar;
|
||||
handler?.BeginInvoke(this, new TimeBarArgs(timeBar), (a) => handler.EndInvoke(a), null);
|
||||
OnLastTimeBar.FireEventAsync(this, new TimeBarArgs(timeBar));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -5,6 +5,7 @@ using System.Windows.Forms;
|
||||
using MtApi;
|
||||
using System.Threading.Tasks;
|
||||
using System.Linq;
|
||||
using MtApi.Monitors;
|
||||
|
||||
namespace TestApiClientUI
|
||||
{
|
||||
@@ -12,6 +13,8 @@ namespace TestApiClientUI
|
||||
{
|
||||
private readonly List<Action> _groupOrderCommands = new List<Action>();
|
||||
private readonly MtApiClient _apiClient = new MtApiClient();
|
||||
private readonly TimerTradeMonitor _timerTradeMonitor;
|
||||
private readonly TimeframeTradeMonitor _timeframeTradeMonitor;
|
||||
|
||||
public Form1()
|
||||
{
|
||||
@@ -29,6 +32,13 @@ namespace TestApiClientUI
|
||||
|
||||
comboBox1.SelectedIndex = 0;
|
||||
comboBox2.SelectedIndex = 0;
|
||||
|
||||
_timerTradeMonitor = new TimerTradeMonitor(_apiClient);
|
||||
_timerTradeMonitor.Interval = 1000; // 1 sec
|
||||
_timerTradeMonitor.AvailabilityOrdersChanged += _tradeMonitor_AvailabilityOrdersChanged;
|
||||
|
||||
_timeframeTradeMonitor = new TimeframeTradeMonitor(_apiClient);
|
||||
_timeframeTradeMonitor.AvailabilityOrdersChanged += _tradeMonitor_AvailabilityOrdersChanged;
|
||||
}
|
||||
|
||||
private void initOrderCommandsGroup()
|
||||
@@ -200,16 +210,20 @@ namespace TestApiClientUI
|
||||
int port;
|
||||
int.TryParse(textBoxPort.Text, out port);
|
||||
|
||||
_timerTradeMonitor.Start();
|
||||
_timeframeTradeMonitor.Start();
|
||||
|
||||
if (string.IsNullOrEmpty(serverName))
|
||||
_apiClient.BeginConnect(port);
|
||||
else
|
||||
_apiClient.BeginConnect(serverName, port);
|
||||
|
||||
OnConnected();
|
||||
}
|
||||
|
||||
private void buttonDisconnect_Click(object sender, EventArgs e)
|
||||
{
|
||||
_timerTradeMonitor.Stop();
|
||||
_timeframeTradeMonitor.Stop();
|
||||
|
||||
_apiClient.BeginDisconnect();
|
||||
}
|
||||
|
||||
@@ -1052,6 +1066,7 @@ namespace TestApiClientUI
|
||||
AddToLog(string.Format("ICustom result: {0}", retVal));
|
||||
}
|
||||
|
||||
//CopyRates
|
||||
private async void button24_Click(object sender, EventArgs e)
|
||||
{
|
||||
string symbol = textBoxSelectedSymbol.Text;
|
||||
@@ -1078,6 +1093,7 @@ namespace TestApiClientUI
|
||||
}
|
||||
}
|
||||
|
||||
//CopyRates
|
||||
private async void button25_Click(object sender, EventArgs e)
|
||||
{
|
||||
string symbol = textBoxSelectedSymbol.Text;
|
||||
@@ -1104,6 +1120,7 @@ namespace TestApiClientUI
|
||||
}
|
||||
}
|
||||
|
||||
//CopyRates
|
||||
private async void button26_Click(object sender, EventArgs e)
|
||||
{
|
||||
string symbol = textBoxSelectedSymbol.Text;
|
||||
@@ -1130,6 +1147,7 @@ namespace TestApiClientUI
|
||||
}
|
||||
}
|
||||
|
||||
//Print
|
||||
private void button27_Click(object sender, EventArgs e)
|
||||
{
|
||||
var msg = textBoxPrint.Text;
|
||||
@@ -1139,5 +1157,19 @@ namespace TestApiClientUI
|
||||
AddToLog(string.Format("Print executed"));
|
||||
}
|
||||
}
|
||||
|
||||
private void _tradeMonitor_AvailabilityOrdersChanged(object sender, AvailabilityOrdersEventArgs e)
|
||||
{
|
||||
if (e.Opened != null)
|
||||
{
|
||||
AddToLog($"{sender.GetType()}: Opened orders - {string.Join(", ", e.Opened.Select(o => o.Ticket).ToList())}");
|
||||
}
|
||||
|
||||
if (e.Closed != null)
|
||||
{
|
||||
AddToLog($"{sender.GetType()}: Closed orders - {string.Join(", ", e.Closed.Select(o => o.Ticket).ToList())}");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user