using System; using System.Collections.Generic; using System.ComponentModel; using System.Data; using System.Diagnostics; using System.Drawing; using System.Linq; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; using System.Xml; using TradeIdeas.MiscSupport; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Interfaces; using TradeIdeas.TIProGUI.Surfer; using TradeIdeas.XML; namespace TradeIdeas.TIProGUI.LimitAlerts { public partial class LimitAlertsForm : Form, IFont, ISaveLayout, IRowSelect, IAccountStatusChanged, ISnapToGrid, IDemoMode, Surfer.Surfable, IContextMenuStrip, IHaveGrids, ISymbolLinkingChannel { /// /// This is the xml that was used to restore this form. If this form wasn't layout-restored then this will be null. /// public XmlNode RestoredLayout { get; set; } /// /// This gets called before any other code in the SaveLayout function. Currently this is aimed at the Scottrade addin but anything can set this code. /// public Action
PreSaveLayoutCode { get; set; } /// /// This gets called after SaveBase and it passes the XmlNode that the form is being saved to. This allows extensions to add properties to the layout. Access the layout XmlNode /// from a form using RestoredLayout. /// public Action SaveLayoutCode { get; set; } /// /// Exposes the right click menu for wrappers to access. /// public ContextMenuStrip MyContextMenuStrip { get { return _limitAlertsGrid.getDataGridView().ContextMenuStrip; } } private ColumnListState _columnListState = new ColumnListState(); /// /// Actions for this form. /// private Actions _actions; private const float FLOW_LAYOUT_HEIGHT_OFFSET = 13f; private readonly IConnectionMaster _connectionMaster; private LimitAlertsGrid _limitAlertsGrid; public static readonly WindowIconCache WindowIconCache = new WindowIconCache(FORM_TYPE); private Timer _refreshTimer; private string message = GuiEnvironment.XmlConfig.Node("MARKETING_MESSAGE").PropertyForCulture("TEXT", "***"); private LinkLabel.Link _link; public static bool inDemoMode = false; private bool _showDailyProfitChart = false; private bool _showIntradayProfitChart = false; // new variable to set that dock panel is being used - RVH20210329 private bool _dockPanelMode = true; private List _dataGrids = new List(); public LimitAlertsForm(IConnectionMaster connectionMaster) { _connectionMaster = connectionMaster; InitializeComponent(); StartPosition = FormStartPosition.Manual; _actions = new Actions(this); Text = "Price Alerts"; _limitAlertsGrid = new LimitAlertsGrid(_connectionMaster); AttachLimitAlertsManagerListening(); _limitAlertsGrid.Dock = DockStyle.Fill; _limitAlertsGrid.Margin = new System.Windows.Forms.Padding(0); _statusFilterCheckedListBox.Font = Font; tableLayoutPanelMain.Controls.Add(_limitAlertsGrid, 1, 1); _dataGrids.Add(_limitAlertsGrid.getDataGridView()); FormClosing += LimitAlertsForm_FormClosing; VisibleChanged += LimitAlertsForm_VisibleChanged; SetupLimitAlertStatusFilter(); selectTheFont(); WindowIconCache.SetIcon(this); UpdateStatusFilterText(); UpdateWindowTitle(); SetSnapToGrid(GuiEnvironment.SnapToGrid); lblMessage.Text = message; linkLabel.Text = GuiEnvironment.XmlConfig.Node("MARKETING_MESSAGE_LEARN").PropertyForCulture("TEXT", "***"); _link = new LinkLabel.Link(); _link.LinkData = GuiEnvironment.MarketingLink + GuiEnvironment.UserMarketingData + "product=lim"; linkLabel.Links.Clear(); linkLabel.Links.Add(_link); applyAccountStatus(); SetupProfitChart(chartPandLDaily); SetupProfitChart(chartPandLIntraday); ShowHideProfit(); MergeAlerts(); _refreshTimer = new Timer(); _refreshTimer.Interval = 1000; _refreshTimer.Tick += _refreshTimer_Tick; _refreshTimer.Start(); SurfManager.Instance().DataRefreshed(this); } /// /// Exposes the data grids. /// public List DataGrids { get { return _dataGrids; } } public void AttachLimitAlertsManagerListening() { if (null != GuiEnvironment.LimitAlertsMessageManager) { GuiEnvironment.LimitAlertsMessageManager.StatusProcessed -= LimitAlertsMessageManager_StatusProcessed; GuiEnvironment.LimitAlertsMessageManager.StatusProcessed += LimitAlertsMessageManager_StatusProcessed; } } void LimitAlertsMessageManager_StatusProcessed() { GuiEnvironment.LogMessage("[LimitAlertsForm StatusProcessed] Going to MergeAlerts"); MergeAlerts(); } private void SetupProfitChart(Chart chart) { chart.ChartAreas[0].AxisX.LabelStyle.Enabled = false; chart.Series[0].XValueType = System.Windows.Forms.DataVisualization.Charting.ChartValueType.Time; chart.BackColor = Color.Black; chart.BorderColor = Color.FromArgb(100, 100, 100); chart.ChartAreas[0].BackColor = Color.Black; chart.ChartAreas[0].BorderColor = Color.FromArgb(55, 55, 55); chart.ChartAreas[0].AxisY.LabelStyle.ForeColor = Color.FromArgb(55, 55, 55); chart.ChartAreas[0].AxisY.MajorGrid.LineColor = Color.FromArgb(55, 55, 55); chart.ChartAreas[0].AxisY.MinorGrid.LineColor = Color.FromArgb(55, 55, 55); chart.ChartAreas[0].AxisY.LabelStyle.Format = "0.00"; } public void ShowHideIntradayChart(bool show) { _showIntradayProfitChart = show; ShowHideProfit(); } public void ShowHideDailyChart(bool show) { _showDailyProfitChart = show; ShowHideProfit(); } private void ShowHideProfit() { labelChartDaily.Visible = _showDailyProfitChart; chartPandLDaily.Visible = _showDailyProfitChart; labelChartIntraday.Visible = _showIntradayProfitChart; chartPandLIntraday.Visible = _showIntradayProfitChart; tableLayoutPanelPandLCharts.Visible = _showIntradayProfitChart || _showDailyProfitChart; if (tableLayoutPanelPandLCharts.Visible) { tableLayoutPanelMain.ColumnStyles[0].SizeType = SizeType.AutoSize; } else { tableLayoutPanelMain.ColumnStyles[0].SizeType = SizeType.Absolute; tableLayoutPanelMain.ColumnStyles[0].Width = 0; } _limitAlertsGrid.ShowChartDaily = _showDailyProfitChart; _limitAlertsGrid.ShowChartIntraday = _showIntradayProfitChart; } void _refreshTimer_Tick(object sender, EventArgs e) { this.InvokeIfRequired(delegate() { SetProfitSummaryColors(); _limitAlertsGrid.UpdateGradients(); NotifySurfers(); }); } private Color _defaultProfitColor = Color.FromArgb(217, 217, 217); private Color _greenProfitColor = Color.FromArgb(0, 255, 0); private Color _redProfitColor = Color.FromArgb(255, 0, 0); private void SetProfitSummaryColors() { double profit = _limitAlertsGrid.GetTotalSinceTriggered(); labelTotalProfitValue.Text = profit.ToString("N2"); if (profit > 0) labelTotalProfitValue.ForeColor = _greenProfitColor; else if (profit < 0) labelTotalProfitValue.ForeColor = _redProfitColor; else labelTotalProfitValue.ForeColor = _defaultProfitColor; } void LimitAlertsForm_VisibleChanged(object sender, EventArgs e) { if (Visible) { //_limitAlertsGrid.ClearAlerts(); //_limitAlertsGrid.AddExistingLimitAlerts(); UpdateStatusFilterText(); } } void LimitAlertsForm_FormClosing(object sender, FormClosingEventArgs e) { _refreshTimer.Stop(); _refreshTimer.Dispose(); _limitAlertsGrid.Dispose(); _statusFilterCheckedListBox.ItemCheck -= _statusFilterCheckedListBox_ItemCheck; MyContextMenuStrip.Opening -= LayoutManager.Instance().contextMenuStrip_Opening; SurfManager.Instance().Remove(this); } internal void AddLimitAlert(LimitAlert limitAlert, bool bringToFront) { _limitAlertsGrid.AddLimitAlert(limitAlert, bringToFront); } internal void RemoveLimitAlert(LimitAlert limitAlert) { _limitAlertsGrid.RemoveLimitAlert(limitAlert); } internal void ClearAlerts() { _limitAlertsGrid.ClearAlerts(); } public void SetSnapToGrid(bool enabled) { formSnapper1.Enabled = enabled; if (GuiEnvironment.RunningWin10 && enabled) { formSnapper1.Win10HeightAdjustment = GuiEnvironment.HEIGHT_INCREASE; formSnapper1.Win10WidthAdjustment = GuiEnvironment.WIDTH_INCREASE; } } public const string FORM_TYPE = "LIMIT_ALERTS_FORM"; static public void RegisterLayout() { // change parameters to accommodate dock panel changes - RVH20210402 //LayoutManager.Instance().AddRestoreRule(FORM_TYPE, (RestoreLayout)delegate(XmlNode description, bool ignorePosition, bool cascadePosition) LayoutManager.Instance().AddRestoreRule(FORM_TYPE, (RestoreLayout)delegate (XmlNode description, bool ignorePosition, bool cascadePosition, bool dockPanelMode, string mainDockPanelName, string mainDockPanelTitle, string dockPanelID) { IConnectionMaster connectionMaster = GuiEnvironment.FindConnectionMaster(description.Property("CONNECTION")); if (null == connectionMaster) { // We could report an error here, but it's simpler just to do nothing. Any error message we tried to // report would probably be confusing at best to the user. } else { //CheckForOtherLimitAlertsFormInstance(); LimitAlertsForm form = new LimitAlertsForm(connectionMaster); // change parameters to accommodate dock panel changes - RVH20210402 //form.Restore(description, ignorePosition, cascadePosition); form.Restore(description, ignorePosition, cascadePosition, dockPanelMode, mainDockPanelName, mainDockPanelTitle, dockPanelID); form.RestoredLayout = description; } }); } /// /// Returns true/false for whether Limit Alerts are enabled for this user or not. /// /// private static bool LimitAlertsEnabled() { return LayoutManager.OddsMakerAvailable && LayoutManager.OddsMakerTrials == GuiEnvironment.SUBSCRIBED_ODDSMAKER_TRIALS; } static private void CheckForOtherLimitAlertsFormInstance() { //We're preventing the case of having more than one instance of LimitAlertsForm being open. //The form being restored from the layout is the form that shall be shown. foreach (Form form in Application.OpenForms.Cast().Where(x => !x.IsDisposed).ToList()) { LimitAlertsForm test = form as LimitAlertsForm; if (test != null) { form.Close(); break; } } } /// /// Public selector for actions so LimitAlertsGrid can access it. /// public Actions Actions { get { return _actions; } } public void SaveLayout(XmlNode parent) { if (null != PreSaveLayoutCode) PreSaveLayoutCode(this); XmlNode description = LayoutManager.SaveBase(parent, this, FORM_TYPE); XmlNode colHeaders = description.NewNode("COL_HEADERS"); if (null != SaveLayoutCode) SaveLayoutCode(this, description); _actions.Save(description); //save Column Order... _columnListState.LoadFrom(_limitAlertsGrid.getDataGridView()); _columnListState.SaveTo(description.NewNode("COLUMNS")); description.SetProperty("PINNED", Pinned); // filtering description.SetProperty("SHOW_FILTER", tableLayoutPanelFilter.Visible); XmlNode statusFiltersNode = description.NewNode("STATUS_FILTERS"); foreach (LimitAlertStatus status in _selectedStatuses) { XmlNode statusFilterNode = statusFiltersNode.NewNode("STATUS_FILTER"); statusFilterNode.SetProperty("STATUS", status.ToString()); } // p and l charts description.SetProperty("SHOW_INTRADAY_CHART", chartPandLIntraday.Visible); description.SetProperty("SHOW_DAILY_CHART", chartPandLDaily.Visible); //save SortOrder... description.SetProperty("LOCALSORTENABLED", _limitAlertsGrid.LocalSortEnabled); if (null != _limitAlertsGrid.SortByColumn) description.SetProperty("LOCALSORTCODE", _limitAlertsGrid.SortByColumn.InternalCode); description.SetProperty("LOCALSORTTYPE", _limitAlertsGrid.LocalSortType); //save column visibility DataGridView dgv = _limitAlertsGrid.getDataGridView(); foreach (DataGridViewColumn c in dgv.Columns) { XmlNode visibleNode = colHeaders.NewNode("VISIBILITY"); visibleNode.SetProperty("HEADER_NAME", c.HeaderText); visibleNode.SetProperty("IS_VISIBLE", c.Visible); } // Save the symbol plus layout. description.SetProperty("SYMBOL_PLUS_LAYOUT", _limitAlertsGrid.SymbolPlusLayout.ToString()); } // change parameters to accommodate dock panel changes - RVH20210402 //public void Restore(XmlNode description, bool ignorePosition, bool cascadePosition) public void Restore(XmlNode description, bool ignorePosition, bool cascadePosition, bool dockPanelMode = false, string mainDockPanelName = "", string mainDockPanelTitle = "", string dockPanelID = "") { _refreshTimer.Stop(); // change parameters to accommodate dock panel changes - RVH20210402 // LayoutManager.RestoreBase(description, this, ignorePosition, cascadePosition); LayoutManager.RestoreBase(description, this, ignorePosition, cascadePosition, null, false, dockPanelMode, mainDockPanelName, mainDockPanelTitle, dockPanelID); _dockPanelMode = dockPanelMode; //restore column order.. _columnListState.LoadFrom(description.Node("COLUMNS")); // move this code to before the SaveTo() call so that column visibility is correct - RVH20210619 // get the column Visibility string platform = description.Property("PLATFORM", ""); if (_columnListState._columnOrder.Count <= _limitAlertsGrid.getDataGridView().Columns.Count) { XmlNode colHeaderCollection = description.Node("COL_HEADERS"); Dictionary dictionary = new Dictionary(); foreach (XmlNode visibility in colHeaderCollection.Enum()) { bool test; Boolean.TryParse(visibility.Property("IS_VISIBLE"), out test); dictionary.Add(visibility.Property("HEADER_NAME"), test); } DataGridViewColumnCollection dgvCols = _limitAlertsGrid.getDataGridView().Columns; foreach (DataGridViewColumn c in dgvCols) { string hText = c.HeaderText; if (dictionary.ContainsKey(hText)) { c.Visible = dictionary[hText]; } else { if (platform == "GWT") c.Visible = false; } } } // add parameter for form type - RVH20210528 //_columnListState.SaveTo(_limitAlertsGrid.getDataGridView()); _columnListState.SaveTo(_limitAlertsGrid.getDataGridView(), FORM_TYPE); _actions.Load(description); Pinned = description.Property("PINNED", false); _showDailyProfitChart = description.Property("SHOW_DAILY_CHART", false); _showIntradayProfitChart = description.Property("SHOW_INTRADAY_CHART", false); ShowHideProfit(); bool showFilter = description.Property("SHOW_FILTER", false); tableLayoutPanelFilter.Visible = showFilter; _limitAlertsGrid.ShowFilter = showFilter; if (null != description.Node("STATUS_FILTERS") && description.Node("STATUS_FILTERS").HasChildNodes) { XmlNodeList statusFiltersNodeList = description.Node("STATUS_FILTERS").ChildNodes; foreach (XmlNode statusFilterNode in statusFiltersNodeList) { LimitAlertStatus status = statusFilterNode.PropertyEnum("STATUS", LimitAlertStatus.TRIGGERED); if (!_selectedStatuses.Contains(status)) { _selectedStatuses.Add(status); int index = _statusFilterCheckedListBox.Items.IndexOf(status); if (index != -1) _statusFilterCheckedListBox.SetItemChecked(index, true); } } _limitAlertsGrid.SelectedStatuses = _selectedStatuses.ToList(); UpdateStatusFilterText(); UpdateWindowTitle(); } //restore sort order... if (null != description.Property("LOCALSORTENABLED")) { bool enabled = bool.Parse(description.Property("LOCALSORTENABLED", "False")); _limitAlertsGrid.LocalSortEnabled = enabled; } if (null != description.Property("LOCALSORTTYPE")) { SortType value = (SortType)Enum.Parse(typeof(SortType), description.Property("LOCALSORTTYPE", "Descending")); _limitAlertsGrid.LocalSortType = value; } bool needToRestoreSortBy = false; if (null != description.Property("LOCALSORTCODE") && description.Property("LOCALSORTCODE") != String.Empty) { _limitAlertsGrid.ImportSortBy = description.Property("LOCALSORTCODE"); needToRestoreSortBy = true; } if (needToRestoreSortBy) { List columns = _limitAlertsGrid.getColumnInfoList(); ColumnInfo sortCol = null; //Get the columnInfo from the "ImportSortBy" property.. foreach (ColumnInfo c in columns) { if (c.InternalCode == _limitAlertsGrid.ImportSortBy) { sortCol = c; _limitAlertsGrid.SortByColumn = c; break; } } if (sortCol != null) { DataGridView dgv = _limitAlertsGrid.getDataGridView(); object o = dgv.DataSource; BindingList rowDatas = (BindingList)o; RowDataHelper.DoSorting(dgv, rowDatas, sortCol, _limitAlertsGrid.LocalSortType); RowDataHelper.FixCellHeaders(dgv, columns, sortCol); } } //// move this code to before the SaveTo() call so that column visibility is correct - RVH20210619 //// get the column Visibility //if (_columnListState._columnOrder.Count <= _limitAlertsGrid.getDataGridView().Columns.Count) //{ // XmlNode colHeaderCollection = description.Node("COL_HEADERS"); // Dictionary dictionary = new Dictionary(); // foreach (XmlNode visibility in colHeaderCollection.Enum()) // { // bool test; // Boolean.TryParse(visibility.Property("IS_VISIBLE"), out test); // dictionary.Add(visibility.Property("HEADER_NAME"), test); // } // DataGridViewColumnCollection dgvCols = _limitAlertsGrid.getDataGridView().Columns; // foreach (DataGridViewColumn c in dgvCols) // { // string hText = c.HeaderText; // if (dictionary.ContainsKey(hText)) // { // c.Visible = dictionary[hText]; // } // } //} // Restore the symbol plus layout. DataCell.SymbolPlusLayoutType symbolPlusLayout; Enum.TryParse(description.Property("SYMBOL_PLUS_LAYOUT", "Off"), out symbolPlusLayout); _limitAlertsGrid.SymbolPlusLayout = symbolPlusLayout; var column = _limitAlertsGrid.getDataGridView().Columns.Cast().FirstOrDefault(gridColumn => gridColumn.ColumnInfo.Format == "symbolplus"); var symbolPlusColumnWidth = -1; if (column != null) symbolPlusColumnWidth = _columnListState.GetColumnStateWidth(column.ColumnInfo.InternalCode); _limitAlertsGrid.UpdateSymbolPlus(true, symbolPlusColumnWidth); _limitAlertsGrid.DoDataBinding(); _refreshTimer.Start(); } public void selectTheFont() { Font = GuiEnvironment.FontSettings; (_limitAlertsGrid.getDataGridView()).DefaultCellStyle.Font = Font; _limitAlertsGrid.setContextMenuFont(Font); DataGridViewHelper.SetRowHeight(_limitAlertsGrid.getDataGridView(), _limitAlertsGrid.SymbolPlusLayout); if (tableLayoutPanelMain.RowStyles[0].Height != 0f) { tableLayoutPanelMain.RowStyles[0].Height = Font.GetHeight() + FLOW_LAYOUT_HEIGHT_OFFSET; } _statusFilterCheckedListBox.Font = Font; labelTotalProfitValue.Font = Font; _limitAlertsGrid.UpdateSymbolPlus(); } public void OnDemoModeChanged() { if (inDemoMode) { pnlMarketing.BringToFront(); } else { pnlMarketing.SendToBack(); } } public bool Pinned { get { return _limitAlertsGrid.Pinned; } set { _limitAlertsGrid.Pinned = value; } } WindowIconCache ISaveLayout.WindowIconCache { get { return WindowIconCache; } } private Panel _statusFilterDropDownPanel = new Panel(); private CustomCheckedListBox _statusFilterCheckedListBox = new CustomCheckedListBox(); private void comboBoxTypeFilter_Click(object sender, EventArgs e) { _statusFilterCheckedListBox.ItemCheck -= _statusFilterCheckedListBox_ItemCheck; _statusFilterCheckedListBox.ItemCheck += _statusFilterCheckedListBox_ItemCheck; _statusFilterCheckedListBox.BorderStyle = BorderStyle.None; _statusFilterCheckedListBox.CheckOnClick = true; _statusFilterDropDownPanel.AutoScroll = true; _statusFilterCheckedListBox.Visible = true; _statusFilterCheckedListBox.Location = new Point(comboBoxStatusFilter.Left, comboBoxStatusFilter.Bottom); int widest = 0; foreach (LimitAlertStatus statusFilter in Enum.GetValues(typeof(LimitAlertStatus))) { Size itemSize = TextRenderer.MeasureText(statusFilter.ToString(), _statusFilterCheckedListBox.Font); if (itemSize.Width > widest) widest = itemSize.Width + 40; // checkbox width is 16 } _statusFilterCheckedListBox.Width = widest; // Improve the client size; this will remove the extra blank item in the tool strip drop down. _statusFilterCheckedListBox.ClientSize = new Size(_statusFilterCheckedListBox.ClientSize.Width, _statusFilterCheckedListBox.GetItemRectangle(0).Height * _statusFilterCheckedListBox.Items.Count); ToolStripDropDown popup = new ToolStripDropDown(); popup.AutoSize = false; popup.Margin = new Padding(0, 0, 0, 3); popup.Padding = Padding.Empty; popup.Size = new Size(_statusFilterCheckedListBox.Width, _statusFilterCheckedListBox.Height + 6); ToolStripControlHost host = new ToolStripControlHost(_statusFilterCheckedListBox); host.Margin = new Padding(5, 3, 0, 0); host.Padding = Padding.Empty; host.AutoSize = false; host.Size = new Size(_statusFilterCheckedListBox.Width, _statusFilterCheckedListBox.Height); popup.Items.Add(host); popup.Show(comboBoxStatusFilter, 0, comboBoxStatusFilter.Height); popup.Focus(); _statusFilterDropDownPanel.Size = host.Size; } HashSet _selectedStatuses = new HashSet(); void _statusFilterCheckedListBox_ItemCheck(object sender, ItemCheckEventArgs e) { _selectedStatuses.Clear(); try { LimitAlertStatus selectedFilter = (LimitAlertStatus)_statusFilterCheckedListBox.Items[e.Index]; if (e.NewValue == CheckState.Checked) _selectedStatuses.Add(selectedFilter); } catch (Exception exc) { string debugView = exc.ToString(); } foreach (var item in _statusFilterCheckedListBox.CheckedItems.OfType()) if (!_selectedStatuses.Contains(item)) _selectedStatuses.Add(item); try { LimitAlertStatus currentPositionType = (LimitAlertStatus)_statusFilterCheckedListBox.Items[e.Index]; if (e.NewValue == CheckState.Checked && !_selectedStatuses.Contains(currentPositionType)) _selectedStatuses.Add(currentPositionType); else if (e.NewValue == CheckState.Unchecked && _selectedStatuses.Contains(currentPositionType)) _selectedStatuses.Remove(currentPositionType); } catch { } UpdateLimitGridFilter(); _limitAlertsGrid.DoDataBinding(); UpdatePandLCharts(); UpdateStatusFilterText(); comboBoxStatusFilter.SelectedIndex = 0; UpdateWindowTitle(); } private void UpdateWindowTitle() { this.InvokeIfRequired(delegate() { if (_selectedStatuses.Count == 0 || _selectedStatuses.Count == Enum.GetValues(typeof(LimitAlertStatus)).Length) Text = "Price Alerts - All"; else { string comma = ""; string text = "Price Alerts - "; foreach (LimitAlertStatus status in _selectedStatuses) { text += comma + status.ToString(); comma = ", "; } Text = text; } }); } private void UpdateStatusFilterText() { this.InvokeIfRequired(delegate { comboBoxStatusFilter.SelectedIndex = 0; if (_selectedStatuses.Count == 0) comboBoxStatusFilter.Items[0] = "All Statuses"; else if (_selectedStatuses.Count == 1) comboBoxStatusFilter.Items[0] = _selectedStatuses.First().ToString(); else comboBoxStatusFilter.Items[0] = _selectedStatuses.Count + " Types"; }); } /// /// Updates the limit alert grid filter from the GUI. /// private void UpdateLimitGridFilter() { _limitAlertsGrid.SelectedStatuses.Clear(); foreach (LimitAlertStatus status in _selectedStatuses) { _limitAlertsGrid.SelectedStatuses.Add(status); } } private void SetupLimitAlertStatusFilter() { comboBoxStatusFilter.SelectedIndex = 0; comboBoxStatusFilter.DropDownHeight = 1; _statusFilterCheckedListBox.Items.Clear(); foreach (LimitAlertStatus status in Enum.GetValues(typeof(LimitAlertStatus))) { _statusFilterCheckedListBox.Items.Add(status, false); } } public void ShowHideFilterPanel(bool show) { tableLayoutPanelFilter.Visible = show; } internal void Duplicate() { IConnectionMaster connectionMaster = GuiEnvironment.FindConnectionMaster(""); if (null != connectionMaster) { GuiEnvironment.RecordUseCase("LimitAlerts.RightClick.Duplicate", connectionMaster.SendManager); LayoutManager.Instance().Duplicate(this); } } internal void SaveToCloud() { IConnectionMaster connectionMaster = GuiEnvironment.FindConnectionMaster(""); if (null != connectionMaster) { GuiEnvironment.RecordUseCase("LimitAlerts.RightClick.SaveToCloud", connectionMaster.SendManager); TIProGUI.SaveToCloud.DoIt(connectionMaster.SendManager, this); } } public void chooseRowSelect() { _limitAlertsGrid.chooseRowSelect(); } public void onAccountStatusChanged() { applyAccountStatus(); } private void applyAccountStatus() { if (_connectionMaster.LoginManager.AccountStatus == AccountStatus.Good) { if (LayoutManager.OddsMakerAvailable && LayoutManager.OddsMakerTrials == GuiEnvironment.SUBSCRIBED_ODDSMAKER_TRIALS) { tableLayoutPanelMain.Visible = true; // Note this will reset the max price alert set via the Xml file. GuiEnvironment.MaxLimitAlerts = GuiEnvironment.MAXLIMTALERTS; AttachLimitAlertsManagerListening(); } else if (!_connectionMaster.LoginManager.IsDemo) { // Standard subscription gets limited number price alerts tableLayoutPanelMain.Visible = true; GuiEnvironment.MaxLimitAlerts = GuiEnvironment.STDLIMTALERTS; AttachLimitAlertsManagerListening(); } else { // Demo mode. Price alerts are not allowed. tableLayoutPanelMain.Visible = false; } } else { tableLayoutPanelMain.Visible = true; lblMessage.Text = "No Connection"; linkLabel.Visible = false; } } private void linkLabel_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e) { _link.LinkData = GuiEnvironment.MarketingLink + GuiEnvironment.UserMarketingData + "product=lim"; Process.Start(e.Link.LinkData as string); } public bool MergingAlerts { get { return _limitAlertsGrid.MergingAlerts; } } internal void MergeAlerts() { if (!GuiEnvironment.LoadingLimitAlerts && !MergingAlerts) { try { GuiEnvironment.LogMessage("[LimitAlertsForm MergeAlerts] Going to MergeAlerts since LoadingLimitAlerts is false"); _limitAlertsGrid.MergeAlerts(); GuiEnvironment.LogMessage("[LimitAlertsForm MergeAlerts] Done with MergeAlerts"); } catch (Exception e) { string debugView = e.StackTrace; } } } internal void UpdatePandLCharts() { if (_showDailyProfitChart || _showIntradayProfitChart) { DateTime? firstTriggered = GetFirstTriggered(); if (firstTriggered.HasValue) { DateTime now = ServerFormats.Now; if (_showDailyProfitChart) { DateTime tradingDay = firstTriggered.Value; List dailyProfits = new List(); while (tradingDay >= firstTriggered.Value && tradingDay <= now) { double profit = GetTotalDailyProfit(tradingDay); dailyProfits.Add(profit); tradingDay = tradingDay.AddWeekDays(1); } this.InvokeIfRequired(delegate() { ClearChart(chartPandLDaily); int xValue = 0; double currentProfit = 0; foreach (double profit in dailyProfits) { DataPoint point = new DataPoint(xValue, profit); if (profit < 0) point.Color = Color.Red; else point.Color = Color.Green; chartPandLDaily.Series[0].Points.Add(point); currentProfit = profit; xValue++; } if (dailyProfits.Count > 0) { double minValue = dailyProfits.Min(); double maxValue = dailyProfits.Max(); double range = maxValue - minValue; if (range > 0) { chartPandLDaily.ChartAreas[0].AxisY.Minimum = minValue - (range * 0.05); chartPandLDaily.ChartAreas[0].AxisY.Maximum = maxValue + (range * 0.05); } else { chartPandLDaily.ChartAreas[0].AxisY.Minimum = -1; chartPandLDaily.ChartAreas[0].AxisY.Maximum = 1; } SetChartType(chartPandLDaily); } }); } if (_showIntradayProfitChart) { DateTime tradingMinute = firstTriggered.Value; DateTime todayOpen = GuiEnvironment.GetMarketOpenLocalTime(); if (tradingMinute < todayOpen) tradingMinute = todayOpen; List profits = new List(); double profitReference = 0; int minuteCount = 0; while (tradingMinute >= firstTriggered.Value && tradingMinute <= now) { double? profit = GetTotalProfit(tradingMinute); if (profit.HasValue) { if (minuteCount == 0) profitReference = profit.Value; double profitToAdd = profit.Value - profitReference; profits.Add(profitToAdd); //GuiEnvironment.LogMessage("[IntradayChart] Profit for " + tradingMinute.ToString() + " " + profitToAdd); } tradingMinute = tradingMinute.AddMinutes(1); minuteCount++; } this.InvokeIfRequired(delegate() { ClearChart(chartPandLIntraday); int xValue = 0; double currentProfit = 0; foreach (double profit in profits) { DataPoint point = new DataPoint(xValue, profit); if (profit < 0) point.Color = Color.Red; else point.Color = Color.Green; chartPandLIntraday.Series[0].Points.Add(point); currentProfit = profit; xValue++; } if (profits.Count > 0) { double minValue = profits.Min(); double maxValue = profits.Max(); double range = maxValue - minValue; if (range > 0) { chartPandLIntraday.ChartAreas[0].AxisY.Minimum = minValue - (range * 0.05); chartPandLIntraday.ChartAreas[0].AxisY.Maximum = maxValue + (range * 0.05); } else { chartPandLIntraday.ChartAreas[0].AxisY.Minimum = -1; chartPandLIntraday.ChartAreas[0].AxisY.Maximum = 1; } SetChartType(chartPandLIntraday); } }); } } else { ClearChart(chartPandLDaily); ClearChart(chartPandLIntraday); } } } private readonly double MIN_PIXELS_PER_BAR = 5; /// /// Sets chart type depending on number of bars and size of chart. /// private void SetChartType(Chart chart) { this.InvokeIfRequired(delegate() { if (chartPandLDaily.Series.Count > 0) { int numPoints = chart.Series[0].Points.Count; chart.ChartAreas[0].RecalculateAxesScale(); double pixelMin = chart.ChartAreas[0].AxisX.ValueToPixelPosition(chart.ChartAreas[0].AxisX.Minimum); double pixelMax = chart.ChartAreas[0].AxisX.ValueToPixelPosition(chart.ChartAreas[0].AxisX.Maximum); double chartAreaWidth = pixelMax - pixelMin; double pixelsPerBar = chartAreaWidth / numPoints; if (pixelsPerBar < MIN_PIXELS_PER_BAR) chart.Series[0].ChartType = SeriesChartType.Area; else chart.Series[0].ChartType = SeriesChartType.Column; } }); } private void ClearChart(Chart chart) { this.InvokeIfRequired(delegate() { chart.Series[0].Points.Clear(); }); } private double GetTotalDailyProfit(DateTime tradingDay) { List allProfits = new List(); foreach (LimitAlert limitAlert in GuiEnvironment.LimitAlerts.ToList()) { if (null != limitAlert && limitAlert.Triggered.HasValue && null != limitAlert.DailyPerformance) { DateTime triggered = limitAlert.Triggered.Value; int index = GuiEnvironment.GetWeekDays(triggered, tradingDay); if (index >= 0 && null != limitAlert.DailyPerformance && limitAlert.DailyPerformance.Values.Count >= index + 1) { double profitDollars = limitAlert.DailyPerformance.Values[index]; allProfits.Add(profitDollars); } } } double profit = 0; if (allProfits.Count > 0) profit = allProfits.Sum(); return profit; } private double? GetTotalProfit(DateTime tradingMinute) { //int triggeredAlerts = GuiEnvironment.LimitAlerts.Where(x => x.Triggered.HasValue).Count(); List allProfits = new List(); foreach (LimitAlert limitAlert in GuiEnvironment.LimitAlerts.ToList()) { if (null != _limitAlertsGrid && _limitAlertsGrid.LimitAlertMatchesFilter(limitAlert) && null != limitAlert && limitAlert.Triggered.HasValue && null != limitAlert.IntradayPerformance) { DateTime referenceDate = limitAlert.Triggered.Value; DateTime marketOpen = GuiEnvironment.GetMarketOpenLocalTime(); if (referenceDate < marketOpen) referenceDate = marketOpen; TimeSpan sinceTrigger = tradingMinute - referenceDate; int index = (int)sinceTrigger.TotalMinutes; //GuiEnvironment.LogMessage("GetTotalProfit for " + tradingMinute.ToShortTimeString() + " symbol: " + limitAlert.Symbol + ", triggered: " + limitAlert.Triggered.Value.ToString() + ", index: " + index); if (index >= 0 && limitAlert.IntradayPerformance.Values.Count >= index + 1) { double profitDollars = limitAlert.IntradayPerformance.Values[index]; //GuiEnvironment.LogMessage("GetTotalProfit for " + tradingMinute.ToShortTimeString() + " adding symbol: " + limitAlert.Symbol + ", triggered: " + limitAlert.Triggered.Value.ToString() + ", index: " + index + ", profit=" + profitDollars + ", totalcount=" + limitAlert.IntradayPerformance.Values.Count); allProfits.Add(profitDollars); } else { //GuiEnvironment.LogMessage("GetTotalProfit for " + tradingMinute.ToShortTimeString() + " NOT adding symbol: " + limitAlert.Symbol + ", triggered: " + limitAlert.Triggered.Value.ToString() + ", index: " + index + ", totalcount=" + limitAlert.IntradayPerformance.Values.Count); } } } double? profit = null; if (allProfits.Count > 0) profit = allProfits.Sum(); //GuiEnvironment.LogMessage("GetTotalProfit for " + tradingMinute.ToShortTimeString() + " triggered alerts: " + triggeredAlerts + ", profits: " + allProfits.Count + ", profit=" + profit); return profit; } private DateTime? GetFirstTriggered() { DateTime? firstTriggered = null; foreach (LimitAlert limitAlert in GuiEnvironment.LimitAlerts.ToList()) { if (null != _limitAlertsGrid && _limitAlertsGrid.LimitAlertMatchesFilter(limitAlert) && null != limitAlert && limitAlert.Triggered.HasValue && (!firstTriggered.HasValue || limitAlert.Triggered.Value < firstTriggered.Value)) firstTriggered = limitAlert.Triggered; } return firstTriggered; } private void LimitAlertsForm_VisibleChanged_1(object sender, EventArgs e) { if (Visible) { OnDemoModeChanged(); } } private void LimitAlertsForm_VisibleChanged_2(object sender, EventArgs e) { if (GuiEnvironment.CLICKED_MAIN_FORM_MENU) { GuiEnvironment.CLICKED_MAIN_FORM_MENU = false; GuiEnvironment.SetWindowOpeningPosition(this, null); } } public List Surf(int total) { DataGridView grid = _limitAlertsGrid.getDataGridView(); List topRows = new List(); foreach (DataGridViewRow row in grid.Rows) { RowData rowData = row.DataBoundItem as RowData; if (null != rowData) topRows.Add(rowData); if (topRows.Count >= total) break; } return topRows; } public void SelectItem(RowData rowData) { DataGridView grid = _limitAlertsGrid.getDataGridView(); grid.ClearSelection(); foreach (DataGridViewRow row in grid.Rows) { RowData thisRowData = row.DataBoundItem as RowData; if (null != thisRowData && rowData == thisRowData) { foreach (DataGridViewCell cell in row.Cells) cell.Selected = true; } } string symbol = rowData.GetSymbol(); int selectedRows = grid.SelectedRows.Count; Debug.WriteLine("[Surf SelectItem PriceAlerts] selected " + symbol + " total selected: " + selectedRows); } public void PostSurfingLogic() { // Add post surfing logic as required. } /// /// List of callbacks to notify surf managers that new items are available. /// private List> _dataRefreshCallbacks = new List>(); public void AddDataRefreshedCallback(Action callback) { if (!_dataRefreshCallbacks.Contains(callback)) _dataRefreshCallbacks.Add(callback); } private void NotifySurfers() { foreach (Action dataRefreshCallback in _dataRefreshCallbacks) dataRefreshCallback(this); } public void AddNewEventCallback(Action callback) { } public SurfStatus SurfStatus() { return Surfer.SurfStatus.Available; } public string WindowTitle() { return Text; } public DataGridView GetDataGridView() { return _limitAlertsGrid.getDataGridView(); } public Control GetOwner() { return this; } private void LimitAlertsForm_TextChanged(object sender, EventArgs e) { // change Text of parent form if running in dock panel mode - RVH20210329 if (_dockPanelMode) { Form parent = (Form)this.Parent; if (parent != null) parent.Text = this.Text; } } private void LimitAlertsForm_Shown(object sender, EventArgs e) { // force calling of TextChanged event to update parent form text - RVH20210428 LimitAlertsForm_TextChanged(null, null); } /// /// Set the symbol link channel from symbol linking /// /// The link channel void ISymbolLinkingChannel.SetLinkChannel(string linkChannel) { _limitAlertsGrid.SetLinkChannel(linkChannel); } /// /// Get the symbol link channel /// /// string ISymbolLinkingChannel.GetLinkChannel() { return _limitAlertsGrid.GetLinkChannel(); } /// /// Set the symbol link channel /// /// The link channel public void SetLinkChannel(string linkChannel) { // change window icon SymbolLinkingChannelsHelper.SetFormIcon(this, "P", linkChannel); } } public enum LimitAlertStatus { TRIGGERED, WORKING, EXPIRED, INVALIDATED }; }