using System; using System.Collections.Generic; using System.Xml; using System.ComponentModel; using TradeIdeas.XML; using TradeIdeas.TIProGUI; using TradeIdeas.MiscSupport; namespace TIProAutoTradeExtension { public class TradeOrder : IRowDataCapable, INotifyPropertyChanged { public event PropertyChangedEventHandler PropertyChanged; [ColumnInfoAttribute(DisplayName = "Time", SizeHint = "XX:XX XX", Format = "t")] public DateTime Created { get; set; } = ServerFormats.Now; [ColumnInfoAttribute(DisplayName = "Updated", SizeHint = "XX:XX XX", Format = "t")] public DateTime Updated { get; set; } = ServerFormats.Now; /// /// True if the order was created prior to the connection to the IBroker. If this is the /// case, then the Created property has the timestamp of the connection time, not the actual /// creation time of the order. Can be used as a flag to determine if Created is accurate or not. /// [Browsable(false)] public bool CreatedPriorToConnection { get; set; } = false; [ColumnInfoAttribute(DisplayName = "Symbol", SizeHint = "SymbolXX")] public string Symbol { get; set; } = ""; [ColumnInfoAttribute(DisplayName = "Side", Format = "buysell", SizeHint = "XXXXX")] public bool Buy { get; set; } = true; private Instrument _instrument; [Browsable(false)] public Instrument Instrument { get => _instrument; set { _instrument = value; Symbol = _instrument.Symbol; } } private decimal _shares; [ColumnInfoAttribute(Format = "N1", DisplayName = "Quantity", SizeHint = "QuantityXX")] public decimal Shares { get => _shares; set { decimal previousShares = _shares; _shares = value; if (_shares != previousShares) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Shares")); } } } [ColumnInfoAttribute(DisplayName = "Filled / Shares", Format = "textaligncenter", SizeHint = "XXXX / XXXX")] public string SharesVersusFilled => _filledShares + "/" + _originalShares; private decimal _newShares; [Browsable(false)] public decimal NewShares { get => _newShares; set { decimal previousValue = _newShares; _newShares = value; if (previousValue != _newShares) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("NewShares")); } } } private decimal _filledShares; [ColumnInfoAttribute(Format = "N1", DisplayName = "Filled Shares", SizeHint = "FilledXX")] public decimal FilledShares { get => _filledShares; set { decimal previousValue = _filledShares; _filledShares = value; if (previousValue != _filledShares) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FilledShares")); } } } private decimal _originalShares; [Browsable(false)] public decimal OriginalShares { get => _originalShares; set { decimal previousValue = _originalShares; _originalShares = value; if (previousValue != _originalShares) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("OriginalShares")); } } } private decimal _filledPrice; [ColumnInfoAttribute(DisplayName = "Filled Price", Format = "4", SizeHint = "XXXX.XXX")] public decimal FilledPrice { get => _filledPrice; set { decimal previousValue = _filledPrice; _filledPrice = value; if (previousValue != _filledPrice) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("FilledPrice")); } } } [ColumnInfoAttribute(DisplayName = "Type", Format = "textalignright", SizeHint = "MarketXX")] public TradeType TradeType { get; set; } = TradeType.Market; [Browsable(false)] public bool? IsEntryOrder { get; set; } private decimal? _limitPrice; [ColumnInfoAttribute(Format = "2", DisplayName = "Limit Price", SizeHint = "XXXX.XX")] public decimal? LimitPrice { get => _limitPrice; set { _limitPrice = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("LimitPrice")); MarkPositionChanged("LimitPrice"); } } private decimal? _stopPrice; [ColumnInfoAttribute(Format = "2", DisplayName = "Stop Price", SizeHint = "XXXX.XX")] public decimal? StopPrice { get => _stopPrice; set { _stopPrice = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("StopPrice")); MarkPositionChanged("StopPrice"); } } private decimal? _trailingStopDistance; [ColumnInfoAttribute(Format = "2", DisplayName = "Trailing Stop Amount", SizeHint = "XXXX.XX")] public decimal? TrailingStopDistance { get => _trailingStopDistance; set { _trailingStopDistance = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("TrailingStopDistance")); } } private OcaGroup _ocaGroup; [ColumnInfoAttribute(DisplayName = "OCA Group", SizeHint = "XXXXXXX", DefaultVisible = false)] public OcaGroup OcaGroup { get => _ocaGroup; set => _ocaGroup = value; } private TradingStrategy _tradingStrategy; [ColumnInfoAttribute(DisplayName = "Strategy Name", SizeHint = "Strategy NameXX")] public TradingStrategy TradingStrategy { get => _tradingStrategy; set { _tradingStrategy = value; if (null != _tradingStrategy) { if (null != PropertyChanged && _strategyId != _tradingStrategy.Id) PropertyChanged(this, new PropertyChangedEventArgs("TradingStrategy")); _strategyId = _tradingStrategy.Id; } } } private string _strategyId = ""; [Browsable(false)] public string StrategyId { get => _strategyId; set => _strategyId = value; } private TradeOrderStatus _status = TradeOrderStatus.None; [ColumnInfoAttribute(DisplayName = "Status", SizeHint = "XXXXXXXX")] public TradeOrderStatus Status { get => _status; set { _status = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("Status")); } } /// /// Gets a value indicating whether this instance is live. Determined by current status of order. /// [Browsable(false)] public bool IsLive => _status == TradeOrderStatus.PartiallyFilled || _status == TradeOrderStatus.Queued || _status == TradeOrderStatus.Submitted || _status == TradeOrderStatus.New || _status == TradeOrderStatus.Held || _status == TradeOrderStatus.None || _status == TradeOrderStatus.Accepted; private int _resultCode; [ColumnInfoAttribute(DisplayName = "Error Code", SizeHint = "XXXXXX", DefaultVisible = false)] public int ResultCode { get => _resultCode; set => _resultCode = value; } /// /// Stores the rejection reason if an order is rejected. /// [ColumnInfoAttribute(DisplayName = "Error Reason", SizeHint = "XXXXXX", DefaultVisible = false)] public string ErrorReason { get; set; } private string _orderId = ""; [ColumnInfoAttribute(DisplayName = "Order Id", SizeHint = "XXXXXX", DefaultVisible = false)] public string OrderId { get => _orderId; set { _orderId = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("OrderId")); } } private string _localOrderId = ""; [Browsable(false)] public string LocalOrderId { get => _localOrderId; set => _localOrderId = value; } private string _brokerOrderId = ""; [ColumnInfoAttribute(DisplayName = "Reference Id", SizeHint = "Reference")] public string BrokerOrderId { get => _brokerOrderId; set => _brokerOrderId = value; } private string _parentId = ""; [ColumnInfoAttribute(DisplayName = "Parent Id", SizeHint = "XXXXXX")] public string ParentId { get => _parentId; set => _parentId = value; } private string _timeInForce = "GTD"; [ColumnInfoAttribute(SizeHint = "GTDXX", DisplayName = "Time In Force")] public string TimeInForce { get => _timeInForce; set => _timeInForce = value; } private DateTime? _goodAfterTime; [ColumnInfoAttribute(SizeHint = "XX:XX XX", Format = "t", DisplayName = "Good After Time")] public DateTime? GoodAfterTime { get => _goodAfterTime; set { _goodAfterTime = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("GoodAfterTime")); MarkPositionChanged("GAT"); } } private DateTime? _goodUntilTime; [ColumnInfoAttribute(SizeHint = "XX:XX XX", Format = "t", DisplayName = "Good Until Time")] public DateTime? GoodUntilTime { get => _goodUntilTime; set { _goodUntilTime = value; PropertyChanged?.Invoke(this, new PropertyChangedEventArgs("GoodUntilTime")); } } private DateTime? _cancelTime; [ColumnInfoAttribute(SizeHint = "XX:XX XX", Format = "t", DisplayName = "Cancel Time")] public DateTime? CancelTime { get => _cancelTime; set => _cancelTime = value; } [Browsable(false)] public IBroker Broker { get; set; } [ColumnInfoAttribute(SizeHint = "AccountXX", DisplayName = "Account")] public Account Account { get; set; } private Position _position; [Browsable(false)] public Position Position { get => _position; set => _position = value; } private bool _locallyCreated; /// /// True if order was initiated within the robot, false if was initiated in the broker software. /// [Browsable(false)] public bool LocallyCreated { get => _locallyCreated; set => _locallyCreated = value; } /// /// Corresponds to the Order Ref column in TWS. Used to track order ids to map back to AI trades. /// [Browsable(false)] public string OrderReference { get; set; } private TradeOrderSource _source = TradeOrderSource.unknown; /// /// Used to track source of order /// [Browsable(false)] public TradeOrderSource Source { get => _source; set => _source = value; } private bool _loggedFill; /// /// Used to determine whether an order has been logged for reporting purposes already. This is required for orders that are partially filled so that the "order_filled" event /// can be reported a single time and not repeatedly. /// [Browsable(false)] public bool LoggedFill { get => _loggedFill; set => _loggedFill = value; } private bool _loggedCreation; /// /// Used to determine whether an order has been logged for reporting purposes already. This is required for orders that are partially filled so that the creation event /// can be reported a single time and not repeatedly. /// [Browsable(false)] public bool LoggedCreation { get => _loggedCreation; set => _loggedCreation = value; } private bool _loggedCancel; /// /// Used to determine whether a canceled order has been logged for reporting purposes already. /// [Browsable(false)] public bool LoggedCancel { get => _loggedCancel; set => _loggedCancel = value; } /// /// This stores the original RowData object from the alert. /// [Browsable(false)] public RowData RowData { get; set; } private void MarkPositionChanged(string changeType) { Position?.MarkChanged(changeType); } public TradeOrder() { } public TradeOrder(TradeOrder order) { Symbol = order.Symbol; _instrument = order.Instrument; Buy = order.Buy; _shares = order.Shares; TradeType = order.TradeType; _limitPrice = order.LimitPrice; _stopPrice = order.StopPrice; _ocaGroup = order.OcaGroup; _tradingStrategy = order.TradingStrategy; Broker = order.Broker; Account = order.Account; _goodAfterTime = order.GoodAfterTime; _goodUntilTime = order.GoodUntilTime; _position = order.Position; _orderId = order.OrderId; OrderReference = order.OrderReference; IsEntryOrder = order.IsEntryOrder; _localOrderId = order.LocalOrderId; } public RowData ToRowData() { // Replace the default Account ToString value to display the Account.DisplayName value. var replacePropertyValueDictionary = new Dictionary(); if (null != Account) replacePropertyValueDictionary.Add("Account", Account.DisplayName); return RowDataHelper.GetRowDataFromObjectPrivately(this, null, replacePropertyValueDictionary); } public override bool Equals(object obj) { // If parameter is null return false. // If parameter cannot be cast to Point return false. var c = obj as TradeOrder; if (c == null) return false; // Return true if the fields match: return (LocalOrderId == c.LocalOrderId); } public bool Equals(TradeOrder c) { // If parameter is null return false: if (c == null) return false; // Return true if the fields match: return (LocalOrderId == c.LocalOrderId); } public override int GetHashCode() { return Symbol.GetHashCode(); } public override string ToString() { string tradeString = "symbol=" + Symbol + ", orderid=" + _orderId + ", localorderid=" + _localOrderId + ", status=" + _status + ", shares=" + _shares + ", buy=" + Buy + ", brokerorderid=" + _brokerOrderId + ", type=" + TradeType + ", gtd= " + _goodUntilTime + ", stop=" + _stopPrice + ", lmt=" + _limitPrice; if (null != _ocaGroup) tradeString += ", ocagroup=" + _ocaGroup.Name; if (null != _position) tradeString += ", position=" + _position.Shares; if (null != _tradingStrategy) tradeString += ", strategy=" + _tradingStrategy.Name; return tradeString; } public void Restore(XmlNode description) { Instrument = new Instrument(description.Property("SYMBOL")); Shares = description.Property("SHARES", 0M); FilledShares = description.Property("FILLEDSHARES", 0M); Buy = description.Property("BUY", true); if (description.Property("ISENTRYORDER") != "") IsEntryOrder = description.Property("ISENTRYORDER", false); if (description.Property("GOODAFTERTIME") != "") { DateTime.TryParse(description.Property("GOODAFTERTIME"), out var gat); if (null != gat) GoodAfterTime = gat; } //Ron: TradeType is a Class so it can't be used with this function //Is TradeType always Market here ? //TradeType = description.PropertyEnum("TRADETYPE", TradeType.Market); TradeType = TradeType.Market; if (description.Property("ACCOUNT") != "") { Account = new Account(); Account.Name = description.Property("ACCOUNT"); } StrategyId = description.Property("STRATEGYID"); OrderId = description.Property("ORDERID"); BrokerOrderId = description.Property("BROKERORDERID"); } public void Save(XmlNode parent) { parent.SetProperty("SYMBOL", Instrument.Symbol); parent.SetProperty("SHARES", Shares); parent.SetProperty("FILLEDSHARES", FilledShares); parent.SetProperty("BUY", Buy); if (null != GoodAfterTime) parent.SetProperty("GOODAFTERTIME", GoodAfterTime); if (null != Account) parent.SetProperty("ACCOUNT", Account.Name); parent.SetProperty("TRADETYPE", TradeType); if (null != IsEntryOrder) parent.SetProperty("ISENTRYORDER", IsEntryOrder); parent.SetProperty("ORDERID", OrderId); parent.SetProperty("BROKERORDERID", BrokerOrderId); if (null != TradingStrategy) parent.SetProperty("STRATEGYID", TradingStrategy.Id); } } }