diff --git a/MtApi/Monitors/AvailabilityOrdersEventArgs.cs b/MtApi/Monitors/AvailabilityOrdersEventArgs.cs
index f6bea3c8..3f569321 100644
--- a/MtApi/Monitors/AvailabilityOrdersEventArgs.cs
+++ b/MtApi/Monitors/AvailabilityOrdersEventArgs.cs
@@ -10,8 +10,13 @@ namespace MtApi.Monitors
Opened = opened;
Closed = closed;
}
-
+ ///
+ /// Contains all newly opened orders since the last time the monitor checked the open orders.
+ ///
public List Opened { get; private set; }
+ ///
+ /// Contains all newly closed orders since the last time the monitor checked the open orders.
+ ///
public List Closed { get; private set; }
}
}
diff --git a/MtApi/Monitors/MtMonitorBase.cs b/MtApi/Monitors/MtMonitorBase.cs
new file mode 100644
index 00000000..8f5c0407
--- /dev/null
+++ b/MtApi/Monitors/MtMonitorBase.cs
@@ -0,0 +1,113 @@
+using System;
+using MtApi.Monitors.Triggers;
+
+namespace MtApi.Monitors
+{
+ public abstract class MtMonitorBase
+ {
+ #region Fields
+ private volatile bool _isStarted = false;
+ private bool _syncTrigger;
+ #endregion
+
+ #region Properties
+ ///
+ /// ApiClient
+ ///
+ protected MtApiClient ApiClient { get; }
+ ///
+ /// Returns true if the is connected.
+ ///
+ public bool IsMtConnected => ApiClient.ConnectionState == MtConnectionState.Connected;
+ ///
+ /// Returns the trigger which will be used to raise the monitoring call.
+ ///
+ public IMonitorTrigger MonitorTrigger { get; }
+ ///
+ /// Returns true if the Monitor is started.
+ ///
+ public bool IsStarted { get => _isStarted; }
+ ///
+ /// If true, the will be stopped or started automatically when or will be called.
+ /// CAUTION: If you use the MonitorTrigger for different Monitors, this will stop all monitors if you call stop and is true.
+ ///
+ public bool SyncTrigger { get => _syncTrigger; set => _syncTrigger = value; }
+ #endregion
+
+ #region ctor
+ ///
+ /// Default constructor for Monitors
+ ///
+ /// The apiClient which will be used to work with.
+ /// The trigger which lead this Monitor to do his work.
+ /// See property .
+ public MtMonitorBase(MtApiClient apiClient, IMonitorTrigger monitorTrigger, bool syncTrigger = false)
+ {
+ ApiClient = apiClient ?? throw new ArgumentNullException(nameof(apiClient));
+ MonitorTrigger = monitorTrigger ?? throw new ArgumentNullException(nameof(monitorTrigger));
+ SyncTrigger = syncTrigger;
+ }
+ #endregion
+
+ #region Methods
+ ///
+ /// Let the monitor listen to the .
+ ///
+ public virtual void Start()
+ {
+ if (!_isStarted)
+ {
+ ApiClient.ConnectionStateChanged += ApiClientConnectionStateChanged;
+ MonitorTrigger.Raised += MonitorTriggerRaised;
+ _isStarted = true;
+ OnStart();
+ if (SyncTrigger)
+ MonitorTrigger.Start();
+ }
+ }
+ ///
+ /// Let the monitor stop listening to the .
+ ///
+ public virtual void Stop()
+ {
+ if (_isStarted)
+ {
+ MonitorTrigger.Raised -= MonitorTriggerRaised;
+ ApiClient.ConnectionStateChanged -= ApiClientConnectionStateChanged;
+ _isStarted = false;
+ OnStop();
+ if (SyncTrigger)
+ MonitorTrigger.Stop();
+ }
+ }
+ private void MonitorTriggerRaised(object sender, EventArgs e) => OnTriggerRaised();
+ private void ApiClientConnectionStateChanged(object sender, MtConnectionEventArgs e)
+ {
+ if (e.Status == MtConnectionState.Connected)
+ OnMtConnected();
+ else if (e.Status == MtConnectionState.Failed || e.Status == MtConnectionState.Disconnected)
+ OnMtDisconnected();
+ }
+ ///
+ /// Will be called when will be called.
+ ///
+ protected virtual void OnStart() { }
+ ///
+ /// Will be called when will be called.
+ ///
+ protected virtual void OnStop() { }
+ ///
+ /// Will be called when the is successfully connected.
+ ///
+ protected virtual void OnMtConnected() { }
+ ///
+ /// Will be called when is disconnected.
+ ///
+ protected virtual void OnMtDisconnected() { }
+ ///
+ /// Will be called when the raised.
+ ///
+ protected abstract void OnTriggerRaised();
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MtApi/Monitors/OrderModification/ModifiedOrdersEventArgs.cs b/MtApi/Monitors/OrderModification/ModifiedOrdersEventArgs.cs
new file mode 100644
index 00000000..d803ea8d
--- /dev/null
+++ b/MtApi/Monitors/OrderModification/ModifiedOrdersEventArgs.cs
@@ -0,0 +1,20 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MtApi.Monitors
+{
+ public class ModifiedOrdersEventArgs : EventArgs
+ {
+ ///
+ /// Returns a list of all modified orders
+ ///
+ public List ModifiedOrders { get; }
+ public ModifiedOrdersEventArgs(List modifiedOrders)
+ {
+ ModifiedOrders = modifiedOrders;
+ }
+ }
+}
diff --git a/MtApi/Monitors/OrderModification/ModifiedOrdersMonitor.cs b/MtApi/Monitors/OrderModification/ModifiedOrdersMonitor.cs
new file mode 100644
index 00000000..5a4618f6
--- /dev/null
+++ b/MtApi/Monitors/OrderModification/ModifiedOrdersMonitor.cs
@@ -0,0 +1,68 @@
+using MtApi.Monitors.Triggers;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MtApi.Monitors
+{
+ public class ModifiedOrdersMonitor : MtMonitorBase
+ {
+ #region Fields
+ private List _lastOrders = null;
+
+ #endregion
+
+ #region Properties
+ ///
+ /// Define on which types of modification this monitor should raise
+ ///
+ public OrderModifiedTypes OrderModifiedTypes { get; set; }
+ #endregion
+
+ #region Events
+ ///
+ /// Will be raised when this monitor detects changes on open orders
+ ///
+ public event EventHandler OrdersModified;
+ #endregion
+
+ #region ctor
+ public ModifiedOrdersMonitor(MtApiClient apiClient, IMonitorTrigger monitorTrigger, OrderModifiedTypes orderModifiedTypes = OrderModifiedTypes.All, bool syncTrigger = false)
+ : base(apiClient, monitorTrigger, syncTrigger)
+ {
+ _lastOrders = GetOrders();
+ OrderModifiedTypes = orderModifiedTypes;
+ }
+ #endregion
+
+ ///
+ /// Requests all current open orders
+ ///
+ ///
+ private List GetOrders() => IsMtConnected ? ApiClient.GetOrders(OrderSelectSource.MODE_TRADES) : null;
+ protected override void OnTriggerRaised()
+ {
+ if(_lastOrders == null)
+ {
+ _lastOrders = GetOrders();
+ return;
+ }
+ List currentOrders = GetOrders();
+ OrderModifiedTypes omt = OrderModifiedTypes;
+ var mtModifiedOrders = currentOrders
+ .Select(co => new MtModifiedOrder(_lastOrders.FirstOrDefault(x => x.Ticket == co.Ticket), co))
+ .ToList();
+ List modifiedOrders = new List();
+ modifiedOrders.AddRange(GetMtModifiedOrdersWithModType(mtModifiedOrders, omt, OrderModifiedTypes.TakeProfit)); //If the takeprofit were changed between both calls
+ modifiedOrders.AddRange(GetMtModifiedOrdersWithModType(mtModifiedOrders, omt, OrderModifiedTypes.StopLoss)); //If the stoploss were changed between both calls
+ modifiedOrders.AddRange(GetMtModifiedOrdersWithModType(mtModifiedOrders, omt, OrderModifiedTypes.Operation)); //If an order changed from limit / stop order to an open order
+ if (modifiedOrders.Count > 0)
+ OrdersModified?.Invoke(this, new ModifiedOrdersEventArgs(modifiedOrders));
+ _lastOrders = currentOrders;
+ }
+ private static IEnumerable GetMtModifiedOrdersWithModType(IEnumerable orders, OrderModifiedTypes globalSearchFlag, OrderModifiedTypes modifiedType)
+ => globalSearchFlag.HasFlag(modifiedType) ? orders.Where(o => o.ModifyType.HasFlag(modifiedType)) : new List();
+ }
+}
\ No newline at end of file
diff --git a/MtApi/Monitors/OrderModification/MtModifiedOrder.cs b/MtApi/Monitors/OrderModification/MtModifiedOrder.cs
new file mode 100644
index 00000000..7bb82d0c
--- /dev/null
+++ b/MtApi/Monitors/OrderModification/MtModifiedOrder.cs
@@ -0,0 +1,50 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MtApi.Monitors
+{
+ public class MtModifiedOrder
+ {
+ ///
+ /// The order in its old state (before the changes)
+ ///
+ public MtOrder OldOrder { get; }
+ ///
+ /// The order in its new state (after the changes)
+ ///
+ public MtOrder NewOrder { get; }
+ ///
+ /// The changes found by this instance
+ ///
+ public OrderModifiedTypes ModifyType { get; private set; }
+ ///
+ /// Initializes an instance and compare the order in its old and new state
+ ///
+ /// The order in its old state (before the changes)
+ /// The order in its new state (after the changes)
+ public MtModifiedOrder(MtOrder oldOrder, MtOrder newOrder)
+ {
+ if (oldOrder != null && newOrder != null && oldOrder.Ticket != newOrder.Ticket)
+ throw new ArgumentException(nameof(oldOrder) + " and " + nameof(newOrder) + " need to have the same ticket id");
+ OldOrder = oldOrder;
+ NewOrder = newOrder;
+ ModifyType = OrderModifiedTypes.None;
+ Compare();
+ }
+ private void Compare()
+ {
+ if(NewOrder != null && OldOrder != null)
+ {
+ if (OldOrder.StopLoss != NewOrder.StopLoss)
+ ModifyType |= OrderModifiedTypes.StopLoss;
+ if (OldOrder.TakeProfit != NewOrder.TakeProfit)
+ ModifyType |= OrderModifiedTypes.TakeProfit;
+ if (OldOrder.Operation != NewOrder.Operation)
+ ModifyType |= OrderModifiedTypes.Operation;
+ }
+ }
+ }
+}
diff --git a/MtApi/Monitors/OrderModification/OrderModifiedTypes.cs b/MtApi/Monitors/OrderModification/OrderModifiedTypes.cs
new file mode 100644
index 00000000..74c351b8
--- /dev/null
+++ b/MtApi/Monitors/OrderModification/OrderModifiedTypes.cs
@@ -0,0 +1,18 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace MtApi.Monitors
+{
+ [Flags]
+ public enum OrderModifiedTypes
+ {
+ None = 0x0,
+ TakeProfit = 1 << 0,
+ StopLoss = 1 << 1,
+ Operation = 1 << 2,
+ All = 7
+ }
+}
diff --git a/MtApi/Monitors/TimeframeTradeMonitor.cs b/MtApi/Monitors/TimeframeTradeMonitor.cs
index 41663663..8d08a0b2 100644
--- a/MtApi/Monitors/TimeframeTradeMonitor.cs
+++ b/MtApi/Monitors/TimeframeTradeMonitor.cs
@@ -1,56 +1,18 @@
-
+using MtApi.Monitors.Triggers;
+
namespace MtApi.Monitors
{
public class TimeframeTradeMonitor : TradeMonitor
{
- #region Fields
- private volatile bool _isStarted;
- #endregion
-
- #region ctor
- public TimeframeTradeMonitor(MtApiClient apiClient)
- : base(apiClient)
+ ///
+ /// Constructor for initializing a new instance with a trigger instance of .
+ /// SyncTrigger is set to true by default
+ ///
+ /// The which will be used to communicate with MetaTrader.
+ public TimeframeTradeMonitor(MtApiClient apiClient)
+ : base(apiClient, new NewBarTrigger(apiClient))
{
- apiClient.OnLastTimeBar += ApiClient_OnLastTimeBar;
+ SyncTrigger = true; //Sync-Trigger set to true, to have the same behavior as before
}
- #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 => _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
}
-}
+}
\ No newline at end of file
diff --git a/MtApi/Monitors/TimerTradeMonitor.cs b/MtApi/Monitors/TimerTradeMonitor.cs
index c8b430f2..769ada89 100644
--- a/MtApi/Monitors/TimerTradeMonitor.cs
+++ b/MtApi/Monitors/TimerTradeMonitor.cs
@@ -1,90 +1,47 @@
-using System.Timers;
+using System;
+using MtApi.Monitors.Triggers;
namespace MtApi.Monitors
{
public class TimerTradeMonitor : TradeMonitor
{
#region Fields
- private readonly Timer _timer = new Timer();
+ private readonly TimeElapsedTrigger _timeElapsedTrigger;
#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.
+ #region Properties
+ ///
+ /// Interval for raising the trigger
+ ///
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; }
+ get => _timeElapsedTrigger.Interval.TotalMilliseconds;
+ set => _timeElapsedTrigger.Interval = TimeSpan.FromMilliseconds(value);
}
#endregion
- #region Protected Methods
- protected override void OnStart()
+ #region ctors
+ ///
+ /// Constructor for initializing a new instance with a default of 10 seconds.
+ /// SyncTrigger is set to true by default
+ ///
+ /// The which will be used to communicate with MetaTrader.
+ public TimerTradeMonitor(MtApiClient apiClient)
+ : this(apiClient, new TimeElapsedTrigger(TimeSpan.FromSeconds(10)))
{
- if (IsMtConnected)
- {
- _timer.Start();
- }
+ SyncTrigger = true; //Sync-Trigger set to true, to have the same behavior as before
}
-
- protected override void OnStop()
+ ///
+ /// Constructor for initializing a new instance with a custom instance of .
+ /// SyncTrigger is set to false by default
+ ///
+ /// The which will be used to communicate with MetaTrader.
+ /// The custom instance of which will be used to trigger this instance of .
+ public TimerTradeMonitor(MtApiClient apiClient, TimeElapsedTrigger timeElapsedTrigger)
+ : base(apiClient, timeElapsedTrigger)
{
- _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
+ _timeElapsedTrigger = timeElapsedTrigger;
}
#endregion
}
-}
+}
\ No newline at end of file
diff --git a/MtApi/Monitors/TradeMonitor.cs b/MtApi/Monitors/TradeMonitor.cs
index fc746339..a80361a0 100644
--- a/MtApi/Monitors/TradeMonitor.cs
+++ b/MtApi/Monitors/TradeMonitor.cs
@@ -1,63 +1,18 @@
using System;
using System.Collections.Generic;
+using System.Linq;
using System.Threading.Tasks;
+using MtApi.Monitors.Triggers;
namespace MtApi.Monitors
{
- public abstract class TradeMonitor
+ public class TradeMonitor : MtMonitorBase
{
#region Fields
- private readonly MtApiClient _apiClient;
private List _prevOrders;
private readonly object _locker = new object();
#endregion
- #region ctor
- protected 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:
@@ -65,17 +20,28 @@ namespace MtApi.Monitors
public event EventHandler AvailabilityOrdersChanged;
#endregion
- #region Protected Methods
+ #region ctor
+ ///
+ /// Constructor for initializing an instance of .
+ ///
+ /// The which will be used to communicate with MetaTrader.
+ /// The custom instance of which will be used to trigger this instance of .
+ public TradeMonitor(MtApiClient apiClient, IMonitorTrigger monitorTrigger) : base(apiClient, monitorTrigger) { }
+ #endregion
- protected abstract void OnStart();
- protected abstract void OnStop();
-
- protected abstract void OnMtConnected();
- protected abstract void OnMtDisconnected();
-
- public bool IsMtConnected => _apiClient.ConnectionState == MtConnectionState.Connected;
-
- protected void Check()
+ protected override void OnMtConnected()
+ {
+ InitialCheck();
+ base.OnMtConnected();
+ }
+ protected override void OnStart()
+ {
+ if (IsMtConnected)
+ InitialCheck();
+ base.OnStart();
+ }
+ protected override void OnTriggerRaised() => Check();
+ private void Check()
{
try
{
@@ -90,92 +56,48 @@ namespace MtApi.Monitors
//TODO: write error to log
}
}
- #endregion
+ private void InitialCheck()
+ {
+ lock (_locker)
+ _prevOrders = null;
- #region Private Methods
+ Task.Factory.StartNew(Check);
+ }
private void CheckOrders()
{
var openedOrders = new List();
var closedOrders = new List();
+ List prevOrders;
// get current orders from MetaTrader
- var tradesOrders = _apiClient.GetOrders(OrderSelectSource.MODE_TRADES);
+ var tradesOrders = ApiClient.GetOrders(OrderSelectSource.MODE_TRADES);
- List prevOrders;
- lock(_locker)
- {
+ 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);
- }
- }
+ openedOrders = tradesOrders.Where(to => prevOrders.Find(a => a.Ticket == to.Ticket) == null).ToList();
//check closed orders
- var closeOrdersTemp = new List();
- foreach (var order in prevOrders)
- {
- if (tradesOrders.Find(a => a.Ticket == order.Ticket) == null)
- {
- closeOrdersTemp.Add(order);
- }
- }
+ var closeOrdersTemp = prevOrders.Where(po => tradesOrders.Find(a => a.Ticket == po.Ticket) == null).ToList();
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);
- }
- }
+ var historyOrders = ApiClient.GetOrders(OrderSelectSource.MODE_HISTORY);
+ closedOrders = closeOrdersTemp.Where(cot => historyOrders.Find(a => a.Ticket == cot.Ticket) != null).ToList();
}
}
- lock(_locker)
- {
+ 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
}
-}
\ No newline at end of file
+}
diff --git a/MtApi/Monitors/Triggers/IMonitorTrigger.cs b/MtApi/Monitors/Triggers/IMonitorTrigger.cs
new file mode 100644
index 00000000..54e268d8
--- /dev/null
+++ b/MtApi/Monitors/Triggers/IMonitorTrigger.cs
@@ -0,0 +1,27 @@
+using System;
+
+namespace MtApi.Monitors.Triggers
+{
+ ///
+ /// Interface for triggers which can be used to trigger a .
+ ///
+ public interface IMonitorTrigger
+ {
+ ///
+ /// Event will be called if the trigger raised.
+ ///
+ event EventHandler Raised;
+ ///
+ /// Returns whether the trigger is started
+ ///
+ bool IsStarted { get; }
+ ///
+ /// Stops the trigger and prevents further calls of the event.
+ ///
+ void Stop();
+ ///
+ /// Starts the trigger.
+ ///
+ void Start();
+ }
+}
diff --git a/MtApi/Monitors/Triggers/NewBarTrigger.cs b/MtApi/Monitors/Triggers/NewBarTrigger.cs
new file mode 100644
index 00000000..4c39109b
--- /dev/null
+++ b/MtApi/Monitors/Triggers/NewBarTrigger.cs
@@ -0,0 +1,68 @@
+using System;
+
+namespace MtApi.Monitors.Triggers
+{
+ ///
+ /// Raises the event if a bar is closed and a new one started.
+ ///
+ public class NewBarTrigger : IMonitorTrigger
+ {
+ #region Fields
+ private volatile bool _isStarted;
+ private readonly MtApiClient _apiClient;
+ #endregion
+
+ #region Properties
+ ///
+ /// Returns true if the trigger is started, otherwise false
+ ///
+ public bool IsStarted => _isStarted;
+ #endregion
+
+ #region Events
+ ///
+ /// Event will be called if the trigger raised.
+ ///
+ public event EventHandler Raised;
+ #endregion
+
+ #region ctor
+ public NewBarTrigger(MtApiClient apiClient)
+ {
+ _apiClient = apiClient;
+ _apiClient.OnLastTimeBar += _apiClient_OnLastTimeBar;
+ }
+ #endregion
+
+ #region Public methods
+ ///
+ /// Starts the trigger
+ ///
+ public void Start() => SetIsStarted(true);
+ ///
+ /// Stops the trigger
+ ///
+ public void Stop() => SetIsStarted(false);
+ #endregion
+
+ #region Private methods
+ private void _apiClient_OnLastTimeBar(object sender, TimeBarArgs e)
+ {
+ if (_isStarted)
+ Raised?.Invoke(this, EventArgs.Empty);
+ }
+
+ private void SetIsStarted(bool value)
+ {
+ if (value != _isStarted)
+ {
+ _isStarted = value;
+ if (value)
+ _apiClient.OnLastTimeBar += _apiClient_OnLastTimeBar;
+ else
+ _apiClient.OnLastTimeBar -= _apiClient_OnLastTimeBar;
+ }
+ }
+ #endregion
+ }
+}
diff --git a/MtApi/Monitors/Triggers/TimeElapsedTrigger.cs b/MtApi/Monitors/Triggers/TimeElapsedTrigger.cs
new file mode 100644
index 00000000..65d0b59d
--- /dev/null
+++ b/MtApi/Monitors/Triggers/TimeElapsedTrigger.cs
@@ -0,0 +1,74 @@
+using System;
+using System.Timers;
+
+namespace MtApi.Monitors.Triggers
+{
+ public class TimeElapsedTrigger : IMonitorTrigger
+ {
+ #region Fields
+ readonly Timer _timer;
+ #endregion
+
+ #region Properties
+ ///
+ /// Interval for raising the trigger
+ ///
+ public TimeSpan Interval
+ {
+ get => TimeSpan.FromMilliseconds(_timer.Interval);
+ set => _timer.Interval = value.TotalMilliseconds;
+ }
+
+ ///
+ /// Returns true if the trigger is started, otherwise false
+ ///
+ public bool IsStarted => _timer.Enabled;
+
+ ///
+ /// If true, the trigger will raise continuosly after elapsed , otherwise the trigger will raise only once after elapsed .
+ ///
+ public bool AutoReset { get => _timer.AutoReset; set => _timer.AutoReset = value; }
+ #endregion
+
+ #region Events
+ ///
+ /// Returns true if the trigger is started, otherwise false
+ ///
+ public event EventHandler Raised;
+ #endregion
+
+ #region ctor
+ ///
+ /// Constructor for initializing TimeElapsedTrigger
+ ///
+ /// Defines the interval for raising the event.
+ /// If true, the trigger will raise continuosly after elapsed , otherwise the trigger will raise only once after elapsed .
+ public TimeElapsedTrigger(TimeSpan time, bool autoReset = true)
+ {
+ _timer = new Timer(time.TotalMilliseconds);
+ _timer.Elapsed += _timer_Elapsed;
+ AutoReset = autoReset;
+ }
+ #endregion
+
+ #region Public methods
+ ///
+ /// Starts the trigger
+ ///
+ public void Start() => _timer.Start();
+ ///
+ /// Stops the trigger
+ ///
+ public void Stop() => _timer.Stop();
+ #endregion
+
+ #region private methods
+ private void _timer_Elapsed(object sender, ElapsedEventArgs e)
+ {
+ _timer.Elapsed -= _timer_Elapsed;
+ Raised?.Invoke(this, EventArgs.Empty);
+ _timer.Elapsed += _timer_Elapsed;
+ }
+ #endregion
+ }
+}
\ No newline at end of file
diff --git a/MtApi/Monitors/readme.md b/MtApi/Monitors/readme.md
new file mode 100644
index 00000000..d50dae28
--- /dev/null
+++ b/MtApi/Monitors/readme.md
@@ -0,0 +1,66 @@
+## Monitors
+
+Monitors can be used to monitor different changes on Mt4 structs.
+
+## MtMonitorBase
+This is the base class for monitoring extensions. If inherited `MtMonitorBase` needs an instance of `MtApiClient` and `IMonitorTrigger`.
+
+`SyncTrigger` (which can be set in the constructor or as Property) can be used to define whether the trigger should be started and stopped as well if `Start()` or `Stop()` will be called on an instance of a child class of `MtMonitorBase`.
+Keep in mind: If an `IMonitorTrigger` will be used for several monitors, `SyncTrigger` set to `true` would cause that all monitors related to this trigger would stop:
+
+```
+var fooTrigger = new FooTrigger();
+var fooMonitor = new FooMonitor(apiClient, fooTrigger, true);
+var barMonitor = new FooMonitor(apiClient, fooTrigger, false);
+
+fooTrigger.Start();
+barMonitor.Start();
+fooMonitor.Start();
+
+fooMonitor.Stop(); //Because of SyncTrigger = true in the constructor of FooMonitor, fooTrigger.Stop() were triggered as well. Therefore barMonitor will not get any further triggers.
+```
+
+## IMonitorTrigger
+An `IMonitorTrigger` is for defining when the monitor should check whether his conditions are met for invoking his event.
+
+There are already two `IMonitorTrigger`s which can be used:
+### NewBarTrigger
+Triggers when a new bar starts
+### TimeElapsedTrigger
+Triggers when a defined time elapsed
+
+## Default monitors:
+There are already two different monitors defined. You can extend them or define new ones by inheriting from `MtMonitorBase`.
+
+### TradeMonitor
+Can be used to get updates on new opened trades or closed trades.
+
+## ModifiedOrdersMonitor
+Can be used to get updates on modified trades (takeprofit, stoploss, operation).
+
+`OrderModifiedTypes` defines which modifications should be monitored:
+
+1. `None` would cause no monitoring. But please use `Start()` and `Stop()` instead.
+2. `TakeProfit` would cause observing whether the TakeProfit were changed.
+3. `StopLoss` would cause observing whether the StopLoss were changed.
+4. `Operation` would cause observing trades which changed from a stop / limit order to an open order.
+5. `All` would cause observing all above defined.
+
+Because `OrderModifiedTypes` is defined with the Flag-Attribute, you can combine the above monitoring types with a pipe: `OrderModifiedTypes.TakeProfit | OrderModifiedTypes.StopLoss`.
+
+### Example
+```
+var orderModifyMonitor = new ModifiedOrdersMonitor(
+ _apiClient,
+ new MtApi.Monitors.Triggers.TimeElapsedTrigger(TimeSpan.FromSeconds(1)),
+ OrderModifiedTypes.All,
+ true
+);
+orderModifyMonitor.OrdersModified += OrderModifyMonitor_OrdersModified;
+orderModifyMonitor.Start();
+
+private void OrderModifyMonitor_OrdersModified(object sender, ModifiedOrdersEventArgs e)
+{
+ //Receives the event
+}
+```
diff --git a/MtApi/MtApi.csproj b/MtApi/MtApi.csproj
index 3a7b9699..92bc2804 100755
--- a/MtApi/MtApi.csproj
+++ b/MtApi/MtApi.csproj
@@ -76,6 +76,14 @@
+
+
+
+
+
+
+
+
diff --git a/TestClients/TestApiClientUI/Form1.cs b/TestClients/TestApiClientUI/Form1.cs
index dcf7cef9..ccb12183 100644
--- a/TestClients/TestApiClientUI/Form1.cs
+++ b/TestClients/TestApiClientUI/Form1.cs
@@ -212,8 +212,8 @@ namespace TestApiClientUI
foreach (var quote in quotes)
{
AddNewQuote(quote);
- }
- }
+ }
+ }
}
private void OnDisconnected()
@@ -456,7 +456,7 @@ namespace TestApiClientUI
ticket = (int)listBoxSendedOrders.SelectedItems[0];
else if (listBoxClosedOrders.SelectedItems.Count > 0)
ticket = (int)listBoxClosedOrders.SelectedItems[0];
-
+
if (ticket >= 0)
{
var result = _apiClient.OrderSelect(ticket, OrderSelectMode.SELECT_BY_POS);