using System; using System.Collections.Generic; using System.ComponentModel; using System.Linq; using System.Text; using System.Xml; using TradeIdeas.XML; using TradeIdeas.TIProData; using TradeIdeas.TIProGUI; using TradeIdeas.TIProData.Configuration; using System.Reflection; using System.Collections.Specialized; using System.Web; using System.IO; using TradeIdeas.MiscSupport; using TradeIdeas.TIProData.Interfaces; namespace TIProAutoTradeExtension { public enum TradePositionSizing { FixedShares, FixedDollars, BasedOnStopLoss } public enum TradeEntryType { Limit, Market, StopLimit, Stop } //public enum TradeEntryLimitReference { Last, BidAsk, Filter } public enum TradeEntryLimitReference { Last, Filter } /// /// Mode for how to treat the limit offset amount: as a dollar amount (default) or a percent /// public enum TradeEntryLimitOffsetType { /// /// Limit offset amount is in dollars /// Dollars, /// /// Limit offset amount is interpreted as a percent /// Percent } public enum TradeStopLossType { None, Percent, Dollars, Filter } /// /// Mode for trading AI signals: RiskOff, RiskOn, BestAtSignalTime /// public enum AiTradingMode { /// /// Strategy specific trading instruction are in effect /// RiskOff, /// /// Strategy specific trading instructions are mostly ignored /// RiskOn /// /// Choose RiskOn or RiskOff depending on best mode for segment at signal time /// //BestAtSignalTime }; /// /// The type of time stop order to be used for a strategy. /// public enum TradeTimeStopType { /// /// No time stop order will be entered. /// None, /// /// A time stop order will be entered at a certain time of day on the same day of the trade signal. /// TimeOfDay, /// /// A time stop order will be entered a certain amount of time after the trade signal. /// TimeAfterSignal, /// /// At the market open after a certain number of days. /// AtOpenDays, /// /// At the market close after a certain number of days. /// AtCloseDays } /// /// Fired when a server-side advanced exit triggers for an AI trade. /// /// The row data. public delegate void AdvancedExitTriggeredHandler(RowData rowData); public delegate void NewAlertHandler(RowData rowData, TradingStrategy strategy); public class TradingStrategy: IRowDataCapable { private bool _long = true; [ColumnInfoAttribute(DisplayName = "Side", Format = "longshort", SizeHint = "XXXXX")] public bool Long { get { return _long; } set { _long = value; } } private TradeEntryType _tradeEntryType = TradeEntryType.Limit; [ColumnInfoAttribute(DisplayName = "Entry Type", SizeHint = "XXXXXXX", Format = "textalignright")] public TradeEntryType TradeEntryType { get { return _tradeEntryType; } set { _tradeEntryType = value; } } private string _id = ""; [Browsable(false)] public string Id { get { return _id; } set { _id = value; } } private AiTradingMode _aiTradingMode = AiTradingMode.RiskOff; [Browsable(false)] public AiTradingMode AiTradingMode { get { return _aiTradingMode; } set { _aiTradingMode = value; } } private string _name = ""; [ColumnInfoAttribute(SizeHint="XXXXXXXXXXXXXXXXXX", Format = "strategyname")] public string Name { get { return _name; } set { _name = value; } } private double? _winrate = null; //[ColumnInfoAttribute(DisplayName = "Win %", Format="1", SizeHint = "Win %XX", MidForColor = 50, MinForColor = 30, MaxForColor = 80.0)] [Browsable(false)] public double? WinRate { get { return _winrate; } set { _winrate = value; } } private double? _omTrades = null; //[ColumnInfoAttribute(DisplayName = "Simulated Trades", Format="0", SizeHint = "TradesXX")] [Browsable(false)] public double? OmTrades { get { return _omTrades; } set { _omTrades = value; } } private double? _profitFactor = null; //[ColumnInfoAttribute(DisplayName = "Profit Factor", Format="2", SizeHint = "ProfitXX", MinForColor = 0.5, MidForColor = 1, MaxForColor = 5.0)] [Browsable(false)] public double? ProfitFactor { get { return _profitFactor; } set { _profitFactor = value; } } private double? _oddsMakerProgress = null; //[ColumnInfoAttribute(DisplayName = "Odds Maker Progress", Format="2", SizeHint = "Odds MakerXX", MinForColor = -100, MidForColor = 0, MaxForColor = 100, DefaultVisible = false)] [Browsable(false)] public double? OddsMakerProgress { get { return _oddsMakerProgress; } set { _oddsMakerProgress = value; } } private bool _oddsMakerIsRunning = false; [Browsable(false)] public bool OddsMakerIsRunning { get { return _oddsMakerIsRunning; } set { _oddsMakerIsRunning = value; } } private bool _oddsMakerIsQueued = false; [Browsable(false)] public bool OddsMakerIsQueued { get { return _oddsMakerIsQueued; } set { _oddsMakerIsQueued = value; } } private string _oddsMakerCsv = ""; [Browsable(false)] public string OddsMakerCsv { get { return _oddsMakerCsv; } set { _oddsMakerCsv = value; } } private string _lastSymbol = ""; [ColumnInfoAttribute(DisplayName = "Last Symbol", SizeHint = "SymbolXX")] public string LastSymbol { get { return _lastSymbol; } set { _lastSymbol = value; } } private decimal _lastSignalPrice = 0; [ColumnInfoAttribute(DisplayName = "Last Signal Price", SizeHint = "SymbolXX", Format="2")] public decimal LastSignalPrice { get { return _lastSignalPrice; } set { _lastSignalPrice = value; } } private DateTime? _lastSignalTime = null; [ColumnInfoAttribute(Format = "relativetime", DisplayName = "Last Signal Time", SizeHint = "Last SignalXX")] public DateTime? LastSignalTime { get { return _lastSignalTime; } set { _lastSignalTime = value; } } private bool _tradingEnabled = false; [ColumnInfoAttribute(DisplayName = "Strategy Status", Format = "onoff", SizeHint = "StrategyXX")] public bool TradingEnabled { get { return _tradingEnabled; } set { _tradingEnabled = value; } } private DateTime _startTime = GuiEnvironment.GetMarketOpenLocalTime(); [ColumnInfoAttribute(DisplayName="Start Time",Format = "tod", SizeHint = "XX:XX XX")] public DateTime StartTime { get { return _startTime; } set { _startTime = value; } } private int _startTimeMinutesFromOpen = GetXmlValue("START_TIME_MINUTES_FROM_OPEN", 5); [ColumnInfoAttribute(DisplayName="Start Time Minutes from Open",Format = "0")] public int StartTimeMinutesFromOpen { get { return _startTimeMinutesFromOpen; } set { _startTimeMinutesFromOpen = value; DateTime startTimeInMarketTimeZone = GuiEnvironment.GetMarketOpenInMarketTimeZone().AddMinutes(_startTimeMinutesFromOpen); _startTime = GuiEnvironment.ConvertTimeCheckingKindProperty(startTimeInMarketTimeZone, GuiEnvironment.GetMarketTimeZone(), TimeZoneInfo.Local); } } private DateTime _endTime = GuiEnvironment.GetMarketCloseLocalTime(); [ColumnInfoAttribute(DisplayName = "End Time", Format = "tod", SizeHint = "XX:XX XX")] public DateTime EndTime { get { return _endTime; } set { _endTime = value; } } private int _endTimeMinutesFromClose = GetXmlValue("END_TIME_MINUTES_BEFORE_CLOSE", 15); [ColumnInfoAttribute(DisplayName="End Time Minutes from Close",Format = "0")] public int EndTimeMinutesFromClose { get { return _endTimeMinutesFromClose; } set { _endTimeMinutesFromClose = value; DateTime endTimeInMarketTimeZone = GuiEnvironment.GetMarketCloseInMarketTimeZone().AddMinutes(-_endTimeMinutesFromClose); _endTime = GuiEnvironment.ConvertTimeCheckingKindProperty(endTimeInMarketTimeZone, GuiEnvironment.GetMarketTimeZone(), TimeZoneInfo.Local); } } private int _maxDailyOrders = GetXmlValue("MAX_DAILY_ORDERS", 10); [ColumnInfoAttribute(Format = "0", DisplayName = "Max Daily Trades", SizeHint = "Max DailyXX")] public int MaxDailyOrders { get { return _maxDailyOrders; } set { _maxDailyOrders = value; } } private bool _cfdEnabled = false; /// /// Should this strategy use CFDs or the normal underlying? This applies to non-US only. /// [ColumnInfoAttribute(Format = "truefalse", DisplayName = "CFD Enabled", SizeHint = "FALSEXX")] public bool CfdEnabled { get { return _cfdEnabled; } set { _cfdEnabled = value; } } // tracking attributes private int _tradesSoFarToday = 0; [ColumnInfoAttribute(DisplayName = "Trades Today", Format = "0", SizeHint = "Trades")] public int TradesSoFarToday { get { return _tradesSoFarToday; } set { _tradesSoFarToday = value; } } [ColumnInfoAttribute(DisplayName = "Stop Loss / Profit Target", Format = "textaligncenter", SizeHint = "Profit TargetXX")] public string ProfitTargetStopLoss { get { return _stopLossOffset.ToString("N2") + " / " + _targetOffset.ToString("N2"); } } private int _timeStopDays; [ColumnInfoAttribute(DisplayName = "Exit Date", Format = "0", SizeHint = "ExitXX")] public int TimeStopDays { get { return _timeStopDays; } set { _timeStopDays = value; } } private decimal _openProfit = 0; [ColumnInfoAttribute(Format="profit", DisplayName = "Open P&L", SizeHint = "OpenXX")] public decimal OpenProfit { get { return _openProfit; } set { _openProfit = value; AddProfitToBars(TotalProfit); } } private decimal _closedProfit = 0; /// /// Gets an abbreviated summary of the entry strategy. Currently used to supply informative text in the /// right click menus for initiating a trade in a symbol from the Strategy Trades window. /// /// The trade data. /// The instrument. /// internal string GetSummary(RowData tradeData, Instrument instrument) { string toReturn = Name; if (null != instrument) { toReturn += ": "; if (TradeEntryType == TradeEntryType.Limit) toReturn += "Limit $" + BrokerManager.GetLimitPrice(tradeData, this, instrument.MinMove.GetValueOrDefault(0.01)); else if (TradeEntryType == TradeEntryType.Market) { toReturn += "Market"; if (instrument.Last > 0) toReturn += " ($" + instrument.Last + ")"; } else if (TradeEntryType == TradeEntryType.Stop) toReturn += "Stop $" + BrokerManager.GetEntryStopPrice(tradeData, this, instrument.MinMove.GetValueOrDefault(0.01)); else if (TradeEntryType == TradeEntryType.StopLimit) toReturn += "Stop $" + BrokerManager.GetEntryStopPrice(tradeData, this, instrument.MinMove.GetValueOrDefault(0.01)) + " Limit $" + BrokerManager.GetLimitPrice(tradeData, this, instrument.MinMove.GetValueOrDefault(0.01)); } return toReturn; } [ColumnInfoAttribute(Format = "profit", DisplayName = "Closed P&L", SizeHint = "ClosedXX")] public decimal ClosedProfit { get { return _closedProfit; } set { _closedProfit = value; AddProfitToBars(TotalProfit); } } [ColumnInfoAttribute(Format = "profit", DisplayName = "Total P&L", SizeHint = "ClosedXX")] public decimal TotalProfit { get { return _closedProfit + _openProfit; } } private string _route = "SMART"; [ColumnInfoAttribute(DefaultVisible = false, DisplayName = "Selected Route", SizeHint = "SelectedXX")] public string Route { get { return _route; } set { _route = value; } } private string _contra = "SMART"; [ColumnInfoAttribute(DefaultVisible = false, DisplayName = "Contra", SizeHint = "SelectedXX")] public string Contra { get { return _contra; } set { _contra = value; } } private bool _useCustomRoute = false; [Browsable(false)] public bool UseCustomRoute { get { return _useCustomRoute; } set { _useCustomRoute = value; } } private StreamingAlerts _mainAlerts; [Browsable(false)] public StreamingAlerts MainAlerts { get { return _mainAlerts; } set { _mainAlerts = value; StartStreamingAlerts(); } } private StreamingAlerts _exitAlerts; [Browsable(false)] public StreamingAlerts ExitAlerts { get { return _exitAlerts; } set { _exitAlerts = value; } } private IConnectionMaster _connectionMaster; [Browsable(false)] public IConnectionMaster ConnectionMaster { get { return _connectionMaster; } set { _connectionMaster = value; } } private bool _queueOrders = false; [Browsable(false)] public bool QueueOrders { get { return _queueOrders; } set { _queueOrders = value; } } private bool _useOrderThrottle = true; [Browsable(false)] public bool UseOrderThrottle { get { return _useOrderThrottle; } set { _useOrderThrottle = value; } } private bool _useSentOrderThrottling = true; /// /// Uses threshold of SENDING orders for order throttling rather than FILLING. True = sending, False = filling /// This corresponds to the drop down on the Edit Trading Strategy form. /// [Browsable(false)] public bool UseSentOrderThrottling { get { return _useSentOrderThrottling; } set { _useSentOrderThrottling = value; } } // entry order attributes private TradePositionSizing _positionSizing = TradePositionSizing.FixedDollars; [Browsable(false)] public TradePositionSizing PositionSizing { get { return _positionSizing; } set { _positionSizing = value; } } private decimal _positionSizeValue = GetXmlValue("POSITION_SIZE_VALUE", 10000.0); [ColumnInfoAttribute(Format = "0", DisplayName = "Position Size", DefaultVisible = false, SizeHint = "PositionXX")] public decimal PositionSizeValue { get { return _positionSizeValue; } set { _positionSizeValue = value; } } private decimal _targetOffset; [ColumnInfoAttribute(Format = "2", DisplayName = "Target Offset", DefaultVisible = false, SizeHint = "TargetXX")] public decimal TargetOffset { get { return _targetOffset; } set { _targetOffset = value; } } // Target Price - RVH20211227 private decimal _targetPrice; [ColumnInfoAttribute(Format = "2", DisplayName = "Target Price", DefaultVisible = false, SizeHint = "TargetXX")] public decimal TargetPrice { get { return _targetPrice; } set { _targetPrice = value; } } private TradeStopLossType _targetType = TradeStopLossType.None; [ColumnInfoAttribute(DisplayName = "Profit Target", DefaultVisible = false, SizeHint = "TargetXX")] public TradeStopLossType TargetType { get { return _targetType; } set { _targetType = value; } } private TradeStopLossType _stopLossType = TradeStopLossType.None; [ColumnInfoAttribute(DisplayName = "Stop Loss Type", DefaultVisible = false, SizeHint = "TypeXX")] public TradeStopLossType StopLossType { get { return _stopLossType; } set { _stopLossType = value; } } [ColumnInfoAttribute(DisplayName = "Stop Loss", DefaultVisible = false, SizeHint = "StopXX")] public string StopLossDisplay { get { if (_stopLossType == TradeStopLossType.Dollars) return "$" + _stopLossOffset.ToString("N2"); else if (_stopLossType == TradeStopLossType.Percent) return _stopLossOffset.ToString("N2") + "%"; else return ""; } } private TradeTimeStopType _timeStopType = TradeTimeStopType.None; /// /// Gets or sets the time stop type. /// [ColumnInfoAttribute(DisplayName = "Time Stop Type", DefaultVisible = false, SizeHint = "StopXX")] public TradeTimeStopType TimeStopType { get { return _timeStopType; } set { _timeStopType = value; } } private TradeEntryLimitReference _tradeEntryLimitReference = TradeEntryLimitReference.Last; [Browsable(false)] public TradeEntryLimitReference TradeEntryLimitReference { get { return _tradeEntryLimitReference; } set { _tradeEntryLimitReference = value; } } private int _timeStopMinutesAfterOpen = GetXmlValue("TIME_STOP_MINUTES_AFTER_OPEN", 375); [ColumnInfoAttribute(DisplayName = "Time Stop Minutes After Open", DefaultVisible = false, SizeHint = "MinutesXX")] public int TimeStopMinutesAfterOpen { get { return _timeStopMinutesAfterOpen; } set { _timeStopMinutesAfterOpen = value; } } private int _timeStopDuration = GetXmlValue("TIME_STOP_DURATION", 60); [ColumnInfoAttribute(DisplayName = "Time Stop Duration", DefaultVisible = false, SizeHint = "DurationXX")] public int TimeStopDuration { get { return _timeStopDuration; } set { _timeStopDuration = value; } } [ColumnInfoAttribute(DisplayName = "Time Stop", DefaultVisible = false, SizeHint = "MinutesXX", Format = "0")] public int? TimeStopDisplay { get { if (_timeStopType == TradeTimeStopType.TimeAfterSignal) return _timeStopDuration; else if (_timeStopType == TradeTimeStopType.TimeOfDay) return _timeStopMinutesAfterOpen; else return null; } } private TradeStopLossType _trailingStopType = TradeStopLossType.None; [ColumnInfoAttribute(Format = "textalignright", DisplayName = "Trailing Stop Type", DefaultVisible = false, SizeHint = "TrailingXX")] public TradeStopLossType TrailingStopType { get { return _trailingStopType; } set { _trailingStopType = value; } } private IBroker _broker = null; [ColumnInfoAttribute(DisplayName = "Broker", DefaultVisible = false, SizeHint = "BrokerXX")] [TypeConverter(typeof(BrokerConverter))] public IBroker Broker { get { return _broker; } set { _broker = value; } } private bool _usingProfiles = false; /// /// Gets or sets a value indicating whether strategy uses allocation profiles. True = using profiles, False = using direct to account method. /// /// /// true if [using profiles]; otherwise, false. /// [ColumnInfoAttribute(DisplayName = "Using Profiles", DefaultVisible = false, SizeHint = "AccountXX")] public bool UsingProfiles { get { return _usingProfiles; } set { _usingProfiles = value; } } /// /// Gets or sets the allocation profile. Only in use when UsingProfiles is set to true. /// /// /// The allocation profile. /// [ColumnInfoAttribute(DisplayName = "Allocation Profile", DefaultVisible = false, SizeHint = "AccountXX")] public string AllocationProfile { get; set; } = ""; /// /// The account name. Since the robot isn't connected to the broker when the layout is opened, we don't have /// a list of available accounts at that time. This records the account name to be used to map to the actual /// account once that list becomes available. /// private string _accountName = ""; /// /// The account name. Priority is _account but if not available then _accountName. /// [Browsable(false)] public string AccountName => null == Account ? _accountName : Account.Name; /// /// The account that this strategy should use when sending trades. /// [ColumnInfoAttribute(DisplayName = "Account", DefaultVisible = false, SizeHint = "AccountXX")] public Account Account { get; set; } = null; [Browsable(false)] public string LimitOrderFilter { get; set; } = ""; [Browsable(false)] public string EntryStopOrderFilter { get; set; } = ""; [Browsable(false)] public decimal LimitOrderOffset { get; set; } = GetXmlValue("LIMIT_ORDER_OFFSET", -0.02); /// /// How should the LimitOrderOffset numeric value be interpreted. Dollars or percent. /// [Browsable(false)] public TradeEntryLimitOffsetType LimitOffsetType { get; set; } = TradeEntryLimitOffsetType.Dollars; [Browsable(false)] public double LimitRelativeOffset { get; set; } private string _timeInForce = "DAY"; [Browsable(false)] public string TimeInForce { get { return _timeInForce; } set { _timeInForce = value; } } private int _limitGoodForSeconds = GetXmlValue("LIMIT_GOOD_FOR_SECONDS", 300); [Browsable(false)] public int LimitGoodForSeconds { get { return _limitGoodForSeconds; } set { _limitGoodForSeconds = value; } } // stop loss attributes private decimal _stopLossOffset; [Browsable(false)] public decimal StopLossOffset { get { return _stopLossOffset; } set { _stopLossOffset = value; } } // Stop Loss Price - RVH20211227 private decimal _stopLossPrice; [Browsable(false)] public decimal StopLossPrice { get { return _stopLossPrice; } set { _stopLossPrice = value; } } private string _stopLossFilter = "Price"; [Browsable(false)] public string StopLossFilter { get { return _stopLossFilter; } set { _stopLossFilter = value; } } // exit order attribute private bool _useLastAsReference = true; [Browsable(false)] public bool UseLastAsReference { get { return _useLastAsReference; } set { _useLastAsReference = value; } } // time stop attributes // trailing stop attributes private decimal _trailingStopOffset; [Browsable(false)] public decimal TrailingStopOffset { get { return _trailingStopOffset; } set { _trailingStopOffset = value; } } private string _trailingStopFilter = "Price"; [Browsable(false)] public string TrailingStopFilter { get { return _trailingStopFilter; } set { _trailingStopFilter = value; } } // target attributes private string _targetFilter = "Price"; [Browsable(false)] public string TargetFilter { get { return _targetFilter; } set { _targetFilter = value; } } private TradeStopLossType _scaleType = TradeStopLossType.None; [Browsable(false)] public TradeStopLossType ScaleType { get { return _scaleType; } set { _scaleType = value; } } private double _scaleStart; [Browsable(false)] public double ScaleStart { get { return _scaleStart; } set { _scaleStart = value; } } private string _scaleFilter = "Price"; [Browsable(false)] public string ScaleFilter { get { return _scaleFilter; } set { _scaleFilter = value; } } private bool _outsideRth; /// /// Used to specify whether an order should be filled Outside Regular Trading Hours. /// [Browsable(false)] public bool OutsideRth { get => _outsideRth; set => _outsideRth = value; } // TODO: exit alert private Dictionary _sentOrders = new Dictionary(); /// /// Keeps track of the number of orders sent per symbol for this strategy. /// [Browsable(false)] public Dictionary SentOrders { get { return _sentOrders; } set { _sentOrders = value; } } private HashSet _ordersBeingSent = new HashSet(); /// /// Keeps track of orders currently being sent. In the event that two alerts come in very close together /// this list can be checked to make sure an order isn't currently in progress. An instrument is added to /// this list at the start of and removed after orders are placed and the instrument is /// added to SentOrders. /// [Browsable(false)] public HashSet OrdersBeingSent { get { return _ordersBeingSent; } set { _ordersBeingSent = value; } } private HashSet _ordersSentTowardsMax = new HashSet(); /// /// Keeps track of orders sent. This is used to track if the MaxDailyOrder set in the strategy has been /// met. This list is checked in to determine if an order can be submitted. /// SentEntryOrders is not good to use for this purpose since some broker interfaces don't add to that list /// until broker callbacks are done using async processing. /// [Browsable(false)] public HashSet OrdersSentTowardsMax { get { return _ordersSentTowardsMax; } set { _ordersSentTowardsMax = value; } } private HashSet _entryOrders = new HashSet(); /// /// Set of all entry orders submitted for this strategy. /// [Browsable(false)] public HashSet EntryOrders { get { return _entryOrders; } set { _entryOrders = value; } } private HashSet _allOrders = new HashSet(); /// /// All orders submitted through this strategy. See also EntryOrders which is a subset of this. /// [Browsable(false)] public HashSet AllOrders { get { return _allOrders; } set { _allOrders = value; } } /// /// Positions associated with this trading strategy. /// private Dictionary _positions = new Dictionary(); public void UpdatePosition(Position position) { if (_positions.ContainsKey(position.Instrument)) _positions[position.Instrument] = position; else _positions.Add(position.Instrument, position); } private List _signals = new List(); [Browsable(false)] public List Signals { get { return _signals; } set { _signals = value; } } /// /// Keeps track of signals received by this strategy by symbol name /// private Dictionary> _signalsBySymbol = new Dictionary>(); private bool _oneDailyChancePerSymbol = true; [Browsable(false)] public bool OneDailyChancePerSymbol { get { return _oneDailyChancePerSymbol; } set { _oneDailyChancePerSymbol = value; } } private bool _allowDuplicateOrders = false; [Browsable(false)] public bool AllowDuplicateOrders { get { return _allowDuplicateOrders; } set { _allowDuplicateOrders = value; } } private int _customDisplaySize = 0; [Browsable(false)] public int CustomDisplaySize { get { return _customDisplaySize; } set { _customDisplaySize = value; } } private string _tags = ""; [Browsable(false)] public string Tags { get { return _tags; } set { _tags = value; } } private string _destination = ""; [Browsable(false)] public string Destination { get { return _destination; } set { _destination = value; } } /// /// Set of all entry orders submitted for this strategy. /// [ColumnInfoAttribute(DisplayName = "Sent Entry Orders", Format = "0", SizeHint = "OrdersXX", DefaultVisible = false)] public int SentEntryOrders { get { return _entryOrders.Count; } } /// /// Keeps track of orders sent. This is used to track if the MaxDailyOrder set in the strategy has been /// met. This list count is checked in to determine if an order can be submitted. /// SentEntryOrders is not good to use for this purpose since some broker interfaces don't add to that list /// until broker callbacks are done using async processing. /// [ColumnInfoAttribute(DisplayName = "Sent Orders Towards Max", Format = "0", SizeHint = "OrdersXX", DefaultVisible = false)] public int SentOrdersTowardsMax { get { return _ordersSentTowardsMax.Count; } } /// /// Set of all entry orders filled for this strategy. /// [ColumnInfoAttribute(DisplayName = "Filled Entry Orders", Format = "0", SizeHint = "OrdersXX", DefaultVisible = false)] public int FilledEntryOrders { get { var filledOrders = _entryOrders.ToList().Where(x => x.Status == TradeOrderStatus.Filled || x.Status == TradeOrderStatus.PartiallyFilled); return filledOrders.Count(); } } [Browsable(false)] public List ProfitBars = new List(); //[ColumnInfoAttribute(DisplayName = "Simulated Performance", Format = "thermograph", SizeHint = "XXXXXXXXXXXXXXXXXXXXXXX", DefaultVisible = true)] [Browsable(false)] public Thermograph OddsMakerThermograph { get; set; } private void AddProfitToBars(decimal profit) { double profitAsDouble = (double)profit; if (ProfitBars.Count == 0 || ProfitBars.Last().Date.ToString("yyyy-MM-dd hh:mm") != ServerFormats.Now.ToString("yyyy-MM-dd hh:mm")) { ProfitBars.Add(new Bar(ServerFormats.Now, profitAsDouble, profitAsDouble, profitAsDouble, profitAsDouble)); return; } Bar lastBar = ProfitBars.Last(); if (lastBar.High < profitAsDouble) lastBar.High = profitAsDouble; if (lastBar.Low > profitAsDouble) lastBar.Low = profitAsDouble; lastBar.Close = profitAsDouble; } private Dictionary _balancesOpen = new Dictionary(); private Dictionary _balancesClosed = new Dictionary(); private Dictionary _holdings = new Dictionary(); private Dictionary _columnKeys = new Dictionary(); public event NewAlertHandler onNewAlertReceived; public TradingStrategy(IConnectionMaster connectionMaster) { _connectionMaster = connectionMaster; } /// /// Tells the strategy about an execution associated with it for reporting purposes. /// /// public void AddExecution(TradeExecution execution) { Instrument instrument = execution.Instrument; decimal shares = execution.IsBuy ? execution.Shares : -Math.Abs(execution.Shares); decimal price = (decimal)execution.Price; decimal balance = -shares * price; if (_holdings.ContainsKey(instrument)) { decimal priorShares = _holdings[instrument]; decimal priorBalance = _balancesOpen[instrument]; _balancesOpen[instrument] += balance; _holdings[instrument] += shares; if ((priorShares < 0 && _holdings[instrument] > priorShares) || (priorShares > 0 && _holdings[instrument] < priorShares)) { decimal averageCostPerShare = priorBalance / priorShares; _balancesClosed[instrument] += balance - shares * averageCostPerShare; } if (_holdings[instrument] == 0) _balancesOpen[instrument] = 0; } else { _balancesOpen.Add(instrument, -shares * price); _balancesClosed.Add(instrument, 0); _holdings.Add(instrument, shares); } UpdateProfit(); } public static string MakeConfigStringEdits(string configString) { configString = ModifyConfigString(configString, "Price"); configString = ModifyConfigString(configString, "SmartStopD"); return configString; } /// /// By default ensures that price is displayed as a column in the strategy. Use the filter parameter to specify another filter. /// /// /// /// public static string ModifyConfigString(string configString, string filter = "Price") { bool filterFound = false; int maxShowNumber = -1; try { NameValueCollection parameters = HttpUtility.ParseQueryString(configString); foreach (string key in parameters.AllKeys) { if (key.StartsWith("show") && parameters[key] != "") { int showNumber = int.Parse(key.Replace("show", "")); if (showNumber > maxShowNumber) maxShowNumber = showNumber; if (parameters[key] == filter) filterFound = true; } } } catch { } maxShowNumber++; if (!filterFound) configString += "&show" + maxShowNumber + "=" + filter; return configString; } public static bool ColumnIsShown(string configString, string filter) { NameValueCollection parameters = HttpUtility.ParseQueryString(configString); bool filterFound = false; foreach (string key in parameters.AllKeys) { if (key.StartsWith("show") && parameters[key] != "") { if (parameters[key] == filter) filterFound = true; } } return filterFound; } public void UpdateProfit() { _openProfit = 0; _closedProfit = 0; foreach (Instrument instrument in _balancesClosed.Keys.ToList()) { decimal currentShares = _holdings[instrument]; decimal openBalance = _balancesOpen[instrument]; decimal currentWorth = instrument.Last * currentShares; if (instrument.Last == 0) currentWorth = -openBalance; if (currentShares != 0) _openProfit += currentWorth + openBalance; _closedProfit += _balancesClosed[instrument]; } } /// /// Controls whether initial history for today's trades has been retrieved. /// private bool _historyComplete = false; private bool _historyRunning = false; private List _historyQueue = new List(); private History _history = null; /// /// Alert Streaming is only started if users are authorized. /// private void StartStreamingAlerts() { // Allow streaming alerts only for premium and above user subscriptions. if (!(GuiEnvironment.Authorizations.ContainsKey("oddsmaker") && GuiEnvironment.Authorizations["oddsmaker"])) return; if ( null != _mainAlerts && _mainAlerts.Config != "") { DateTime now = GuiEnvironment.GetNowInMarketTimeZone(); DateTime marketOpen = GuiEnvironment.GetMarketOpenInMarketTimeZone(); if (!_historyComplete && now > marketOpen) LoadHistory(); else { _mainAlerts.Stop(); _mainAlerts.StreamingAlertsData -= _mainAlerts_StreamingAlertsData; _mainAlerts.StreamingAlertsConfig -= _mainAlerts_StreamingAlertsConfig; _mainAlerts.StreamingAlertsData += new StreamingAlertsData(_mainAlerts_StreamingAlertsData); _mainAlerts.StreamingAlertsConfig += new StreamingAlertsConfig(_mainAlerts_StreamingAlertsConfig); _mainAlerts.Start(); StreamingAlertsStarted = true; } } } [Browsable(false)] public bool StreamingAlertsStarted { get; set; } private bool _chartLongStrategy = false; /// /// Default strategy for long trades on a chart /// [ColumnInfoAttribute(Format = "onoff", DisplayName = "Default Chart Long Strategy", SizeHint = "FALSEXX")] public bool ChartLongStrategy { get { return _chartLongStrategy; } set { _chartLongStrategy = value; } } private bool _chartShortStrategy = false; /// /// Default strategy for short trades on a chart /// [ColumnInfoAttribute(Format = "onoff", DisplayName = "Default Chart Short Strategy", SizeHint = "FALSEXX")] public bool ChartShortStrategy { get { return _chartShortStrategy; } set { _chartShortStrategy = value; } } private void LoadHistory() { HistoryRequest historyRequest = new HistoryRequest(); historyRequest.Config = _mainAlerts.Config; historyRequest.EndTime = GuiEnvironment.GetMarketOpenLocalTime(); historyRequest.MaxCount = 100; historyRequest.StartTime = null; _history = _connectionMaster.HistoryManager.GetHistory(historyRequest); _history.HistoryData += _history_HistoryData; _history.HistoryStatus += _history_HistoryStatus; _historyQueue.Clear(); _historyRunning = true; GuiEnvironment.LogMessage("[TradingStrategy] Starting history request for strategy " + _name); _history.Start(); } void _history_HistoryStatus(History sender) { _historyRunning = sender.HistoryDisposition != HistoryDisposition.Done && sender.HistoryDisposition != HistoryDisposition.NotSent; // we used to respond to the MoreAvailable even to keep going, but that seems like a bad idea. We've seen people try to load // a very chatty alert window and the program crashes. I say 1000 historical events is enough. if (sender.HistoryDisposition == HistoryDisposition.MoreAvailable) { _history.Stop(); LoadHistoryQueue(); } if (sender.HistoryDisposition == HistoryDisposition.Done) LoadHistoryQueue(); } private void LoadHistoryQueue() { try { int queuedAlerts = _historyQueue.Count; GuiEnvironment.LogMessage("[TradingStrategy] Starting processing " + queuedAlerts + " alerts from strategy " + _name); List positions = new List(); int todaysTrades = 0; foreach (RowData alert in _historyQueue.ToList()) { string symbol = alert.GetSymbol(); TradeDecision decision = BrokerManager.ShouldTakeTrade(alert, this, positions, true); if (decision.TakeTrade) { if (!_signalsBySymbol.ContainsKey(symbol)) _signalsBySymbol.Add(symbol, new HashSet()); HashSet signals = _signalsBySymbol[symbol]; Signal signal = GetSignal(alert); signals.Add(signal); LastSymbol = alert.GetSymbol(); LastSignalTime = alert.GetTime(); LastSignalPrice = (decimal)alert.GetPrice(); todaysTrades++; } } _historyQueue.Clear(); _historyComplete = true; GuiEnvironment.LogMessage("[TradingStrategy] Done processing " + todaysTrades + " trades out of " + queuedAlerts + " alerts from strategy " + _name); StartStreamingAlerts(); } catch (Exception e) { string debugView = e.StackTrace; } } public bool SignaledToday(string symbol) { return _signalsBySymbol.ContainsKey(symbol); } private Signal GetSignal(RowData rowData) { Signal signal = new TIProAutoTradeExtension.Signal(); signal.Symbol = rowData.GetSymbol(); signal.Strategy = this; signal.RowData = rowData; return signal; } void _history_HistoryData(List alerts, History sender) { foreach (RowData rowData in alerts) { _historyQueue.Insert(0, rowData); } } void _mainAlerts_StreamingAlertsConfig(StreamingAlerts source) { string debugView = source.Columns.ToString(); _columnKeys.Clear(); foreach (ColumnInfo column in source.Columns.ToList()) { string rowDataKey = column.WireName; string internalCode = column.InternalCode; if (!_columnKeys.ContainsKey(internalCode)) _columnKeys.Add(internalCode, rowDataKey); } } public string GetRowDataKey(string internalCode) { if (_columnKeys.ContainsKey(internalCode)) return _columnKeys[internalCode]; else { if (internalCode.StartsWith("c_")) return internalCode; else return "c_" + internalCode; } } /// /// Constructor using xml generated by TradingStrategy.Save(). /// /// Connection master reference /// Xml public TradingStrategy(IConnectionMaster connectionMaster, XmlNode xml) { StreamingAlertsStarted = false; _connectionMaster = connectionMaster; _id = xml.Property("ID", ""); if (_id == "") _id = GenerateId(); _name = xml.Property("NAME", ""); _long = xml.Property("LONG", true); _cfdEnabled = xml.Property("CFD_ENABLED", false); // MAIN_ALERTS and EXIT_ALERTS are premium and above features. // As of this change the oddsmaker authorization is equivalent to the premium plan. // We will initialize _mainAlerts if authorization is not complete. Once authorization is complete we apply autorization logic. if (GuiEnvironment.Authorizations.Count == 0 || (GuiEnvironment.Authorizations.ContainsKey("oddsmaker") && GuiEnvironment.Authorizations["oddsmaker"])) { string mainAlertsConfig = xml.Property("MAIN_ALERTS", ""); if (mainAlertsConfig != "") { mainAlertsConfig = MakeConfigStringEdits(mainAlertsConfig); _mainAlerts = _connectionMaster.StreamingAlertsManager.GetAlerts(mainAlertsConfig); StartStreamingAlerts(); } string exitAlertsConfig = xml.Property("EXIT_ALERTS", ""); if (exitAlertsConfig != "") { _exitAlerts = _connectionMaster.StreamingAlertsManager.GetAlerts(exitAlertsConfig); _exitAlerts.StreamingAlertsData += new StreamingAlertsData(_exitAlerts_StreamingAlertsData); _exitAlerts.Start(); } } _usingProfiles = xml.Property("USING_PROFILES", false); AllocationProfile = xml.Property("ALLOCATION_PROFILE", ""); _accountName = xml.Property("ACCOUNT_NAME", ""); // Rename Simulated Trading Default to TI Paper Trading if (_accountName == "Simulated Trading Default") _accountName = "TI Paper Trading"; _queueOrders = xml.Property("QUEUE_ORDERS", false); if (xml.Property("START_TIME_MINUTES_AFTER_OPEN").Count() > 0) { _startTimeMinutesFromOpen = xml.Property("START_TIME_MINUTES_AFTER_OPEN", GetXmlValue("START_TIME_MINUTES_AFTER_OPEN", 5)); StartTime = GuiEnvironment.GetMarketOpenLocalTime().AddMinutes(_startTimeMinutesFromOpen); } else if (xml.Property("START_TIME").Count() > 0) { DateTime startTime; DateTime.TryParse(xml.Property("START_TIME", GuiEnvironment.GetMarketOpenLocalTime().ToString()), out startTime); int minutesFromOpen = (int)(startTime - GuiEnvironment.GetMarketOpenLocalTime()).TotalMinutes; int minutesFromClose = (int)(startTime - GuiEnvironment.GetMarketCloseLocalTime()).TotalMinutes; if (minutesFromOpen < 0) StartTime = GuiEnvironment.GetMarketOpenLocalTime(); else if (minutesFromClose > 0) StartTime = GuiEnvironment.GetMarketCloseLocalTime(); else StartTime = startTime; } else StartTime = GuiEnvironment.GetMarketOpenLocalTime(); if (xml.Property("END_TIME_MINUTES_BEFORE_CLOSE").Count() > 0) { _endTimeMinutesFromClose = xml.Property("END_TIME_MINUTES_BEFORE_CLOSE", GetXmlValue("END_TIME_MINUTES_BEFORE_CLOSE", 15)); EndTime = GuiEnvironment.GetMarketCloseLocalTime().AddMinutes(-_endTimeMinutesFromClose); } else if (xml.Property("END_TIME").Count() > 0) { DateTime endTime; DateTime.TryParse(xml.Property("END_TIME", GuiEnvironment.GetMarketCloseLocalTime().ToString()), out endTime); int minutesFromOpen = (int)(endTime - GuiEnvironment.GetMarketOpenLocalTime()).TotalMinutes; int minutesFromClose = (int)(endTime - GuiEnvironment.GetMarketCloseLocalTime()).TotalMinutes; if (minutesFromOpen < 0) EndTime = GuiEnvironment.GetMarketOpenLocalTime(); else if (minutesFromClose > 0) EndTime = GuiEnvironment.GetMarketCloseLocalTime(); else EndTime = endTime; } else EndTime = GuiEnvironment.GetMarketCloseLocalTime(); _maxDailyOrders = xml.Property("MAX_DAILY_ORDERS", GetXmlValue("MAX_DAILY_ORDERS", 10)); _chartLongStrategy = xml.Property("CHART_LONG_DEFAULT", false); _chartShortStrategy = xml.Property("CHART_SHORT_DEFAULT", false); _useOrderThrottle = xml.Property("USE_ORDER_THROTTLE", true); if (_useOrderThrottle) _useSentOrderThrottling = xml.Property("USE_SENT_ORDER_THROTTLE", true); else _useSentOrderThrottling = true; _useCustomRoute = xml.Property("USE_CUSTOM_ROUTE", false); _route = xml.Property("ROUTE", "SMART"); _contra = xml.Property("CONTRA", "SMART"); _positionSizing = xml.PropertyEnum("TRADE_POSITION_SIZING", TradePositionSizing.FixedShares); _positionSizeValue = (decimal)xml.Property("POSITION_SIZE_VALUE", GetXmlValue("POSITION_SIZE_VALUE", 10000)); _tradeEntryType = xml.PropertyEnum("TRADE_ENTRY_TYPE", TradeEntryType.Market); _tradeEntryLimitReference = xml.PropertyEnum("LIMIT_REFERENCE", TradeEntryLimitReference.Last); LimitOrderOffset = (decimal)xml.Property("LIMIT_ORDER_OFFSET", (double)GetXmlValue("LIMIT_ORDER_OFFSET", -0.02)); LimitOffsetType = xml.PropertyEnum("LIMIT_OFFSET_TYPE", TradeEntryLimitOffsetType.Dollars); LimitRelativeOffset = xml.Property("LIMIT_RELATIVE_OFFSET", 0.00); LimitOrderFilter = xml.Property("LIMIT_ORDER_FILTER"); EntryStopOrderFilter = xml.Property("ENTRY_STOP_ORDER_FILTER"); _timeInForce = xml.Property("TIME_IN_FORCE", "DAY"); _limitGoodForSeconds = xml.Property("LIMIT_GOOD_FOR_SECONDS", GetXmlValue("LIMIT_GOOD_FOR_SECONDS", 120)); _stopLossType = xml.PropertyEnum("TRADE_STOP_LOSS_TYPE", TradeStopLossType.None); _stopLossOffset = (decimal)xml.Property("STOP_LOSS_OFFSET", 0.00); _stopLossFilter = xml.Property("STOP_LOSS_FILTER", ""); _useLastAsReference = xml.Property("USE_LAST_AS_REFERENCE", true); _timeStopType = xml.PropertyEnum("TRADE_TIME_STOP_TYPE", TradeTimeStopType.None); _timeStopMinutesAfterOpen = xml.Property("TIME_STOP_MINUTES_AFTER_OPEN", GetXmlValue("TIME_STOP_MINUTES_AFTER_OPEN", 375)); _timeStopDuration = xml.Property("TIME_STOP_DURATION", GetXmlValue("TIME_STOP_DURATION", 60)); _timeStopDays = xml.Property("TIME_STOP_DAYS", 0); _trailingStopType = xml.PropertyEnum("TRADE_TRAILING_STOP_TYPE", TradeStopLossType.None); // StopLoss and TrailingStop are mutually exclusive, with StopLoss as the preferred approach. if (_stopLossType != TradeStopLossType.None) _trailingStopType = TradeStopLossType.None; _trailingStopOffset = (decimal)xml.Property("TRAILING_STOP_OFFSET", 0.00); _trailingStopFilter = xml.Property("TRAILING_STOP_FILTER", ""); _targetType = xml.PropertyEnum("TRADE_TARGET_TYPE", TradeStopLossType.None); _targetOffset = (decimal)xml.Property("TARGET_OFFSET", 0.00); _targetFilter = xml.Property("TARGET_FILTER", ""); _scaleType = xml.PropertyEnum("SCALE_TYPE", TradeStopLossType.None); _scaleStart = xml.Property("SCALE_START", 0); _scaleFilter = xml.Property("SCALE_FILTER", ""); _oneDailyChancePerSymbol = xml.Property("ONE_DAILY_CHANCE_PER_SYMBOL", true); _allowDuplicateOrders = xml.Property("ALLOW_DUPLICATE_ORDERS", false); _customDisplaySize = xml.Property("CUSTOM_DISPLAY_SIZE", 0); _tags = xml.Property("TAGS", ""); _destination = xml.Property("DESTINATION", ""); } public TradingStrategy(TradingStrategy strategy) { _connectionMaster = strategy.ConnectionMaster; _id = GenerateId(); _name = strategy.Name; _cfdEnabled = strategy.CfdEnabled; _useLastAsReference = strategy.UseLastAsReference; _long = strategy.Long; _mainAlerts = strategy.MainAlerts; _exitAlerts = strategy.ExitAlerts; _queueOrders = strategy.QueueOrders; StartTime = strategy.StartTime; EndTime = strategy.EndTime; _useCustomRoute = strategy.UseCustomRoute; _route = strategy.Route; _contra = strategy.Contra; _maxDailyOrders = strategy.MaxDailyOrders; _useOrderThrottle = strategy.UseOrderThrottle; _useSentOrderThrottling = strategy.UseSentOrderThrottling; _positionSizing = strategy.PositionSizing; _positionSizeValue = strategy.PositionSizeValue; _tradeEntryType = strategy.TradeEntryType; _tradeEntryLimitReference = strategy.TradeEntryLimitReference; LimitOrderFilter = strategy.LimitOrderFilter; EntryStopOrderFilter = strategy.EntryStopOrderFilter; LimitOrderOffset = strategy.LimitOrderOffset; LimitOffsetType = strategy.LimitOffsetType; LimitRelativeOffset = strategy.LimitRelativeOffset; _limitGoodForSeconds = strategy.LimitGoodForSeconds; _stopLossType = strategy.StopLossType; _stopLossOffset = strategy.StopLossOffset; _stopLossFilter = strategy.StopLossFilter; _timeStopType = strategy.TimeStopType; _timeStopMinutesAfterOpen = strategy.TimeStopMinutesAfterOpen; _timeStopDuration = strategy.TimeStopDuration; _timeStopDays = strategy.TimeStopDays; _trailingStopType = strategy.TrailingStopType; // StopLoss and TrailingStop are mutually exclusive, with StopLoss as the preferred approach. if (_stopLossType != TradeStopLossType.None) _trailingStopType = TradeStopLossType.None; _trailingStopOffset = strategy.TrailingStopOffset; _trailingStopFilter = strategy.TrailingStopFilter; _targetType = strategy.TargetType; _targetOffset = strategy.TargetOffset; _targetFilter = strategy.TargetFilter; _scaleType = strategy.ScaleType; _scaleStart = strategy.ScaleStart; _scaleFilter = strategy.ScaleFilter; _oneDailyChancePerSymbol = strategy.OneDailyChancePerSymbol; _allowDuplicateOrders = strategy.AllowDuplicateOrders; _customDisplaySize = strategy.CustomDisplaySize; Account = strategy.Account; AllocationProfile = strategy.AllocationProfile; _usingProfiles = strategy.UsingProfiles; _tags = strategy.Tags; _destination = strategy.Destination; } /// /// Return unique string to uniquely identify trading strategies. /// /// public static string GenerateId() { return Guid.NewGuid().ToString(); } public override string ToString() { return _name; } public RowData ToRowData() { if (Id == "1" || Id == "-1") { RowData rowData = new RowData(); rowData.Data.Add("Name", _name); rowData.Data.Add("Long", _long); rowData.Data.Add("OpenProfit", _openProfit); rowData.Data.Add("ClosedProfit", _closedProfit); rowData.Data.Add("TotalProfit", TotalProfit); rowData.Data.Add("TradesSoFarToday", _tradesSoFarToday); rowData.Data.Add("TradingEnabled", true); rowData.Data.Add("AdvancedExitsEnabled", this); rowData.Data.Add("object", this); return rowData; } else { RowData rowData = RowDataHelper.GetRowDataFromObject(this); return rowData; } } public XmlNode Save(XmlNode parent) { XmlNode description = parent.NewNode("TRADING_STRATEGY"); try { description.SetProperty("NAME", _name); description.SetProperty("LONG", _long); description.SetProperty("CFD_ENABLED", _cfdEnabled); // MAIN_ALERTS and EXIT_ALERTS are premium and above features. // As of this change the oddsmaker authorization is equivalent to the premium plan. if (GuiEnvironment.Authorizations.ContainsKey("oddsmaker") && GuiEnvironment.Authorizations["oddsmaker"]) { if (null != _mainAlerts) description.SetProperty("MAIN_ALERTS", _mainAlerts.Config); if (null != _exitAlerts) description.SetProperty("EXIT_ALERTS", _exitAlerts.Config); } description.SetProperty("QUEUE_ORDERS", _queueOrders); description.SetProperty("ID", _id); description.SetProperty("START_TIME_MINUTES_AFTER_OPEN", _startTimeMinutesFromOpen); description.SetProperty("END_TIME_MINUTES_BEFORE_CLOSE", _endTimeMinutesFromClose); description.SetProperty("USE_CUSTOM_ROUTE", _useCustomRoute); description.SetProperty("ROUTE", _route); description.SetProperty("CONTRA", _contra); description.SetProperty("MAX_DAILY_ORDERS", _maxDailyOrders); description.SetProperty("USE_ORDER_THROTTLE", _useOrderThrottle); description.SetProperty("CHART_LONG_DEFAULT", _chartLongStrategy); description.SetProperty("CHART_SHORT_DEFAULT", _chartShortStrategy); if (_useOrderThrottle) description.SetProperty("USE_SENT_ORDER_THROTTLE", _useSentOrderThrottling); else description.SetProperty("USE_SENT_ORDER_THROTTLE", true); description.SetProperty("TRADE_POSITION_SIZING", _positionSizing); description.SetProperty("POSITION_SIZE_VALUE", _positionSizeValue); description.SetProperty("TRADE_ENTRY_TYPE", _tradeEntryType); description.SetProperty("LIMIT_REFERENCE", _tradeEntryLimitReference); description.SetProperty("LIMIT_ORDER_OFFSET", LimitOrderOffset); description.SetProperty("LIMIT_OFFSET_TYPE", LimitOffsetType); description.SetProperty("LIMIT_ORDER_FILTER", LimitOrderFilter); description.SetProperty("ENTRY_STOP_ORDER_FILTER", EntryStopOrderFilter); description.SetProperty("LIMIT_RELATIVE_OFFSET", LimitRelativeOffset); description.SetProperty("TIME_IN_FORCE", _timeInForce); description.SetProperty("LIMIT_GOOD_FOR_SECONDS", _limitGoodForSeconds); description.SetProperty("TRADE_STOP_LOSS_TYPE", _stopLossType); description.SetProperty("STOP_LOSS_OFFSET", _stopLossOffset); description.SetProperty("STOP_LOSS_FILTER", _stopLossFilter); description.SetProperty("USE_LAST_AS_REFERENCE", _useLastAsReference); description.SetProperty("TRADE_TIME_STOP_TYPE", _timeStopType); description.SetProperty("TIME_STOP_MINUTES_AFTER_OPEN", _timeStopMinutesAfterOpen); description.SetProperty("TIME_STOP_DURATION", _timeStopDuration); description.SetProperty("TIME_STOP_DAYS", _timeStopDays); description.SetProperty("TRADE_TRAILING_STOP_TYPE", _trailingStopType); description.SetProperty("TRAILING_STOP_OFFSET", _trailingStopOffset); description.SetProperty("TRAILING_STOP_FILTER", _trailingStopFilter); description.SetProperty("TRADE_TARGET_TYPE", _targetType); description.SetProperty("TARGET_OFFSET", _targetOffset); description.SetProperty("TARGET_FILTER", _targetFilter); description.SetProperty("SCALE_TYPE", _scaleType); description.SetProperty("SCALE_START", _scaleStart); description.SetProperty("SCALE_FILTER", _scaleFilter); description.SetProperty("ONE_DAILY_CHANCE_PER_SYMBOL", _oneDailyChancePerSymbol); description.SetProperty("ALLOW_DUPLICATE_ORDERS", _allowDuplicateOrders); description.SetProperty("CUSTOM_DISPLAY_SIZE", _customDisplaySize); description.SetProperty("TAGS", _tags); description.SetProperty("USING_PROFILES", _usingProfiles); description.SetProperty("ALLOCATION_PROFILE", AllocationProfile); if (null != Account) { // Only saved the old name for backwards conpatibility. var accountName = (Account.Name == "TI Paper Trading") ? "Simulated Trading Default" : Account.Name; description.SetProperty("ACCOUNT_NAME", accountName); } else if (!String.IsNullOrEmpty(_accountName)) description.SetProperty("ACCOUNT_NAME", _accountName); description.SetProperty("DESTINATION", _destination); } catch (Exception e) { string debugView = e.ToString(); } return description; } void _exitAlerts_StreamingAlertsData(List data, StreamingAlerts source) { // TODO } private HashSet _symbolsToday = new HashSet(); void _mainAlerts_StreamingAlertsData(List data, StreamingAlerts source) { ProcessAlerts(data); } /// /// Processes RowData list. /// /// List of RowData private void ProcessAlerts(List data) { foreach (RowData rowData in data) { ProcessAlert(rowData); } } /// /// Processes alert from RowData instance. This can come from an alert strategy or generated manually as /// the "Trade This" menu items do. /// /// public void ProcessAlert(RowData rowData) { if (rowData.GetAsString(CommonAlertFields.HISTORICAL) != "1") { if (rowData.GetSymbol() != "") { string symbol = rowData.GetSymbol(); if (!_symbolsToday.Contains(symbol) || !OneDailyChancePerSymbol) { LastSymbol = symbol; double price = rowData.GetPrice(0.0); if (price != 0.0) LastSignalPrice = (decimal)price; if (rowData.GetTime().HasValue) LastSignalTime = rowData.GetTime().Value; else LastSignalTime = ServerFormats.Now; } if (!_symbolsToday.Contains(symbol)) _symbolsToday.Add(symbol); } if (null != onNewAlertReceived) onNewAlertReceived(rowData, this); } } private OddsMakerRequest GetOddsMakerRequest() { OddsMakerRequest request = new OddsMakerRequest(); request.SuccessDirectionUp = _long; request.DaysOfTest = GuiEnvironment.GetGeneralInfo(GuiEnvironment.FindConnectionMaster("").SendManager).GetOddsMakerDays(); request.SkipDays = 0; if (null != _mainAlerts) request.EntryCondition = _mainAlerts.Config; else return null; request.EntryTimeStart = (int)(_startTime - GuiEnvironment.GetMarketOpenLocalTime()).TotalMinutes; request.EntryTimeEnd = -(int)(_endTime - GuiEnvironment.GetMarketCloseLocalTime()).TotalMinutes; request.Location = SelectedLocation.US; request.SuccessTypePercent = _stopLossType == TradeStopLossType.Percent || _targetType == TradeStopLossType.Percent; request.SuccessMinMove = 0.01; if (_targetType == TradeStopLossType.Dollars || _targetType == TradeStopLossType.Percent) request.ProfitTarget = (double)_targetOffset; else if (_targetType == TradeStopLossType.Filter && _targetFilter != "") { request.UseTargetFilter = true; request.TargetFilter = _targetFilter; } if (_stopLossType == TradeStopLossType.Dollars || _stopLossType == TradeStopLossType.Percent) request.StopLoss = (double)_stopLossOffset; else if (_stopLossType == TradeStopLossType.Filter && _stopLossFilter != "") { request.UseStopFilter = true; request.StopFilter = _stopLossFilter; } request.StopLossWiggle = false; if (_timeStopType == TradeTimeStopType.TimeAfterSignal) { request.TimeoutType = TimeoutType.MinutesAfterEntry; request.TimeoutMinutes = _timeStopDuration; } else if (_timeStopType == TradeTimeStopType.TimeOfDay) { if (_timeStopMinutesAfterOpen == 0 && _timeStopDays > 0) { request.TimeoutType = TimeoutType.Open; request.AtOpenDays = _timeStopDays; } else if (_timeStopDays > 0 && _timeStopMinutesAfterOpen >= 385) { request.TimeoutType = TimeoutType.FutureClose; request.AtCloseDays = _timeStopDays; } else { request.TimeoutType = TimeoutType.Close; request.BeforeCloseMinutes = 390 - _timeStopMinutesAfterOpen; } } request.XmlMode = true; if (null != _exitAlerts) { request.ExitConditionAlert = _exitAlerts.Config; request.ExitConditionType = ExitConditionType.Alert; } else if (_trailingStopType == TradeStopLossType.Percent) { request.ExitConditionType = ExitConditionType.TrailingPercent; request.ExitConditionTrailingStop = (double)_trailingStopOffset; } else request.ExitConditionType = ExitConditionType.None; request.RequestCsvFile = true; return request; } public void RunOddsMaker() { if (!_oddsMakerIsRunning) { _oddsMakerIsRunning = true; OddsMakerRequest request = GetOddsMakerRequest(); OddsMaker oddsMaker = _connectionMaster.OddsMakerManager.GetOddsMaker(request); oddsMaker.OddsMakerProgressXml += oddsMaker_OddsMakerProgressXml; oddsMaker.OddsMakerCSV += new OddsMakerCSV(oddsMaker_OddsMakerCSV); oddsMaker.OddsMakerStatus += oddsMaker_OddsMakerStatus; if (oddsMaker.CanStart()) { _oddsMakerCsv = ""; oddsMaker.Start(); } else _oddsMakerIsRunning = false; } } void oddsMaker_OddsMakerProgressXml(XmlNode progress, OddsMaker sender) { double percentComplete = progress.Property("PERCENT_COMPLETE", -1.0); if (percentComplete != -1.0) _oddsMakerProgress = percentComplete; } void oddsMaker_OddsMakerStatus(OddsMaker sender) { if (sender.OddsMakerDisposition == OddsMakerDisposition.Done || sender.OddsMakerDisposition == OddsMakerDisposition.NotStarted) _oddsMakerIsRunning = false; else if (sender.OddsMakerDisposition == OddsMakerDisposition.Working) _oddsMakerIsRunning = true; if (sender.OddsMakerDisposition == OddsMakerDisposition.Done) { if (_oddsMakerCsv == "") { _winrate = null; _omTrades = 0; _profitFactor = null; } _oddsMakerProgress = 100; _oddsMakerIsRunning = false; } } private void oddsMaker_OddsMakerCSV(string csv, OddsMaker sender) { try { _oddsMakerCsv = csv; TextReader textReader = new StringReader(csv); LumenWorks.Framework.IO.Csv.CsvReader csvReader = new LumenWorks.Framework.IO.Csv.CsvReader(textReader, true); List tradeRecords = new List(); while (csvReader.ReadNextRecord()) { OddsMakerTradeRecord tradeRecord = OddsMakerResults.GetTradeFromCsvRecord(csvReader, _long, _stopLossType != TradeStopLossType.None, _targetType != TradeStopLossType.None); if (null != tradeRecord) tradeRecords.Add(tradeRecord); } _omTrades = tradeRecords.Count; int winningTrades = tradeRecords.Count(x => x.ProfitDollars() > 0); int losingTrades = tradeRecords.Count(x => x.ProfitDollars() <= 0); double winningProfit = 0; double averageWinner = 0; if (winningTrades > 0) { winningProfit = tradeRecords.Where(x => x.ProfitDollars() > 0).Sum(w => w.ProfitDollars()); averageWinner = tradeRecords.Where(x => x.ProfitDollars() > 0).Average(w => w.ProfitDollars()); } double losingProfit = 0; double averageLoser = 0; if (tradeRecords.Where(x => x.ProfitDollars() < 0).Count() > 0) { losingProfit = Math.Abs(tradeRecords.Where(x => x.ProfitDollars() < 0).Sum(w => w.ProfitDollars())); averageLoser = tradeRecords.Where(x => x.ProfitDollars() < 0).Average(w => w.ProfitDollars()); } _winrate = Math.Round((double)winningTrades / (double)_omTrades * 100, 1); double loserate = 100 - _winrate.Value; double avgWinnerLoserRatio = 3; if (Math.Abs(averageLoser) > 0) avgWinnerLoserRatio = averageWinner / Math.Abs(averageLoser); _profitFactor = Math.Round(winningProfit / losingProfit, 2); var dailyResults = from n in tradeRecords group n by n.EntryTime.ToString("yyyy-MM-dd") into g select new { YMD = g.Key, Day = g.First().EntryTime, NumTrades = g.Count(), Profit = g.Sum(x => x.ProfitDollars()), Winners = g.Count(x => x.ProfitDollars() > 0), WinningProfit = g.Where(x => x.ProfitDollars() > 0).Sum(x => x.ProfitDollars()), LosingProfit = Math.Abs(g.Where(x => x.ProfitDollars() < 0).Sum(x => x.ProfitDollars())), WinRate = Math.Round((double)g.Count(x => x.ProfitDollars() > 0) / (double)g.Count() * 100, 1), AvgGainPercent = g.Average(x => x.ProfitPercent()), AvgGainDollars = g.Average(x => x.ProfitDollars()), TotalPercent = g.Sum(x => x.ProfitPercent()), TotalDollars = g.Sum(x => x.ProfitDollars()) }; if (dailyResults.Count() > 0) { double maxDailyProfit = dailyResults.Max(x => x.Profit); double minDailyProfit = dailyResults.Min(x => x.Profit); double maximum = Math.Max(Math.Abs(maxDailyProfit), Math.Abs(minDailyProfit)); List dailyProfits = new List(); DateTime firstDayWithTrades = dailyResults.First().Day; DateTime dateAfterLastTrade = dailyResults.Last().Day.AddDays(1); DateTime endDate = new DateTime(dateAfterLastTrade.Year, dateAfterLastTrade.Month, dateAfterLastTrade.Day, 0, 0, 0); DateTime day = firstDayWithTrades; while (day <= endDate) { var possibleDailyResultSet = dailyResults.Where(x => x.Day.ToString("yyyy-MM-dd") == day.ToString("yyyy-MM-dd")); if (day.DayOfWeek != DayOfWeek.Sunday && day.DayOfWeek != DayOfWeek.Saturday) { if (possibleDailyResultSet.Count() > 0) dailyProfits.Add(possibleDailyResultSet.First().Profit); else dailyProfits.Add(0); } day = day.AddDays(1); } OddsMakerThermograph = new Thermograph(); OddsMakerThermograph.Max = maximum; OddsMakerThermograph.Min = -maximum; OddsMakerThermograph.Mid = 0; OddsMakerThermograph.Values = dailyProfits; } _oddsMakerIsRunning = false; } catch (Exception e) { string debugView = e.Message; } } private static int GetXmlValue(string key, int def) { return GuiEnvironment.XmlConfig.Node("ROBOT").Node(key).Property("VALUE", def); } private static decimal GetXmlValue(string key, double def) { return (decimal)GuiEnvironment.XmlConfig.Node("ROBOT").Node(key).Property("VALUE", def); } /// /// Returns true if all filters required have values in the rowData signal object or if no filter values are /// required by this strategy. Returns false if filter values are required and aren't included in the rowData. /// /// Trade signal. /// internal bool RequiredFiltersAvailable(RowData rowData) { if (!AnyFiltersRequired()) return true; return FilterValuesNeeded(rowData).Count == 0; } /// /// Examines rowData and returns a list of filter values that are required to execute this strategy. For example, if /// the stop is based on the SmartStopD and rowData doesn't contain a value for SmartStopD, then this function will /// return a List with a single value SmartStopD. /// /// Trade signal /// List of strings of filter values required. public List FilterValuesNeeded(RowData rowData) { List requiredFilters = FiltersRequired(); List needFilterValues = new List(); foreach (string filter in requiredFilters) { double filterValue = rowData.GetAsDouble("c_" + filter, double.NaN); if (double.IsNaN(filterValue) && !needFilterValues.Contains(filter)) needFilterValues.Add(filter); } return needFilterValues; } private bool AnyFiltersRequired() { return FiltersRequired().Count > 0; } private List FiltersRequired() { var requiredFilters = new List(); if (TradeEntryLimitReference == TradeEntryLimitReference.Filter) requiredFilters.Add(LimitOrderFilter); if ((TradeEntryType == TradeEntryType.StopLimit && EntryStopOrderFilter != "") || (TradeEntryType == TradeEntryType.Stop && EntryStopOrderFilter != "")) requiredFilters.Add(EntryStopOrderFilter); if (StopLossType == TradeStopLossType.Filter) requiredFilters.Add(_stopLossFilter); if (TrailingStopType == TradeStopLossType.Filter) requiredFilters.Add(_trailingStopFilter); if (TargetType == TradeStopLossType.Filter) requiredFilters.Add(_targetFilter); return requiredFilters; } /// /// The minimum minutes from close for time stop. IB prevents a good-after-time order from being entered TOO close to the close. /// public static readonly int MIN_MINUTES_FROM_CLOSE_FOR_TIME_STOP = 5; public override bool Equals(System.Object obj) { // If parameter is null return false. if (obj == null) { return false; } // If parameter cannot be cast to Point return false. TradingStrategy c = obj as TradingStrategy; if ((System.Object)c == null) { return false; } // Return true if the fields match: return (_id == c._id); } public bool Equals(TradingStrategy c) { // If parameter is null return false: if ((object)c == null) { return false; } // Return true if the fields match: return (_id == c._id); } public override int GetHashCode() { return _id.GetHashCode(); } } public class BrokerConverter : TypeConverter { public override object ConvertTo(ITypeDescriptorContext context, System.Globalization.CultureInfo culture, object value, System.Type destinationType) { if (destinationType == typeof(IBroker)) { IBroker broker = (IBroker)value; return broker.Name(); } return base.ConvertTo(context, culture, value, destinationType); } } }