using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Drawing; using System.Linq; using System.Text; using System.Windows.Forms; using TradeIdeas.MiscSupport; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.XML; namespace TradeIdeas.TIProGUI.LimitAlerts { public partial class LimitAlertCreateForm : Form, TopListRequest.Listener { public string Id { get; set; } public DateTime? Creation { get; set; } public DateTime? Updated { get; set; } private WatermarkTextBox _textBoxSymbol = new WatermarkTextBox(); // NEW SYMBOL LOOKUP SymbolLookupB _symbolLookup; private string Symbol { get { return _symbolLookup.GetSymbol(); } } public double Price { get { double toReturn = -1; if (double.TryParse(textBoxPrice.Text, out toReturn)) return toReturn; return -1; } set { textBoxPrice.Text = value.ToString(); } } /// /// From below = true, from above = false /// public bool IsLong { get { return checkBoxIsLong.Checked; } set { checkBoxIsLong.Checked = value; } } /// /// After the order triggers are we predicting the price to go up (true) or down (false) /// public bool LongAfter { get { return radioButtonSideLong.Checked; } set { radioButtonSideLong.Checked = value; radioButtonSideShort.Checked = !value; } } public double? InvalidPrice { get { double toReturn = -1; if (double.TryParse(textBoxInvalidPrice.Text, out toReturn)) return toReturn; return null; } set { textBoxInvalidPrice.Text = value.ToString(); } } /// /// Trigger price alerts after hours /// public bool AfterHours { get { return checkBoxAfterHours.Checked; } set { checkBoxAfterHours.Checked = value; } } public DateTime? Expires { get { if (checkBoxExpiration.Checked) { DateTime expires = new DateTime(dateTimePickerExpirationDate.Value.Year, dateTimePickerExpirationDate.Value.Month, dateTimePickerExpirationDate.Value.Day, dateTimePickerExpirationTime.Value.Hour, dateTimePickerExpirationTime.Value.Minute, dateTimePickerExpirationTime.Value.Second); return expires; } else return null; } set { if (value.HasValue) { checkBoxExpiration.Checked = true; dateTimePickerExpirationDate.Value = value.Value; dateTimePickerExpirationTime.Value = value.Value; UpdateEnabledStatus(); } } } public string Notes { get { return _notesTextBox.Text; } set { _notesTextBox.Text = value; } } public string WindowName { get { return Text; } set { Text = value; } } public LimitAlert LimitAlert { get { return GetLimitAlertFromGui(); } } WatermarkTextBox _notesTextBox = new WatermarkTextBox(); private TopListRequest.Token _liveData; /// /// This automatically ignores the request if the symbol didn't change. /// /// null or "" means to stop the data. private void FollowSymbol(string symbol) { if (null != symbol) symbol = symbol.Trim(); if ((null != _liveData) && (_liveData.GetRequest().SingleSymbol == symbol)) // No change. return; if (null != _liveData) _liveData.Cancel(); _currentPrice = null; UpdateCurrentPrice(); UpdateEnabledStatus(); if ((null == symbol) || ("" == symbol)) _liveData = null; else { TopListRequest request = new TopListRequest(); request.OutsideMarketHours = false; request.SingleSymbol = symbol; request.Streaming = true; request.UseDatabase = false; request.AddExtraColumn("Price"); // A previous revision of the code requested this data, extracted it into a local variable, // then ignored it. Now we're not requesting it at all. //request.AddExtraColumn("D_Name"); _liveData = request.Send(_sendManager, this); } } /// /// Last price received by SymbolDetails /// private double? _currentPrice = null; private readonly ISendManager _sendManager = GuiEnvironment.FindConnectionMaster("").SendManager; private FontManager _fontManager; private const float DEFAULT_FONT_SIZE = 8.25F; /// /// /// /// May be null public LimitAlertCreateForm(string initialSymbol) { InitializeComponent(); Icon = GuiEnvironment.Icon; MinimizeBox = false; _notesTextBox.WatermarkText = "Notes"; _notesTextBox.Multiline = true; _notesTextBox.Dock = DockStyle.Fill; tableLayoutPanelGrid.Controls.Add(_notesTextBox, 1, 7); _fontManager = new FontManager(this, DEFAULT_FONT_SIZE); _fontManager.selectTheFont(); // NEW SYMBOL LOOKUP //if (GuiEnvironment.DevelopmentMode) //{ string apiURL = GuiEnvironment.AppConfig.Node("SYMBOL_LOOKUP").Property("API_URL", ""); int symbolLimit = GuiEnvironment.AppConfig.Node("SYMBOL_LOOKUP").Property("SYMBOL_LIMIT", -1); int keystrokePause = GuiEnvironment.AppConfig.Node("SYMBOL_LOOKUP").Property("KEYSTROKE_PAUSE", -1); _symbolLookup = new SymbolLookupTextBox() { ApiUrl = apiURL, SymbolLimit = symbolLimit, KeyStrokePause = keystrokePause, FontSize = this.Font.SizeInPoints, TextBoxFont = Font, }; tableLayoutPanelGrid.Controls.Add(_symbolLookup, 1, 0); _symbolLookup.Size = new Size(60, 20); _symbolLookup.Margin = new Padding(3, 3, 3, 3); _symbolLookup.AutoSetHeight(); //_symbolLookup.Location = new Point(14, 12); // new Point(300, 12); //_symbolLookup.Anchor = AnchorStyles.Top | AnchorStyles.Left; _symbolLookup.symbolSelected += SetSymbol; if (null != initialSymbol) _symbolLookup.SetSymbol(initialSymbol); //} //else //{ // _textBoxSymbol.WatermarkText = "Symbol"; // _textBoxSymbol.Size = new Size(60, 20); // _textBoxSymbol.TextChanged += _textBoxSymbol_TextChanged; // _textBoxSymbol.CharacterCasing = CharacterCasing.Upper; // tableLayoutPanelGrid.Controls.Add(_textBoxSymbol, 1, 0); // if (null != initialSymbol) // _textBoxSymbol.Text = initialSymbol; //} UpdateEnabledStatus(); FixStatusLabel(); Resize += LimitAlertCreateForm_Resize; FormClosing += LimitAlertCreateForm_FormClosing; SetDefaultExpirationTime(); StartPosition = FormStartPosition.CenterParent; textBoxPrice.Select(); SetTabOrder(); } private void SetTabOrder() { // NEW SYMBOL LOOKUP //if (GuiEnvironment.DevelopmentMode) _symbolLookup.TabIndex = 0; //else // _textBoxSymbol.TabIndex = 0; flowLayoutPanelAlertPrice.TabIndex = 1; checkBoxIsLong.TabIndex = 2; flowLayoutPanelSide.TabIndex = 3; flowLayoutPanelExpiration.TabIndex = 4; textBoxInvalidPrice.TabIndex = 5; _notesTextBox.TabIndex = 6; flowLayoutPanelButtons.TabIndex = 7; } void LimitAlertCreateForm_FormClosing(object sender, FormClosingEventArgs e) { // NEW SYMBOL LOOKUP //if (GuiEnvironment.DevelopmentMode) //{ _symbolLookup.symbolSelected -= SetSymbol; _symbolLookup.UnsubscribeEvents(); //} FollowSymbol(null); } void _textBoxSymbol_TextChanged(object sender, EventArgs e) { UpdateEnabledStatus(); FollowSymbol(Symbol); } private void SetSymbol(string symbol) { UpdateEnabledStatus(); FollowSymbol(Symbol); } private void QuoteTickReceived(RowData data) { double price = data.GetPrice(); if (price <= 0.0) _currentPrice = null; else _currentPrice = price; UpdateEnabledStatus(); } private void UpdateCurrentPrice() { if (_currentPrice.HasValue) { // TODO normlly we display 4 digits if the price is less than $1. labelCurrentPrice.Text = "Currently: " + _currentPrice.Value.ToString("N2"); if (Price != -1) { double difference = Price - _currentPrice.Value; string plus = "+"; if (difference < 0) plus = ""; labelCurrentPrice.Text = labelCurrentPrice.Text + " (" + plus + difference.ToString("N2") + ")"; } } else labelCurrentPrice.Text = ""; } /// /// Sets default expiration time for expires field. /// private void SetDefaultExpirationTime() { DateTime marketClose = GuiEnvironment.GetMarketCloseLocalTime(); DateTime now = ServerFormats.Now; DateTime expiration = marketClose.AddMinutes(-5); if (now > expiration) { if (now.DayOfWeek == DayOfWeek.Friday) expiration = expiration.AddDays(3); else if (now.DayOfWeek == DayOfWeek.Saturday) expiration = expiration.AddDays(2); else expiration = expiration.AddDays(1); } dateTimePickerExpirationDate.Value = expiration; dateTimePickerExpirationTime.Value = expiration; } void LimitAlertCreateForm_Resize(object sender, EventArgs e) { FixStatusLabel(); } private void FixStatusLabel() { labelStatus.MaximumSize = new Size(tableLayoutPanelGrid.Width, 0); labelStatus.AutoSize = true; } private void buttonCancel_Click(object sender, EventArgs e) { DialogResult = System.Windows.Forms.DialogResult.Cancel; Close(); } private void buttonSave_Click(object sender, EventArgs e) { DialogResult = System.Windows.Forms.DialogResult.OK; Close(); } private LimitAlert GetLimitAlertFromGui() { LimitAlert limitAlert = new LimitAlert(); limitAlert.Symbol = Symbol; limitAlert.Price = Price; limitAlert.InvalidPrice = InvalidPrice; limitAlert.IsLong = IsLong; limitAlert.LongAfter = LongAfter; limitAlert.Expires = Expires; limitAlert.AfterHours = AfterHours; limitAlert.Notes = Notes; limitAlert.Id = Id; if (Creation.HasValue) limitAlert.Created = Creation.Value; if (Updated.HasValue) limitAlert.Updated = Updated.Value; return limitAlert; } private void checkBoxExpiration_CheckedChanged(object sender, EventArgs e) { UpdateEnabledStatus(); } private void UpdateEnabledStatus() { dateTimePickerExpirationDate.Enabled = checkBoxExpiration.Checked; dateTimePickerExpirationTime.Enabled = checkBoxExpiration.Checked; if (FormIsValid()) { if (IsLong) { checkBoxIsLong.Text = "(Price of " + Symbol + " rises to $" + Price.ToString("N2") + ")"; labelStatus.Text = "Alert if price of " + Symbol + " rises to $" + Price.ToString("N2"); if (Expires.HasValue) labelStatus.Text += " before " + Expires.Value.ToString("yyyy-MM-dd hh:mm:ss tt"); if (InvalidPrice.HasValue) labelStatus.Text += " unless price falls to $" + InvalidPrice.Value.ToString("N2") + " first"; } else { checkBoxIsLong.Text = "(Price of " + Symbol + " falls to $" + Price.ToString("N2") + ")"; labelStatus.Text = "Alert if price of " + Symbol + " falls to $" + Price.ToString("N2"); if (Expires.HasValue) labelStatus.Text += " before " + Expires.Value.ToString("yyyy-MM-dd hh:mm:ss tt"); if (InvalidPrice.HasValue) labelStatus.Text += " unless price rises to $" + InvalidPrice.Value.ToString("N2") + " first"; } AddPriceWarningIfRequired(); } else if (Price == -1) { checkBoxIsLong.Text = "Error - update Price field to valid value."; labelStatus.Text = "Error - update Price field to valid value."; } else { checkBoxIsLong.Text = "Error - update Symbol field to valid value."; labelStatus.Text = "Error - update Symbol field to valid value."; } buttonSave.Enabled = FormIsValid(); } /// /// Determines whether the current limit alert would be immediately triggered. /// private void AddPriceWarningIfRequired() { if ((_currentPrice > Price && IsLong) || (_currentPrice < Price && !IsLong)) labelStatus.Text = labelStatus.Text + " - WARNING: alert might trigger immediately since price is currently " + _currentPrice.Value.ToString("N2"); } private bool FormIsValid() { return Price != -1 && Symbol != ""; } private void textBoxPrice_TextChanged(object sender, EventArgs e) { // UpdateCurrentPrice() includes the difference between the stock price and // the alert price. Call it if either input changes. UpdateCurrentPrice(); UpdateEnabledStatus(); } private void dateTimePickerExpirationDate_ValueChanged(object sender, EventArgs e) { UpdateEnabledStatus(); } private void dateTimePickerExpirationTime_ValueChanged(object sender, EventArgs e) { UpdateEnabledStatus(); } private void textBoxInvalidPrice_TextChanged(object sender, EventArgs e) { UpdateEnabledStatus(); } private void checkBoxIsLong_CheckedChanged(object sender, EventArgs e) { UpdateEnabledStatus(); } /// /// This could be called from any thread. /// /// /// /// /// void TopListRequest.Listener.OnRowData(List rows, DateTime? start, DateTime? end, TopListRequest.Token token) { this.BeginInvokeIfRequired((MethodInvoker)delegate { if (token != _liveData) // Old request, don't care. return; token.GetRequest().LastUpdate = DateTime.Now; if (rows.Count != 1) // Probably asked for an invalid symbol. return; QuoteTickReceived(rows[0]); UpdateCurrentPrice(); }); } /// /// This could be called from any thread. /// /// void TopListRequest.Listener.OnMetaData(TopListRequest.Token token) { // Ignore it. We should never get this. } /// /// This could be called from any thread. /// /// void TopListRequest.Listener.OnDisconnect(TopListRequest.Token token) { this.BeginInvokeIfRequired((MethodInvoker)delegate { if (token != _liveData) // old request, don't care return; // Resend the same request. _liveData = token.GetRequest().Send(_sendManager, this); }); } } }