//Strategy.cs //Copyright (c) 2013 StockSharp LLC, all rights reserved. //This code module is part of StockSharp library. //This code is licensed under the GNU GENERAL PUBLIC LICENSE Version 3. //See the file License.txt for the license details. //More info on: http://stocksharp.com using System.Linq.Expressions; namespace StockSharp.Algo.Strategies { using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using Ecng.Common; using Ecng.Collections; using Ecng.ComponentModel; using Ecng.Serialization; using StockSharp.Logging; using StockSharp.Algo.Commissions; using StockSharp.Algo.Latency; using StockSharp.Algo.PnL; using StockSharp.Algo.Positions; using StockSharp.Algo.Slippage; using StockSharp.Algo.Statistics; using StockSharp.BusinessEntities; using Xceed.Wpf.Toolkit.PropertyGrid.Attributes; /// /// Базовый класс для всех торговых стратегий. /// public class Strategy : BaseLogReceiver, INotifyPropertyChangedEx, IPersistable, IMarketRuleContainer, ICloneable { sealed class ChildStrategyList : SynchronizedSet, IStrategyChildStrategyList { private readonly SynchronizedDictionary _childStrategyRules = new SynchronizedDictionary(); private readonly Strategy _parent; public ChildStrategyList(Strategy parent) : base(true) { if (parent == null) throw new ArgumentNullException("parent"); _parent = parent; } protected override void OnAdded(Strategy item) { //pyh: Нельзя использовать OnAdding тк логирование включается по событию Added которое вызовет base.OnAdded base.OnAdded(item); if (item.Parent != null) throw new ArgumentException("Стратегия уже добавлена в качестве дочерней."); item.Parent = _parent; if (item.Trader == null) item.Trader = _parent.Trader; if (item.Portfolio == null) item.Portfolio = _parent.Portfolio; if (item.Security == null) item.Security = _parent.Security; if (!_parent.OnlyLogEvent) { item.OrderRegistering += _parent.OnOrderRegistering; item.OrderRegistered += _parent.AddOrder; item.ReRegistering += _parent.ReRegisterSlippage; item.OrderChanged += _parent.OnChildOrderChanged; item.OrderRegisterFailed += _parent.OnChildOrderRegisterFailed; item.OrderCancelFailed += _parent.OnChildOrderCancelFailed; item.StopOrderRegistering += _parent.OnStopOrderRegistering; item.StopOrderRegistered += _parent.AddOrder; item.StopOrderChanged += _parent.OnChildStopOrderChanged; item.StopOrderRegisterFailed += _parent.OnChildStopOrderRegisterFailed; item.StopOrderCancelFailed += _parent.OnChildStopOrderCancelFailed; item.NewMyTrades += _parent.AddMyTrades; item.OrderReRegistering += _parent.OnOrderReRegistering; item.StopOrderReRegistering += _parent.OnStopOrderReRegistering; } item.ProcessStateChanged += OnChildProcessStateChanged; item.StopOrders.ForEach(_parent.AddOrder); item.Orders.ForEach(_parent.AddOrder); if (!item.MyTrades.IsEmpty()) _parent.AddMyTrades(item.MyTrades); _parent._orderFails.AddRange(item.OrderFails); if (item.ProcessState == _parent.ProcessState && _parent.ProcessState == ProcessStates.Started) OnChildProcessStateChanged(item); else item.ProcessState = _parent.ProcessState; } private void OnChildProcessStateChanged(Strategy child) { if (child.ProcessState == ProcessStates.Started) { // для предотвращения остановки родительской стратегии пока работают ее дочерние var rule = child .WhenStopped() .Do(() => _childStrategyRules.Remove(child)) .Once() .Apply(_parent); rule.UpdateName(rule.Name + " (ChildStrategyList.OnChildProcessStateChanged)"); _childStrategyRules.Add(child, rule); } } protected override bool OnClearing() { foreach (var item in ToArray()) Remove(item); return true; } protected override bool OnRemoving(Strategy item) { item.Parent = null; if (!_parent.OnlyLogEvent) { item.OrderRegistering -= _parent.OnOrderRegistering; item.OrderRegistered -= _parent.AddOrder; item.ReRegistering -= _parent.ReRegisterSlippage; item.OrderChanged -= _parent.OnChildOrderChanged; item.OrderRegisterFailed -= _parent.OnChildOrderRegisterFailed; item.OrderCancelFailed -= _parent.OnChildOrderCancelFailed; item.OrderCanceling -= _parent.OnOrderCanceling; item.StopOrderRegistering -= _parent.OnStopOrderRegistering; item.StopOrderRegistered -= _parent.AddOrder; item.StopOrderChanged -= _parent.OnChildStopOrderChanged; item.StopOrderRegisterFailed -= _parent.OnChildStopOrderRegisterFailed; item.StopOrderCancelFailed -= _parent.OnChildStopOrderCancelFailed; item.StopOrderCanceling -= _parent.OnStopOrderCanceling; item.NewMyTrades -= _parent.AddMyTrades; item.OrderReRegistering -= _parent.OnOrderReRegistering; item.StopOrderReRegistering -= _parent.OnStopOrderReRegistering; } item.ProcessStateChanged -= OnChildProcessStateChanged; _childStrategyRules.SyncDo(d => { var rule = d.TryGetValue(item); if (rule == null) return; // правило могло быть удалено при остановке дочерней стратегии, но перед ее удалением из коллекции у родителя if (rule.IsReady) _parent.TryRemoveRule(rule); d.Remove(item); }); return base.OnRemoving(item); } public void TryRemoveStoppedRule(IMarketRule rule) { var child = rule.Token as Strategy; if (child != null) _childStrategyRules.Remove(child); } } private sealed class StrategyRuleList : MarketRuleList { private readonly Strategy _strategy; public StrategyRuleList(Strategy strategy) : base(strategy) { if (strategy == null) throw new ArgumentNullException("strategy"); _strategy = strategy; } protected override bool OnAdding(IMarketRule item) { return _strategy.ProcessState != ProcessStates.Stopping && base.OnAdding(item); } } // mika // причина создания своего RW (вместо ReaderWriterLockSlim) // у нас Read может повлечь за собой Write (Strategy.Dispose) // но Upgrade возможен только для одного потока (а таких потоков в Strategy может быть много) // как итог, TryInvoke для нормальных событий (OnNewOrders например) возвращал false хотя никакого Dispose не было вызвано // mika обновление от 29.09.2011 // если из Read вызвать Write, то все виснет (в случае стратегий распространенное явление, когда Dispose происходит по какому по какому-то событию) // поэтому сделал Write асинхронным private sealed class SimpleReadWriteAsyncLock { private int _readingCount; private readonly object _writeLock = new object(); private readonly List _pendingWrites = new List(); public void Read(Action action) { if (action == null) throw new ArgumentNullException("action"); lock (_writeLock) { _readingCount++; } try { action(); } finally { lock (_writeLock) { _readingCount--; //Monitor.PulseAll(_writeLock); if (_readingCount == 0) { foreach (var write in _pendingWrites) write(); _pendingWrites.Clear(); } } } } public void WriteAsync(Action action) { if (action == null) throw new ArgumentNullException("action"); lock (_writeLock) { if (_readingCount == 0) action(); else _pendingWrites.Add(action); } } } private sealed class OrderInfo { public bool IsOwn { get; set; } public bool IsCanceled { get; set; } public bool IsHandled { get; set; } public decimal ReceivedVolume { get; set; } } private readonly CachedSynchronizedSet _pendingOrders = new CachedSynchronizedSet(); private readonly SynchronizedSet _pendingStopOrders = new SynchronizedSet(); private readonly SynchronizedDictionary _ordersInfo = new SynchronizedDictionary(); private readonly StrategyNameGenerator _nameGenerator; private readonly SimpleReadWriteAsyncLock _disposeLock = new SimpleReadWriteAsyncLock(); private DateTime _firstOrderTime; private DateTime _lastOrderTime; private TimeSpan _maxOrdersKeepTime; private const string _category = "Общие"; /// /// Создать . /// public Strategy() { _childStrategies = new ChildStrategyList(this); SetChilds(ChildStrategies); Rules = new StrategyRuleList(this); _nameGenerator = new StrategyNameGenerator(this); _nameGenerator.Changed += name => _name.Value = name; UnrealizedPnLInterval = TimeSpan.FromMinutes(1); _id = this.Param("Id", base.Id); _volume = this.Param("Volume", 1); _name = this.Param("Name", new string(GetType().Name.Where(char.IsUpper).ToArray())); _maxErrorCount = this.Param("MaxErrorCount", 1); _disposeOnStop = this.Param("DisposeOnStop", false); _cancelOrdersWhenStopping = this.Param("CancelOrdersWhenStopping", true); _waitAllTrades = this.Param("WaitAllTrades"); _commentOrders = this.Param("CommentOrders"); _ordersKeepTime = this.Param("OrdersKeepTime", TimeSpan.FromDays(1)); _logLevel = this.Param("LogLevel", LogLevels.Inherit); InitMaxOrdersKeepTime(); MemoryStatistics.Instance.Strategies.Add(this); } /// /// Подписаться только на Log события. /// /// /// Данный флаг необходим для StrategyMonitorWindow. /// public bool OnlyLogEvent { private get; set; } private readonly StrategyParam _id; /// /// Уникальный идентификатор источника. /// public override Guid Id { get { return _id.Value; } set { if (value == Id) return; _id.Value = value; } } private readonly StrategyParam _logLevel; /// /// Уровень логирования. По-умолчанию установлено в . /// [Category("Логирование")] //[PropertyOrder(8)] [DisplayName("Уровень логирования")] [Description("Уровень логирования стратегии.")] public override LogLevels LogLevel { get { return _logLevel.Value; } set { _logLevel.Value = value; } } private readonly StrategyParam _name; /// /// Название стратегии. /// [Category(_category)] [PropertyOrder(0)] [DisplayName("Название")] [Description("Название стратегии.")] public override string Name { get { return _name.Value; } set { if (value == Name) return; _nameGenerator.Value = value; _name.Value = value; } } /// /// Генератор имени стратегии. /// [Browsable(false)] public StrategyNameGenerator NameGenerator { get { return _nameGenerator; } } private ITrader _trader; /// /// Шлюз к торговой системе. /// [Browsable(false)] public virtual ITrader Trader { get { return _trader; } set { if (Trader == value) return; if (_trader != null) { _trader.NewOrders -= OnTraderNewOrders; _trader.OrdersChanged -= OnTraderOrdersChanged; _trader.OrdersRegisterFailed -= OnTraderOrdersRegisterFailed; _trader.OrdersCancelFailed -= OnTraderOrdersCancelFailed; _trader.NewStopOrders -= OnTraderNewStopOrders; _trader.StopOrdersChanged -= OnTraderStopOrdersChanged; _trader.StopOrdersRegisterFailed -= OnTraderStopOrdersRegisterFailed; _trader.StopOrdersCancelFailed -= OnTraderStopOrdersCancelFailed; _trader.NewMyTrades -= OnTraderNewMyTrades; _trader.SecuritiesChanged -= OnTraderSecuritiesChanged; _trader.PortfoliosChanged -= OnTraderPortfoliosChanged; _trader.PositionsChanged -= OnTraderPositionsChanged; } _trader = value; if (_trader != null && !OnlyLogEvent) { _trader.NewOrders += OnTraderNewOrders; _trader.OrdersChanged += OnTraderOrdersChanged; _trader.OrdersRegisterFailed += OnTraderOrdersRegisterFailed; _trader.OrdersCancelFailed += OnTraderOrdersCancelFailed; _trader.NewStopOrders += OnTraderNewStopOrders; _trader.StopOrdersChanged += OnTraderStopOrdersChanged; _trader.StopOrdersRegisterFailed += OnTraderStopOrdersRegisterFailed; _trader.StopOrdersCancelFailed += OnTraderStopOrdersCancelFailed; _trader.NewMyTrades += OnTraderNewMyTrades; _trader.SecuritiesChanged += OnTraderSecuritiesChanged; _trader.PortfoliosChanged += OnTraderPortfoliosChanged; _trader.PositionsChanged += OnTraderPositionsChanged; } ChildStrategies.SyncDo(c => { foreach (var strategy in c) { if (strategy.Trader == null || value == null) strategy.Trader = value; } }); TraderChanged.SafeInvoke(); } } private Portfolio _portfolio; /// /// Портфель. /// [Category(_category)] [PropertyOrder(1)] [DisplayName("Портфель")] [Description("Торговый портфель, через который будут совершаться операции с заявками.")] public virtual Portfolio Portfolio { get { return _portfolio; } set { if (_portfolio == value) return; _portfolio = value; ChildStrategies.SyncDo(c => { foreach (var strategy in c) { if (strategy.Portfolio == null) strategy.Portfolio = value; } }); RaiseParametersChanged("Portfolio"); } } private Security _security; /// /// Инструмент. /// [Category(_category)] [PropertyOrder(2)] [DisplayName("Инструмент")] [Description("Торговый инструмент, с которым работает стратегия.")] public virtual Security Security { get { return _security; } set { if (_security == value) return; _security = value; ChildStrategies.SyncDo(c => { foreach (var strategy in c) { if (strategy.Security == null) strategy.Security = value; } }); //pyh: валится в BasketTrader.GetMarketTime(Exchange.RTS) в Студии //OnSecurityChanged(); RaiseParametersChanged("Security"); } } private ISlippageManager _slippageManager = new SlippageManager(); /// /// Менеджер проскальзывания. Учитывает сделки данной стратегии, а так же ее дочерних стратегий . /// [Browsable(false)] public ISlippageManager SlippageManager { get { return _slippageManager; } set { if (value == null) throw new ArgumentNullException("value"); _slippageManager = value; } } /// /// Суммарное значение проскальзывания. /// [Category(_category)] [PropertyOrder(4)] [DisplayName("Проскальзывание")] [Description("Суммарное значение проскальзывания.")] [ReadOnly(true)] public decimal Slippage { get { return SlippageManager.Slippage; } } /// /// Событие изменения . /// public event Action SlippageChanged; private IPnLManager _pnLManager = new PnLManager(); /// /// Менеджер прибыли-убытка. Учитывает сделки данной стратегии, а так же ее дочерних стратегий . /// [Browsable(false)] public IPnLManager PnLManager { get { return _pnLManager; } set { if (value == null) throw new ArgumentNullException("value"); _pnLManager = value; } } /// /// Суммарное значение прибыли-убытка без учета комиссии . /// [Category(_category)] [PropertyOrder(5)] [DisplayName("P&L")] [Description("Суммарное значение прибыли-убытка.")] [ReadOnly(true)] public decimal PnL { get { return PnLManager.PnL; } } /// /// Событие изменения . /// public event Action PnLChanged; private ICommissionManager _commissionManager = new CommissionManager(); /// /// Менеджер комиссии. /// [Browsable(false)] public ICommissionManager CommissionManager { get { return _commissionManager; } set { if (value == null) throw new ArgumentNullException("value"); _commissionManager = value; } } /// /// Суммарное значение комиссии. /// [Category(_category)] [PropertyOrder(6)] [DisplayName("Комиссия")] [Description("Суммарное значение комиссии.")] [ReadOnly(true)] public decimal Commission { get { return CommissionManager.Commission; } } /// /// Событие изменения . /// public event Action CommissionChanged; private IPositionManager _positionManager = new PositionManager(true); /// /// Менеджер позиции. Учитывает сделки данной стратегии, а так же ее дочерних стратегий . /// [Category(_category)] [PropertyOrder(7)] [DisplayName("Позиция")] [Description("Позиция стратегии по инструментам и портфелям.")] public IPositionManager PositionManager { get { return _positionManager; } set { if (value == null) throw new ArgumentNullException("value"); _positionManager = value; } } /// /// Суммарное значение позиции. /// [Browsable(false)] public decimal Position { get { return PositionManager.Position; } set { if (Position == value) return; PositionManager.Position = value; RaisePositionChanged(); } } /// /// Событие изменения . /// public event Action PositionChanged; private ILatencyManager _latencyManager = new LatencyManager(); /// /// Менеджер задержки. Учитывает заявки данной стратегии, а так же ее дочерних стратегий . /// [Browsable(false)] public ILatencyManager LatencyManager { get { return _latencyManager; } set { if (value == null) throw new ArgumentNullException("value"); _latencyManager = value; } } /// /// Суммарное значение задержки. /// [Category(_category)] [PropertyOrder(8)] [DisplayName("Задержка")] [Description("Суммарное значение задержки.")] [ReadOnly(true)] public TimeSpan Latency { get { return LatencyManager.LatencyRegistration + LatencyManager.LatencyCancellation; } } /// /// Событие изменения . /// public event Action LatencyChanged; private StatisticManager _statisticManager = new StatisticManager(); /// /// Менеджер статистики. /// [Browsable(false)] public virtual StatisticManager StatisticManager { get { return _statisticManager; } protected set { if (value == null) throw new ArgumentNullException("value"); _statisticManager = value; } } private readonly SynchronizedSet _parameters = new SynchronizedSet(); /// /// Параметры стратегии. /// [Browsable(false)] public virtual ISynchronizedCollection Parameters { get { return _parameters; } } /// /// Событие изменения . /// public event Action ParametersChanged; /// /// Вызвать события и . /// /// Название параметра. protected internal void RaiseParametersChanged(string name) { ParametersChanged.SafeInvoke(); this.Notify(name); } private readonly SettingsStorage _environment = new SettingsStorage(); /// /// Параметры окружения стратегии. /// [Browsable(false)] public virtual SettingsStorage Environment { get { return _environment; } } private readonly StrategyParam _maxErrorCount; /// /// Максимальное количество ошибок, которое должна получить стратегия прежде, чем она остановил работу. /// /// Значение по умолчанию равно 1. [Browsable(false)] public int MaxErrorCount { get { return _maxErrorCount.Value; } set { if (value < 1) throw new ArgumentOutOfRangeException("value", value, "Максимальное количество ошибок не может быть меньше одного."); _maxErrorCount.Value = value; } } private int _errorCount; /// /// Текущее количество ошибок. /// [Browsable(false)] public int ErrorCount { get { return _errorCount; } private set { if (_errorCount == value) return; _errorCount = value; this.Notify("ErrorCount"); } } private ProcessStates _processState; /// /// Состояние работы. /// [Browsable(false)] public ProcessStates ProcessState { get { return _processState; } private set { if (_processState == value) return; this.AddDebugLog("Переход из состояния {0} в {1}.", _processState, value); if (_processState == ProcessStates.Stopped && value == ProcessStates.Stopping) throw new InvalidOperationException("Стратегия {0} уже остановлена, и не может быть переведена в состояние {1}.".Put(Name, value)); _processState = value; ChildStrategies.SyncDo(c => { var child = (IEnumerable)c; if (ProcessState == ProcessStates.Stopping) child = child.Where(s => s.ProcessState == ProcessStates.Started); child.ToArray().ForEach(s => s.ProcessState = ProcessState); }); try { switch (value) { case ProcessStates.Started: { StartedTime = this.GetMarketTime(); LogProcessState(value); OnStarted(); break; } case ProcessStates.Stopping: { LogProcessState(value); OnStopping(); break; } case ProcessStates.Stopped: { TotalWorkingTime += this.GetMarketTime() - StartedTime; StartedTime = default(DateTime); LogProcessState(value); OnStopped(); break; } } ProcessStateChanged.SafeInvoke(this); this.Notify("ProcessState"); } catch (Exception error) { OnError(error); } if (ProcessState == ProcessStates.Stopping) { if (CancelOrdersWhenStopping) { this.AddInfoLog("Ожидание снятия всех активных заявок."); ProcessCancelActiveOrders(); } foreach (var rule in Rules.SyncGet(c => c.ToArray())) { if (this.TryRemoveRule(rule)) _childStrategies.TryRemoveStoppedRule(rule); } TryFinalStop(); } } } private void LogProcessState(ProcessStates state) { string stateStr; switch (state) { case ProcessStates.Stopped: stateStr = "остановлена"; break; case ProcessStates.Stopping: stateStr = "останавливается"; break; case ProcessStates.Started: stateStr = "запущена"; break; default: throw new ArgumentOutOfRangeException("state"); } this.AddInfoLog("Стратегия {0}. [{1},{2}]. Позиция при старте {3}.", stateStr, ChildStrategies.Count, Parent != null ? Parent.ChildStrategies.Count : -1, Position); } /// /// Событие изменения . /// public event Action ProcessStateChanged; private readonly StrategyParam _cancelOrdersWhenStopping; /// /// Снимать активные заявки при остановке. По-умолчанию включено. /// [Browsable(false)] public bool CancelOrdersWhenStopping { get { return _cancelOrdersWhenStopping.Value; } set { _cancelOrdersWhenStopping.Value = value; } } private readonly CachedSynchronizedSet _orders = new CachedSynchronizedSet { ThrowIfDuplicate = true }; //private readonly CachedSynchronizedList _orders = new CachedSynchronizedList(); /// /// Заявки, зарегистрированные в рамках стратегии. /// [Browsable(false)] public IEnumerable Orders { get { return _orders.Cache; } } private readonly CachedSynchronizedSet _stopOrders = new CachedSynchronizedSet { ThrowIfDuplicate = true }; //private readonly CachedSynchronizedList _stopOrders = new CachedSynchronizedList(); /// /// Стоп-заявки, зарегистрированные в рамках стратегии. /// [Browsable(false)] public IEnumerable StopOrders { get { return _stopOrders.Cache; } } private readonly StrategyParam _ordersKeepTime; /// /// Время хранения заявок и в памяти. /// По-умолчанию равно 2-ум дням. Если значение установлено в , то заявки не будут удаляться. /// [Browsable(false)] public TimeSpan OrdersKeepTime { get { return _ordersKeepTime.Value; } set { if (value < TimeSpan.Zero) throw new ArgumentOutOfRangeException("value", value, "Время хранения заявок не может быть отрицательным."); _ordersKeepTime.Value = value; InitMaxOrdersKeepTime(); RecycleOrders(); } } private void InitMaxOrdersKeepTime() { _maxOrdersKeepTime = TimeSpan.FromTicks((long)(OrdersKeepTime.Ticks * 1.5)); } private readonly CachedSynchronizedSet _myTrades = new CachedSynchronizedSet { ThrowIfDuplicate = true }; /// /// Cделки, прошедшие в течении работы стратегии. /// [Browsable(false)] public IEnumerable MyTrades { get { return _myTrades.Cache; } } private readonly CachedSynchronizedSet _orderFails = new CachedSynchronizedSet { ThrowIfDuplicate = true }; /// /// Заявки с ошибками, зарегистрированные в рамках стратегии. /// [Browsable(false)] public IEnumerable OrderFails { get { return _orderFails.Cache; } } private readonly StrategyParam _volume; /// /// Объем, которым необходимо оперировать. /// /// /// Если значение установлено в 0, то параметр игнорируется. /// [Category(_category)] [PropertyOrder(3)] [DisplayName("Объем")] [Description("Объем, которым необходимо оперировать.")] public decimal Volume { get { return _volume.Value; } set { if (value < 0) throw new ArgumentOutOfRangeException("value", value, "Объем стратегии не может быть меньше нуля."); _volume.Value = value; } } private LogLevels _errorState; /// /// Состояние ошибки. /// [Browsable(false)] public LogLevels ErrorState { get { return _errorState; } private set { if (_errorState == value) return; _errorState = value; this.Notify("ErrorState"); } } private readonly ChildStrategyList _childStrategies; /// /// Дочерние торговые стратегии. /// [Browsable(false)] public IStrategyChildStrategyList ChildStrategies { get { return _childStrategies; } } private DateTime _startedTime; /// /// Время запуска стратегии. /// [Category(_category)] [PropertyOrder(3)] [DisplayName("Время запуска")] [Description("Время запуска стратегии.")] [ReadOnly(true)] public DateTime StartedTime { get { return _startedTime; } private set { _startedTime = value; this.Notify("StartedTime"); } } private TimeSpan _totalWorkingTime; /// /// Общее время работы стратегии с вычетом временных отрезков, когда стратегия останавливалась. /// [Browsable(false)] public TimeSpan TotalWorkingTime { get { var retVal = _totalWorkingTime; if (StartedTime != default(DateTime) && Trader != null) retVal += this.GetMarketTime() - StartedTime; return retVal; } private set { if (_totalWorkingTime == value) return; _totalWorkingTime = value; this.Notify("TotalWorkingTime"); } } private readonly StrategyParam _disposeOnStop; /// /// Автоматически освобождать занятые ресурсы стратерии при ее остановке /// (состояние стало равным ) /// и удалять ее из родительской через . /// /// Режим используется только для одноразовых стратегий, тоесть для тех, что не будут запущены повторно (например, котирование). /// По умолчанию выключено. [Browsable(false)] public bool DisposeOnStop { get { return _disposeOnStop.Value; } set { _disposeOnStop.Value = value; } } private readonly StrategyParam _waitAllTrades; /// /// Останавливать стратегию только после получения всех сделок по зарегистрированным заявкам. /// /// По умолчанию выключено. [Browsable(false)] public bool WaitAllTrades { get { return _waitAllTrades.Value; } set { _waitAllTrades.Value = value; } } private readonly StrategyParam _commentOrders; /// /// Добавлять в название стратегии , выставившая заявку. /// /// По умолчанию выключено. [Browsable(false)] public bool CommentOrders { get { return _commentOrders.Value; } set { _commentOrders.Value = value; } } /// /// Зарегистрированные правила. /// [Browsable(false)] public IMarketRuleList Rules { get; private set; } private readonly object _rulesSuspendLock = new object(); private int _rulesSuspendCount; /// /// Приостановлено ли исполнение правил. /// /// /// Приостановка правил происходит через метод . /// [Browsable(false)] public bool IsRulesSuspended { get { return _rulesSuspendCount > 0; } } /// /// Событие отправки заявки на регистрацию. /// public event Action OrderRegistering; /// /// Событие об успешной регистрации заявки. /// public event Action OrderRegistered; /// /// Событие об ошибке регистрации заявки. /// public event Action OrderRegisterFailed; /// /// Событие отправки стоп-заявки на регистрацию. /// public event Action StopOrderRegistering; /// /// Событие об успешной регистрации стоп-заявки. /// public event Action StopOrderRegistered; /// /// Событие об ошибке регистрации стоп-заявки. /// public event Action StopOrderRegisterFailed; /// /// Событие об изменении заявки. /// public event Action OrderChanged; /// /// Событие об изменении стоп-заявки. /// public event Action StopOrderChanged; /// /// Событие отправки заявки на отмену. /// public event Action OrderCanceling; /// /// Событие отправки стоп-заявки на отмену. /// public event Action StopOrderCanceling; /// /// Событие отправки заявки на перерегистрацию. /// public event Action OrderReRegistering; /// /// Событие отправки стоп-заявки на перерегистрацию. /// public event Action StopOrderReRegistering; /// /// Событие об ошибке отмены заявки. /// public event Action OrderCancelFailed; /// /// Событие об ошибке отмены стоп-заявки. /// public event Action StopOrderCancelFailed; /// /// Событие о появлении новых сделок. /// public event Action> NewMyTrades; /// /// Событие изменения шлюза стратегии. /// public event Action TraderChanged; /// /// Событие изменения инструмента стратегии. /// public event Action SecurityChanged; /// /// Событие изменения портфеля стратегии. /// public event Action PortfolioChanged; /// /// Событие изменения позиций стратегии. /// public event Action> PositionsChanged; /// /// Событие возникновения ошибки в стратегии. /// public event Action Error; /// /// Метод вызывается тогда, когда вызвался метод , и состояние перешло в значение . /// protected virtual void OnStarted() { ThrowIfTraderNotRegistered(); if (Security == null) throw new InvalidOperationException("Инструмент не инициализирован."); if (Portfolio == null) throw new InvalidOperationException("Портфель не инициализирован."); foreach (var parameter in Parameters) { var unit = parameter.Value as Unit; if (unit != null && unit.Security == null && (unit.Type == UnitTypes.Point || unit.Type == UnitTypes.Step)) unit.Security = Security; } //pyh: Это сильно замедляет тестирование + объясните зачем это нужно. //ProcessNewOrders(Trader.StopOrders, true).ForEach(AddOrder); //ProcessNewOrders(Trader.Orders, false).ForEach(AddOrder); ErrorCount = 0; ErrorState = LogLevels.Info; } /// /// Метод вызывается тогда, когда состояние процесса перешло в значение . /// protected virtual void OnStopping() { } /// /// Метод вызывается тогда, когда состояние процесса перешло в значение . /// protected virtual void OnStopped() { } /// /// Зарегистрировать заявку и автоматически добавить для запуска механизмов расчета прибыли-убытка и проскальзывания. /// /// Заявка. public virtual void RegisterOrder(Order order) { if (order == null) throw new ArgumentNullException("order"); this.AddInfoLog("Регистрация новой {0} (0x{5:X}) заявки на {1} с ценой {2} и объемом {3}. {4}", order.Type, order.Direction, order.Price, order.Volume, order.Comment, order.GetHashCode()); ThrowIfTraderNotRegistered(); if (ProcessState != ProcessStates.Started) { this.AddWarningLog("Стратегия в состоянии {0}. Регистрация заявки невозможна.", ProcessState); return; } if (order.Type == OrderTypes.Conditional) _pendingStopOrders.Add(order); else _pendingOrders.Add(order); _ordersInfo.Add(order, new OrderInfo { IsOwn = true }); if (order.Security == null) order.Security = Security; if (order.Portfolio == null) order.Portfolio = Portfolio; //if (order.Strategy == null) // order.Strategy = this; ApplyMonitorRules(order); if (order.Type == OrderTypes.Conditional) OnStopOrderRegistering(order); else OnOrderRegistering(order); Trader.RegisterOrder(order); } /// /// Перерегистрировать заявку и автоматически добавить для запуска механизмов расчета прибыли-убытка и проскальзывания. /// /// Заявка, которую нужно снять и на основе нее зарегистрировать новую. /// Новая заявка. public virtual void ReRegisterOrder(Order oldOrder, Order newOrder) { if (oldOrder == null) throw new ArgumentNullException("oldOrder"); if (newOrder == null) throw new ArgumentNullException("newOrder"); this.AddInfoLog("Перерегистрация заявки {0} с ценой {1} на цену {2}. {3}", oldOrder.TransactionId, oldOrder.Price, newOrder.Price, oldOrder.Comment); ThrowIfTraderNotRegistered(); if (ProcessState != ProcessStates.Started) { this.AddWarningLog("Стратегия в состоянии {0}. Перерегистрация заявки невозможна.", ProcessState); return; } if (oldOrder.Type == OrderTypes.Conditional) _pendingStopOrders.Add(newOrder); else _pendingOrders.Add(newOrder); _ordersInfo.Add(newOrder, new OrderInfo { IsOwn = true }); //if (newOrder.Strategy == null) // newOrder.Strategy = this; ApplyMonitorRules(newOrder); ReRegisterSlippage(oldOrder, newOrder); if (oldOrder.Type == OrderTypes.Conditional) OnStopOrderReRegistering(oldOrder, newOrder); else OnOrderReRegistering(oldOrder, newOrder); Trader.ReRegisterOrder(oldOrder, newOrder); } private void ApplyMonitorRules(Order order) { if (CancelOrdersWhenStopping) { IMarketRule matchedRule = order.WhenMatched(); if (WaitAllTrades) matchedRule = matchedRule.And(order.WhenAllTrades()); order .WhenCanceled() .Or(matchedRule, order.WhenRegisterFailed()) .Do(() => this.AddInfoLog("Заявка {0} больше не активна.".Put(order.TransactionId))) .Until(() => { if (order.State == OrderStates.Failed) return true; using (order.BeginRead()) { if (order.State != OrderStates.Done) return false; if (!WaitAllTrades) return true; var matchedVolume = order.GetMatchedVolume(); if (matchedVolume == 0) return true; var info = _ordersInfo.TryGetValue(order); if (info == null) return false; return matchedVolume == info.ReceivedVolume; } }) .Apply(this); } } /// /// Отменить заявку. /// /// Заявка для отмены. public virtual void CancelOrder(Order order) { if (ProcessState != ProcessStates.Started) { this.AddWarningLog("Стратегия в состоянии {0}. Отмена заявки невозможна.", ProcessState); return; } if (order == null) throw new ArgumentNullException("order"); lock (_ordersInfo.SyncRoot) { var info = _ordersInfo.TryGetValue(order); if (info == null || !info.IsOwn) throw new ArgumentException("Заявка {0} не принадлежит стратегии {1}.".Put(order.TransactionId, Name)); if (info.IsCanceled) { this.AddWarningLog("Для заявки {0} уже был послан сигнал на отмену.", order.TransactionId); return; } info.IsCanceled = true; } ThrowIfTraderNotRegistered(); CancelOrderHandler(order); } private void CancelOrderHandler(Order order) { if (order == null) throw new ArgumentNullException("order"); this.AddInfoLog("Отмена заявки {0}.", order.TransactionId); if (order.Type == OrderTypes.Conditional) OnStopOrderCanceling(order); else OnOrderCanceling(order); Trader.CancelOrder(order); } private event Action ReRegistering; /// /// Уведомить о перерегистрации заявки. /// /// Заявка, которую нужно снять и на основе нее зарегистрировать новую. /// Новая заявка. protected void ReRegisterSlippage(Order oldOrder, Order newOrder) { this.AddInfoLog("Перерегистрация проскальзывания заявки {0} (0x{1:X}) на заявку (0x{2:X}).", oldOrder.TransactionId, oldOrder.GetHashCode(), newOrder.GetHashCode()); SlippageManager.ReRegistering(oldOrder, newOrder); ReRegistering.SafeInvoke(oldOrder, newOrder); } /// /// Добавить заявку в стратегию. /// /// Заявка. public void AddOrder(Order order) { if (order == null) throw new ArgumentNullException("order"); lock (_ordersInfo.SyncRoot) { var info = _ordersInfo.SafeAdd(order); if (info.IsHandled) return; info.IsHandled = true; } TryInvoke(() => { ThrowIfTraderNotRegistered(); var isLatChanged = LatencyManager.ProcessNewOrder(order) != TimeSpan.Zero; if (order.Type == OrderTypes.Conditional) { _stopOrders.Add(order); OnStopOrderRegistered(order); StatisticManager.AddNewOrder(order); } else { _orders.Add(order); OnOrderRegistered(order); //SlippageManager.Registered(order); var isComChanged = CommissionManager.ProcessOrder(order) != 0; var isPosChanged = PositionManager.ProcessOrder(order) != 0; StatisticManager.AddNewOrder(order); if (isComChanged) RaiseCommissionChanged(); if (isPosChanged) RaisePositionChanged(); } if (_firstOrderTime == default(DateTime)) _firstOrderTime = order.Time; _lastOrderTime = order.Time; RecycleOrders(); if (isLatChanged) RaiseLatencyChanged(); if (ProcessState == ProcessStates.Stopping && CancelOrdersWhenStopping) { lock (_ordersInfo.SyncRoot) { var info = _ordersInfo.TryGetValue(order); // заявка принадлежит дочерней стратегии if (info == null || !info.IsOwn) return; // для заявки уже был послан сигнал на снятие if (info.IsCanceled) return; info.IsCanceled = true; } CancelOrderHandler(order); } }); } /// /// Добавить активную заявку в стратегию и обработать сделки по заявке. /// /// /// Используется для восстановления состояния стратегии, когда необходимо /// подписаться на получение данных по заявкам, зарегистрированным ранее. /// /// Заявка. /// Сделки по заявке. public void AttachOrder(Order order, IEnumerable myTrades) { if(order == null) throw new ArgumentNullException("order"); if (myTrades == null) throw new ArgumentNullException("myTrades"); _ordersInfo.Add(order, new OrderInfo { IsOwn = true }); ApplyMonitorRules(order); PositionManager.ProcessOrder(order); if(order.Type == OrderTypes.Conditional) { _pendingStopOrders.Add(order); ProcessNewOrders(Trader.StopOrders, true).ForEach(AddOrder); } else { _pendingOrders.Add(order); ProcessNewOrders(Trader.Orders, false).ForEach(AddOrder); } OrderRegistering.SafeInvoke(order); OnTraderNewMyTrades(myTrades); } private void RecycleOrders() { if (OrdersKeepTime == TimeSpan.Zero) return; var diff = _lastOrderTime - _firstOrderTime; if (diff <= _maxOrdersKeepTime) return; _firstOrderTime = _lastOrderTime - OrdersKeepTime; _orders.SyncDo(d => d.RemoveWhere(o => o.State == OrderStates.Done && o.Time < _firstOrderTime)); _stopOrders.SyncDo(d => d.RemoveWhere(o => o.State == OrderStates.Done && o.Time < _firstOrderTime)); _ordersInfo.SyncDo(d => d.RemoveWhere(o => o.Key.State == OrderStates.Done && o.Key.Time < _firstOrderTime)); } private void ChangeOrders(IEnumerable orders) { if (orders == null) throw new ArgumentNullException("orders"); TryInvoke(() => { foreach (var order in orders) { ChangeOrder(order); } }); } private void ChangeOrder(Order order) { OnOrderChanged(order); if (PositionManager.ProcessOrder(order) != 0) RaisePositionChanged(); StatisticManager.AddChangedOrder(order); } /// /// Текущее время, которое будет передано в . /// public override DateTime CurrentTime { get { return this.GetMarketTime(); } } /// /// Вызвать событие . /// /// Отладочное сообщение. protected override void RaiseLog(LogMessage message) { if (message == null) throw new ArgumentNullException("message"); switch (message.Level) { case LogLevels.Warning: if (ErrorState == LogLevels.Info) ErrorState = LogLevels.Warning; break; case LogLevels.Error: ErrorState = LogLevels.Error; break; } // mika // так как некоторые стратегии слишком много пишут в лог, то получается слишком медленно // //TryInvoke(() => base.RaiseLog(message)); base.RaiseLog(message); } /// /// Запустить торговый алгоритм. /// public virtual void Start() { if (ProcessState == ProcessStates.Stopped) ProcessState = ProcessStates.Started; else this.AddDebugLog("Попытка запустить стратегию в состоянии {0}.", ProcessState); } /// /// Остановить торговый алгоритм. /// public virtual void Stop() { if (ProcessState == ProcessStates.Started) ProcessState = ProcessStates.Stopping; else this.AddDebugLog("Попытка остановить стратегию в состоянии {0}.", ProcessState); } /// /// Событие переинициализации стратегии. /// public event Action Reseted; /// /// Вызвать событие . /// protected void RaiseReseted() { Reseted.SafeInvoke(); } /// /// Переинициализировать торговый алгоритм. /// Вызывается после инициализации объекта стратегии и загрузки сохраненных параметров. /// public virtual void Reset() { this.AddInfoLog("Переинициализация."); ThrowIfTraderNotRegistered(); if (Security == null) throw new InvalidOperationException("Инструмент не инициализирован."); if (Portfolio == null) throw new InvalidOperationException("Портфель не инициализирован."); ChildStrategies.SyncDo(c => c.ForEach(s => s.Reset())); StatisticManager.Reset(); PnLManager.Reset(); CommissionManager.Reset(); LatencyManager.Reset(); PositionManager.Reset(); SlippageManager.Reset(); _orders.Clear(); _orderFails.Clear(); _stopOrders.Clear(); _myTrades.Clear(); _pendingOrders.Clear(); _pendingStopOrders.Clear(); _ordersInfo.Clear(); ProcessState = ProcessStates.Stopped; ErrorState = LogLevels.Info; ErrorCount = 0; _firstOrderTime = _lastOrderTime = default(DateTime); OnReseted(); } /// /// Вызывается из метода . /// protected virtual void OnReseted() { RaiseReseted(); } /// /// Приостановить исполнение правил до следующего восстановления через метод . /// public virtual void SuspendRules() { lock (_rulesSuspendLock) _rulesSuspendCount++; this.AddInfoLog("Приостановка правил. _rulesSuspendCount {0}.", _rulesSuspendCount); } /// /// Восстановить исполнение правил, остановленное через метод . /// public virtual void ResumeRules() { lock (_rulesSuspendLock) { if (_rulesSuspendCount > 0) _rulesSuspendCount--; } this.AddInfoLog("Возобновление правил. _rulesSuspendCount {0}.", _rulesSuspendCount); } private void TryFinalStop() { if (!Rules.IsEmpty()) { this.AddLog(LogLevels.Debug, () => "Попытка остановки. Осталось {0} правил. Правила {1}." .Put(Rules.Count, Rules.SyncGet(c => c.Select(r => r.Name).Join(", ")))); return; } ProcessState = ProcessStates.Stopped; if (DisposeOnStop) { //Trace.WriteLine(Name+" strategy-dispose-on-stop"); if (Parent != null) Parent.ChildStrategies.Remove(this); Dispose(); } } void IMarketRuleContainer.ActivateRule(IMarketRule rule, Func process) { //if (rule == null) // throw new ArgumentNullException("rule"); //if (process == null) // throw new ArgumentNullException("process"); if (_rulesSuspendCount > 0) { this.AddRuleLog(LogLevels.Debug, rule, "Не может быть обработано, так как приостановлено исполнение правил."); return; } try { this.ActiveRule(rule, process); } catch (Exception error) { OnError(error); } finally { if (_processState == ProcessStates.Stopping) TryFinalStop(); } } /// /// Интервал обработки события для изменения . /// /// /// Значение по-умолчанию равно 1 минуте. /// [Browsable(false)] public TimeSpan UnrealizedPnLInterval { get; set; } private DateTime _nextProcessTime; /// /// Метод, который вызывается при изменении инструмента стратегии. /// protected virtual void OnSecurityChanged() { SecurityChanged.SafeInvoke(); if (Trader == null) return; var marketTime = this.GetMarketTime(); if (_nextProcessTime > marketTime + UnrealizedPnLInterval) _nextProcessTime = marketTime + UnrealizedPnLInterval; if (marketTime < _nextProcessTime) return; _nextProcessTime = marketTime + UnrealizedPnLInterval; if (Position != 0) RaisePnLChanged(); } /// /// Метод, который вызывается при изменении портфеля стратегии. /// protected virtual void OnPortfolioChanged() { PortfolioChanged.SafeInvoke(); } /// /// Метод, который вызывается при изменении позиций стратегии. /// /// Измененные позиции стратегии. protected virtual void OnPositionsChanged(IEnumerable positions) { PositionsChanged.SafeInvoke(positions); } /// /// Метод, который вызывается при появлении новых сделок стратегии. /// /// Новые сделки стратегии. protected virtual void OnNewMyTrades(IEnumerable trades) { NewMyTrades.SafeInvoke(trades); } /// /// Вызвать событие . /// /// Заявка. protected virtual void OnOrderRegistering(Order order) { SlippageManager.Registering(order); OrderRegistering.SafeInvoke(order); } /// /// Вызвать событие . /// /// Заявка. protected virtual void OnOrderRegistered(Order order) { OrderRegistered.SafeInvoke(order); } /// /// Вызвать событие . /// /// Стоп-заявка. protected virtual void OnStopOrderRegistering(Order order) { SlippageManager.Registering(order); StopOrderRegistering.SafeInvoke(order); } /// /// Вызвать событие . /// /// Стоп-заявка. protected virtual void OnStopOrderRegistered(Order order) { StopOrderRegistered.SafeInvoke(order); } /// /// Вызвать событие . /// /// Стоп-заявка. protected virtual void OnStopOrderCanceling(Order order) { StopOrderCanceling.SafeInvoke(order); } /// /// Вызвать событие . /// /// Заявка. protected virtual void OnOrderCanceling(Order order) { OrderCanceling.SafeInvoke(order); } /// /// Вызвать событие . /// /// Стоп-заявка, которую нужно снять. /// Новая стоп-заявка, которую нужно зарегистрировать. protected virtual void OnStopOrderReRegistering(Order oldOrder, Order newOrder) { StopOrderReRegistering.SafeInvoke(oldOrder, newOrder); } /// /// Вызвать событие . /// /// Заявка, которую нужно снять. /// Новая заявка, которую нужно зарегистрировать. protected virtual void OnOrderReRegistering(Order oldOrder, Order newOrder) { OrderReRegistering.SafeInvoke(oldOrder, newOrder); } /// /// Метод, который вызывается при изменении заявки стратегии. /// /// Измененная заявка. protected virtual void OnOrderChanged(Order order) { OrderChanged.SafeInvoke(order); if (LatencyManager.ProcessChangedOrder(order) != TimeSpan.Zero) RaiseLatencyChanged(); } /// /// Метод, который вызывается при изменении стоп-заявки стратегии. /// /// Измененная стоп-заявка. protected virtual void OnStopOrderChanged(Order order) { StopOrderChanged.SafeInvoke(order); StatisticManager.AddChangedOrder(order); } /// /// Метод, который вызывается при изменении стоп-заявок стратегии. /// /// Измененные стоп-заявки. protected virtual void OnStopOrdersChanged(IEnumerable orders) { foreach (var order in orders) { OnStopOrderChanged(order); if (order.DerivedOrder == null) continue; lock (_ordersInfo.SyncRoot) { var derivedOrder = order.DerivedOrder; if (_ordersInfo.ContainsKey(derivedOrder)) continue; _ordersInfo.Add(derivedOrder, new OrderInfo { IsOwn = true }); AddOrder(derivedOrder); //заявка могла придти позже сделок по ней OnTraderNewMyTrades(Trader.MyTrades.Where(t => t.Order == derivedOrder)); } } } /// /// Метод, который вызывается при ошибке регистрации заявки стратегии. /// /// Ошибка регистрации заявки. protected virtual void OnOrderFailed(OrderFail fail) { OrderRegisterFailed.SafeInvoke(fail); StatisticManager.AddRegisterFailedOrder(fail); } /// /// Метод, который вызывается при ошибке регистрации стоп-заявки стратегии. /// /// Ошибка регистрации стоп-заявки. protected virtual void OnStopOrderFailed(OrderFail fail) { StopOrderRegisterFailed.SafeInvoke(fail); StatisticManager.AddRegisterFailedOrder(fail); } /// /// Метод, который вызывается при ошибке отмены заявки стратегии. /// /// Ошибка отмены заявки. protected virtual void OnOrderCancelFailed(OrderFail fail) { OrderCancelFailed.SafeInvoke(fail); } /// /// Метод, который вызывается при ошибке отмены стоп-заявки стратегии. /// /// Ошибка отмены стоп-заявки. protected virtual void OnStopOrderCancelFailed(OrderFail fail) { StopOrderCancelFailed.SafeInvoke(fail); } private void OnTraderSecuritiesChanged(IEnumerable securities) { if (securities.Contains(Security)) TryInvoke(OnSecurityChanged); } private void OnTraderPortfoliosChanged(IEnumerable portfolios) { if (portfolios.Contains(Portfolio)) TryInvoke(OnPortfolioChanged); } private void OnTraderPositionsChanged(IEnumerable positions) { Position[] changedPositions; if (Portfolio is BasketPortfolio) { var innerPfs = ((BasketPortfolio)Portfolio).InnerPortfolios; changedPositions = positions.Where(pos => innerPfs.Contains(pos.Portfolio)).ToArray(); } else { changedPositions = positions.Where(p => p.Portfolio == Portfolio).ToArray(); } if (changedPositions.Length > 0) TryInvoke(() => OnPositionsChanged(changedPositions)); } private void OnTraderNewMyTrades(IEnumerable trades) { trades = trades.Where(t => IsOwnOrder(t.Order)).ToArray(); if (trades.IsEmpty()) return; AddMyTrades(trades); } private void OnTraderNewOrders(IEnumerable orders) { lock (_orders.SyncRoot) ProcessNewOrders(orders, false).ForEach(AddOrder); } private void OnTraderNewStopOrders(IEnumerable orders) { lock (_stopOrders.SyncRoot) ProcessNewOrders(orders, true).ForEach(AddOrder); } private void OnTraderOrdersChanged(IEnumerable orders) { orders = orders.Where(IsOwnOrder).ToArray(); if (orders.IsEmpty()) return; ChangeOrders(orders); } private void OnTraderStopOrdersChanged(IEnumerable orders) { orders = orders.Where(_stopOrders.Contains).ToArray(); if (!orders.IsEmpty()) TryInvoke(() => OnStopOrdersChanged(orders)); } private void OnTraderOrdersRegisterFailed(IEnumerable fails) { ProcessRegisterOrderFails(_pendingOrders, fails, OnOrderFailed); } private void OnTraderStopOrdersRegisterFailed(IEnumerable fails) { ProcessRegisterOrderFails(_pendingStopOrders, fails, OnStopOrderFailed); } private void OnTraderOrdersCancelFailed(IEnumerable fails) { ProcessCancelOrderFails(fails); } private void OnTraderStopOrdersCancelFailed(IEnumerable fails) { ProcessCancelOrderFails(fails); } private void AddMyTrades(IEnumerable trades) { var filteredTrades = _myTrades.SyncGet(set => { var newTrades = trades.Where(t => !set.Contains(t)).ToArray(); set.AddRange(newTrades); return newTrades; }); if (filteredTrades.IsEmpty()) return; if (WaitAllTrades) { foreach (var trade in filteredTrades) { lock (_ordersInfo.SyncRoot) { var info = _ordersInfo.TryGetValue(trade.Order); if (info == null || !info.IsOwn) continue; info.ReceivedVolume += trade.Trade.Volume; } } } TryInvoke(() => OnNewMyTrades(filteredTrades)); var isComChanged = false; var isPnLChanged = false; var isPosChanged = false; var isSlipChanged = false; foreach (var trade in filteredTrades) { this.AddInfoLog("Новая {0} сделка {1} по цене {2} на {3} заявки {4}.", trade.Order.Direction, trade.Trade.Id, trade.Trade.Price, trade.Trade.Volume, trade.Order.TransactionId); if (CommissionManager.ProcessMyTrade(trade) != 0) isComChanged = true; var tradeInfo = PnLManager.ProcessMyTrade(trade); if (tradeInfo.PnL != 0) isPnLChanged = true; if (PositionManager.ProcessMyTrade(trade) != 0) isPosChanged = true; if (SlippageManager.AddTrade(trade) != 0) isSlipChanged = true; StatisticManager.AddMyTrade(tradeInfo); } TryInvoke(() => { if (isComChanged) RaiseCommissionChanged(); if (isPnLChanged) RaisePnLChanged(); if (isPosChanged) RaisePositionChanged(); if (isSlipChanged) RaiseSlippageChanged(); }); } private void RaiseSlippageChanged() { this.Notify("Slippage"); SlippageChanged.SafeInvoke(); } private void RaisePositionChanged() { this.AddInfoLog("Новая позиция: {0}.", PositionManager.Positions.Select(pos => pos + "=" + pos.CurrentValue).Join(", ")); this.Notify("Position"); PositionChanged.SafeInvoke(); StatisticManager.AddPosition(this.GetMarketTime(), Position); } private void RaiseCommissionChanged() { this.Notify("Commission"); CommissionChanged.SafeInvoke(); } private void RaisePnLChanged() { this.Notify("PnL"); PnLChanged.SafeInvoke(); StatisticManager.AddPnL(this.GetMarketTime(), PnL); } private void RaiseLatencyChanged() { this.Notify("Latency"); LatencyChanged.SafeInvoke(); } /// /// Обработать поступившие от шлюза заявки, и найти из них те, что принадлежат стратегии. /// /// Новые заявки. /// Признак того, пришли ли это обычные заявки или стоп-заявки. /// Заявки, принадлежащие стратегии. protected virtual IEnumerable ProcessNewOrders(IEnumerable newOrders, bool isStopOrders) { return (isStopOrders ? _pendingStopOrders : _pendingOrders).SyncGet(c => { var retVal = newOrders.Where(c.Contains).ToArray(); c.RemoveRange(retVal); return retVal; }); } /// /// Заявки, ожидающие отправленные на регистрацию, но еще не получившие ответа. /// [Browsable(false)] public IEnumerable PendingOrders { get { return _pendingOrders.Cache; } } /// /// Загрузить настройки. /// /// Хранилище настроек. public virtual void Load(SettingsStorage storage) { var parameters = storage.GetValue("Parameters"); if (parameters == null) return; var dict = Parameters.SyncGet(c => c.ToDictionary(p => p.Name, p => p, StringComparer.InvariantCultureIgnoreCase)); foreach (var pair in parameters) dict[pair.Key].Value = pair.Value; } /// /// Сохранить настройки. /// /// Хранилище настроек. public virtual void Save(SettingsStorage storage) { var paramStorage = new SettingsStorage(); foreach (var strategyParam in Parameters.SyncGet(c => c.ToArray())) paramStorage.SetValue(strategyParam.Name, strategyParam.Value); storage.SetValue("Parameters", paramStorage); } /// /// Событие изменения параметров стратегии. /// public event PropertyChangedEventHandler PropertyChanged; void INotifyPropertyChangedEx.NotifyPropertyChanged(string info) { PropertyChanged.SafeInvoke(this, info); } /// /// Отменить все активные заявки (стоп и обычные). /// public void CancelActiveOrders(Expression> expression = null) { if (ProcessState != ProcessStates.Started) { this.AddWarningLog("Стратегия в состоянии {0}. Отмена заявок невозможна.", ProcessState); return; } if (expression == null) this.AddInfoLog("Снятие всех активных заявок."); else this.AddInfoLog("Снятие активных заявок по условию {0}", expression.Body); ProcessCancelActiveOrders(expression); } private void ProcessCancelActiveOrders(Expression> expression = null) { ThrowIfTraderNotRegistered(); Func query; if (expression == null) query = o => o.State == OrderStates.Active; else { var func = expression.Compile(); query = o => o.State == OrderStates.Active && func(o); } //Func query = (o => o.State == OrderStates.Active && expression(o)); _ordersInfo.SyncGet(d => d.Keys.Filter(query).ToArray()).ForEach(o => { var info = _ordersInfo.TryGetValue(o); //заявка принадлежит дочерней статегии if (!info.IsOwn) return; if (info.IsCanceled) { this.AddWarningLog("Для заявки {0} уже был послан сигнал на отмену.", o.TransactionId); return; } info.IsCanceled = true; CancelOrderHandler(o); }); } private void OnChildOrderChanged(Order order) { ChangeOrder(order); } private void OnChildStopOrderChanged(Order order) { TryInvoke(() => StopOrderChanged.SafeInvoke(order)); } private void OnChildOrderRegisterFailed(OrderFail fail) { SlippageManager.RegisterFailed(fail); TryInvoke(() => OrderRegisterFailed.SafeInvoke(fail)); } private void OnChildStopOrderRegisterFailed(OrderFail fail) { SlippageManager.RegisterFailed(fail); TryInvoke(() => StopOrderRegisterFailed.SafeInvoke(fail)); } private void OnChildOrderCancelFailed(OrderFail fail) { TryInvoke(() => OrderCancelFailed.SafeInvoke(fail)); } private void OnChildStopOrderCancelFailed(OrderFail fail) { TryInvoke(() => StopOrderCancelFailed.SafeInvoke(fail)); } private void ProcessCancelOrderFails(IEnumerable fails) { foreach (var fail in fails) { var order = fail.Order; lock (_ordersInfo.SyncRoot) { var info = _ordersInfo.TryGetValue(order); if (info == null || !info.IsOwn) continue; info.IsCanceled = false; } this.AddErrorLog("Заявка {0} не была отменена по причине {1}.", order.TransactionId, fail.Error); if (order.Type == OrderTypes.Conditional) OnStopOrderCancelFailed(fail); else OnOrderCancelFailed(fail); StatisticManager.AddFailedOrderCancel(fail); } } private void ProcessRegisterOrderFails(SynchronizedSet order, IEnumerable fails, Action evt) { var failedOrders = order.SyncGet(c => { var retVal = new List(); foreach (var fail in fails) { if (c.Contains(fail.Order)) { retVal.Add(fail); c.Remove(fail.Order); } } return retVal; }); foreach (var fail in failedOrders) { this.AddErrorLog("Заявка {0} (0x{2:X}) не была принята по причине {1}.", fail.Order.TransactionId, fail.Error, fail.Order.GetHashCode()); SlippageManager.RegisterFailed(fail); } _orderFails.AddRange(failedOrders); TryInvoke(() => failedOrders.ForEach(evt)); } /// /// Обработка ошибки, полученной в результате работы стратегии. /// /// Ошибка. protected virtual void OnError(Exception error) { ErrorCount++; Error.SafeInvoke(error); this.AddErrorLog(error.ToString()); if (ErrorCount >= MaxErrorCount) Stop(); } object ICloneable.Clone() { return Clone(); } /// /// Создать копию стратегии со всеми настройками. /// /// Копия стратегии. public Strategy Clone() { var clone = GetType().CreateInstance(); clone.Trader = Trader; clone.Security = Security; clone.Portfolio = Portfolio; var id = clone.Id; clone.Load(this.Save()); clone.Id = id; return clone; } private void ThrowIfTraderNotRegistered() { if (Trader == null) throw new InvalidOperationException("Шлюз не инициализирован."); } private void TryInvoke(Action handler) { _disposeLock.Read(() => { if (IsDisposed) return; handler(); }); } private bool IsOwnOrder(Order order) { var info = _ordersInfo.TryGetValue(order); return info != null && info.IsOwn; } /// /// Освободить занятые ресурсы. /// protected override void DisposeManaged() { _disposeLock.WriteAsync(() => { ChildStrategies.SyncDo(c => { c.ForEach(s => s.Dispose()); c.Clear(); }); Trader = null; }); base.DisposeManaged(); MemoryStatistics.Instance.Strategies.Remove(this); } } }