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);