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