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