using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Linq; using System.Windows.Forms; using TradeIdeas.TIProData; using TradeIdeas.TIProData.Configuration; using TradeIdeas.TIProData.Interfaces; namespace TradeIdeas.TIProGUI.CBT { [ToolboxItem(false)] public partial class ConfigDemoHistogram : UserControl, IChild { private readonly IContainer _container; public ConfigDemoHistogram(IContainer container, ConfigurationWindowManager configurationWindowManager, PrepairedStrategy currentStrategy) { _container = container; _configurationWindowManager = configurationWindowManager; _currentStrategy = currentStrategy; InitializeComponent(); // Try to init now because we have the data we need. For a top list this will be perfect. // For an alert window we might send out a request that will eventually be answered. This // structure is simpler for the main program which can but doesn't have to call // HistogramInit() directly. HistogramInit(configurationWindowManager, _container.ConnectionMaster); // Let's just make this an invariant. This will always be set to one of the three known // values, if this tab sheet is visible. // Note that this next line is also responsible for requesting the data, just like if the // user had changed that combo box. histogramBackgroundFiltersComboBox.SelectedIndex = HISTOGRAM_FILTER_TYPE_MARKET_IN_GENERAL; //HistogramAddDemoData(); // This is useful sometimes in debugging. container.MinTextBox.TextChanged += delegate(object sender, EventArgs e) { HistogramUpdateMinFromTextBox(); }; container.MaxTextBox.TextChanged += delegate(object sender, EventArgs e) { HistogramUpdateMaxFromTextBox(); }; UpdateImages(); } private void UpdateImages() { histogramMinPictureBox.Image = _container.ConnectionMaster.ImageCacheManager.GetFilter("Min" + _container.InternalCode); ; histogramMaxPictureBox.Image = _container.ConnectionMaster.ImageCacheManager.GetFilter("Max" + _container.InternalCode); } /// /// Allow us to make different types of requests at any time, not just in the constructor. /// private readonly ConfigurationWindowManager _configurationWindowManager; private readonly PrepairedStrategy _currentStrategy; /// /// Does it make sense to try to display the histogram? /// /// This is not a guarantee that we will have any meaningful data. Because the data is on the server /// side, and it can possibly even change while we are running, the client must be able to display /// a histogram without crashing, if this is wrong. /// /// If we are not sure, we typically say yes. (But we can err in the other direction, too.) /// /// Standard internal code for the filter. I.e. "Price" "BS", etc. /// True if it's reasonable to try to display this. static public bool Available(string code) { if (null == _histogramAvailable) // When we are not sure, we say everything is available. return true; // This is still an approximation as this information can change on the server side at // any time. return _histogramAvailable.Contains(code); // Test cases: // Price: Exists for both histogram and words. // StockTwits average volume: Exists for only histogram. // (There is no particularly good reason this is not in words. It's probably just too // new and hasn't been created yet. So that could change.) // Bid Size: Exists for only words. // Decimal: Exists for neither. } /// /// This is a list of filter codes for which we know how to draw a histogram. /// null means that we don't know. In that case we typically say everything is legal. /// /// This is volatile, so that initialization can easily happen from any thread. The /// HashSet object can never change, but this variable can point to a new hashset at /// any time. /// /// The rule is that this starts null and can be replaced by a new hashset at any /// time. This variable can stay null for any amount of time, but it can never go /// back to null after it has been filled in. /// /// We use some very week requirements here to make this easy to use as a library /// element. Ideally we'd require the main program to give us the information we /// need up front. /// /// Note that we only have one of these. Having one per SendManager might be more /// consistant with the rest of the code. If you are connected to two different /// servers, these might not be identical. In practice, we try to keep Xetra very /// similar to the US servers, with similar names, but even so, Xetra is usually /// behind and will be missing a few values. /// static private volatile HashSet _histogramAvailable; /// /// The config window manager automatically retries forever. So there is no reason /// to create more than one of these requests. Use this variable to determine if /// we've already sent the request. This is initially false and can be set to true /// exactly once. Lock when reading or writing this variable. /// static private bool _histogramInitRequestSent; /// /// This is used to protect . /// static private object _lockObject = new object(); /// /// Try to initialize the list of what's available. /// This function can be called from any thread. /// /// This can be null. /// This can be null. static public void HistogramInit(ConfigurationWindowManager configurationWindowManager, IConnectionMaster connectionMaster) { if ((null != configurationWindowManager) && configurationWindowManager.Loaded && (configurationWindowManager.ConfigurationType == ConfigurationType.TopList)) { // Always try to use this data if we have it. Assume that it might be more current // than the data we've already recorded. That's not completely unreasonable because // the user defined filters can be changed at any time. HashSet newSet = new HashSet(); // Note the assumption that exactly the same filters that are available in the // top list are available here. If that assumption changes, we'll have to make // bigger changes than this. We use a TopListStrategy to encode the request to // the server. foreach (Filter filter in configurationWindowManager.FiltersInOrder) newSet.Add(filter.InternalCode); _histogramAvailable = newSet; } else if ((null != connectionMaster) && (null == _histogramAvailable)) { // We consider this action more expensive, so we don't use it unless we currently // have no data at all. bool createNewRequest = false; lock (_lockObject) { if (!_histogramInitRequestSent) { createNewRequest = true; _histogramInitRequestSent = true; } } if (createNewRequest) { configurationWindowManager = new ConfigurationWindowManager(); configurationWindowManager.LoadFromServer(connectionMaster, ConfigurationType.TopList, delegate(ConfigurationWindowManager configurationWindowManager1) { HistogramInit(configurationWindowManager1, null); }); } } } private HistogramRequestBuilder _pendingHistogramRequest; private void OnHistogramResponse(HistogramRequest request) { if (InvokeRequired) Invoke((MethodInvoker)delegate { OnHistogramResponse(request); }); else if ((_pendingHistogramRequest == request) && (!IsDisposed)) { if (null == request.Result) { // Connection problem. Try again. request.SendRequest(); } else { _pendingHistogramRequest = null; _histogramSelections = request.Result; _histogramTotalCount = request.TotalCount; UpdateHistogramData(); } } } /// /// These are all the positions on the histogram that the user can select. Note that /// these are not the bars in the histogram, but the spaces between the bars. Also, /// there will be one of these to the left of the leftmost bar, and one to the right /// of the right most bar. The use can select any one of these using the track bar. /// The user can also select two additional items on the track bar; all the way on /// either side is a blank. This list might be empy, but never. /// private List _histogramSelections = new List(); /// /// The total number of stocks available. This might be more than if you select the lowest /// value for min and the largest value for max. This also includes stocks where the current /// filter is null. /// private int _histogramTotalCount = 0; private void HistogramAddDemoData() { _histogramSelections.Clear(); HistogramStyle desiredStyle = HistogramStyle.Discrete; HistogramSelection hs; if (desiredStyle == HistogramStyle.Continuous) { // Start with a simple version of a bell curve. This is all continuous // data, with no values at exactly the selected numbers. hs.cumulativeLessCount = 0; hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.value = 10; _histogramSelections.Add(hs); hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.cumulativeLessCount += 1; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.cumulativeLessCount += 6; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.cumulativeLessCount += 15; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.cumulativeLessCount += 20; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.cumulativeLessCount += 15; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.cumulativeLessCount += 6; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.cumulativeLessCount += 1; hs.value += 10; _histogramSelections.Add(hs); } else { // Discrete. hs.cumulativeLessCount = 0; hs.cumulativeMoreCount = hs.cumulativeLessCount; hs.value = 10; //_histogramSelections.Add(hs); hs.cumulativeLessCount = hs.cumulativeMoreCount; hs.cumulativeMoreCount += 1; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeLessCount = hs.cumulativeMoreCount; hs.cumulativeMoreCount += 6; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeLessCount = hs.cumulativeMoreCount; hs.cumulativeMoreCount += 15; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeLessCount = hs.cumulativeMoreCount; hs.cumulativeMoreCount += 20; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeLessCount = hs.cumulativeMoreCount; hs.cumulativeMoreCount += 15; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeLessCount = hs.cumulativeMoreCount; hs.cumulativeMoreCount += 6; hs.value += 10; _histogramSelections.Add(hs); hs.cumulativeLessCount = hs.cumulativeMoreCount; hs.cumulativeMoreCount += 1; hs.value += 10; _histogramSelections.Add(hs); } // Add a few extras where the filter value is null. _histogramTotalCount = hs.cumulativeMoreCount + 100; // Any time the data changes you should call this. UpdateHistogramData(); } private void UpdateHistogramData() { // Set the extremes of the track bars. This is required for some invarients used // in several places. histogramMinTrackBar.Minimum = -1; histogramMinTrackBar.Maximum = _histogramSelections.Count; histogramMaxTrackBar.Minimum = -1; histogramMaxTrackBar.Maximum = _histogramSelections.Count; // Try to pick the best display style, continuous vs. discrete. int totalCount = 0; int atCount = 0; if (_histogramSelections.Count > 0) { totalCount = HistogramGetAbsoluteMaxCount() - HistogramGetAbsoluteMinCount(); foreach (HistogramSelection hs in _histogramSelections) { atCount += hs.cumulativeMoreCount - hs.cumulativeLessCount; } } int betweenCount = totalCount - atCount; // This makes some since if we assume that the data is close to perfectly // matching one or the other. That is to say there is almost nothing between // the selectable items, or almost nothing exactly at the selectable items. if (atCount >= betweenCount) _histogramStyle = HistogramStyle.Discrete; else _histogramStyle = HistogramStyle.Continuous; // Redraw everything that depends on this data. // Update the position of the trackbars, but not the text in the edit controls. _histogramUpdateInProgress = true; UpdateHistogramMinTrackBar(); UpdateHistogramMaxTrackBar(); _histogramUpdateInProgress = false; // Sometimes this is automatic. But when the min and max are both blank, typically // this explicit call is required. HistogramUpdateTotals(); mainHistogramPictureBox.Invalidate(); } private void UpdateHistogramMinTrackBar() { UpdateHistogramTrackBar(_container.MinValue, histogramMinTrackBar, histogramMinTrackBar.Minimum); } private void UpdateHistogramMaxTrackBar() { UpdateHistogramTrackBar(_container.MaxValue, histogramMaxTrackBar, histogramMaxTrackBar.Maximum); } /// /// Update one of the track bars to match the other data. For example, when we first /// initialize, or when the user changes one of the values by typing in a text field. /// /// Note that the track bar has a limited number of positions. The other types of input /// may select a value which does not perfectly match any position on the track bar. /// In that case we try to pick the nearest tickmark. /// /// The value to find. /// Where to store the value. /// Where to leave the slider if we can't find the value. /// This could happen if the input value is null or the list of possible selections /// is empty. private void UpdateHistogramTrackBar(double? newValue, TrackBar trackBar, int defaultValue) { if ((!newValue.HasValue) || (_histogramSelections.Count == 0)) { trackBar.Value = defaultValue; return; } double value = newValue.Value; if (value <= _histogramSelections[0].value) { trackBar.Value = 0; return; } for (int right = 1; right < _histogramSelections.Count; right++) { int left = right - 1; double middle = (_histogramSelections[left].value + _histogramSelections[right].value) / 2.0; if (value < middle) { trackBar.Value = left; return; } } trackBar.Value = _histogramSelections.Count - 1; } /// /// This means that we are updating the min or max value. Normally, /// when a user types in the text box, that changes the track bar, and /// when the user changes the track bar, that changes the tex box. /// This keeps us from an infinate loop. What's more, If a user enters /// a number that does not perfectly match the track bar, the track /// bar should update to the clost position, but the text box should /// stay the same. If the user uses a slider, the text box will only /// display one of a small number of possible values. /// private bool _histogramUpdateInProgress = false; private void HistogramTrackBarScroll(TextBox textBox, TrackBar trackBar) { if (!_histogramUpdateInProgress) { _histogramUpdateInProgress = true; if ((trackBar.Value < 0) || (trackBar.Value >= _histogramSelections.Count)) textBox.Text = ""; else // TODO -- Format this according to the normal formatting rules. textBox.Text = _histogramSelections[trackBar.Value].value.ToString(); _histogramUpdateInProgress = false; } HistogramUpdateTotals(); } private HistogramSelection? HistogramGetSelection(int index) { if ((index < 0) || (index >= _histogramSelections.Count)) return null; return _histogramSelections[index]; } /// /// What is the highest count visible on the histogram? /// Precondition: is not empty. /// /// The value on the far right side of the histogram. private int HistogramGetAbsoluteMaxCount() { return _histogramSelections[_histogramSelections.Count - 1].cumulativeMoreCount; } private int HistogramGetAbsoluteMinCount() { // It is tempting to try to compute this. But the way it's used, this // seems list the most correct possible value. return 0; } private int HistogramStyleAdjustedMax() { if (_histogramStyle == HistogramStyle.Continuous) // The numbers on the track bar refer to the spaces between the bars. // If the min and max track bar are in the same place, they select nothing. return histogramMaxTrackBar.Value; else // The numbers on the track bar refer to the bars. If min and max are // both set to 0, we want to select only the first bar (i.e. bar #0). return histogramMaxTrackBar.Value + 1; } private void HistogramUpdateTotals() { int count; HistogramSelection? min = HistogramGetSelection(histogramMinTrackBar.Value); HistogramSelection? max = HistogramGetSelection(histogramMaxTrackBar.Value); if ((!min.HasValue) && (!max.HasValue)) { // If both are empty then we include everything, including some stocks with a null value for this filter. count = _histogramTotalCount; _barIsIncluded = delegate { return true; }; } else if (!max.HasValue) { // Min is provided but no max. Assume he seleccted the highest value for the max. count = HistogramGetAbsoluteMaxCount() - min.Value.cumulativeLessCount; _barIsIncluded = delegate(int index) { return index >= histogramMinTrackBar.Value; }; } else if (!min.HasValue) { // Max is provided, but not min. count = max.Value.cumulativeMoreCount - HistogramGetAbsoluteMinCount(); _barIsIncluded = delegate(int index) { return index < HistogramStyleAdjustedMax(); }; } else if (histogramMaxTrackBar.Value >= histogramMinTrackBar.Value) { // See what is between the two values. count = max.Value.cumulativeMoreCount - min.Value.cumulativeLessCount; _barIsIncluded = delegate(int index) { return (index >= histogramMinTrackBar.Value) && (index < HistogramStyleAdjustedMax()); }; } else { // See what is outside of the two values. count = (HistogramGetAbsoluteMaxCount() - min.Value.cumulativeLessCount) + (max.Value.cumulativeMoreCount - HistogramGetAbsoluteMinCount()); _barIsIncluded = delegate(int index) { return (index >= histogramMinTrackBar.Value) || (index < HistogramStyleAdjustedMax()); }; } histogramTotalLabel.Text = "Count: " + count; mainHistogramPictureBox.Invalidate(); } private void HistogramUpdateMinFromTextBox() { if (!_histogramUpdateInProgress) { _histogramUpdateInProgress = true; UpdateHistogramMinTrackBar(); _histogramUpdateInProgress = false; } } private void HistogramUpdateMaxFromTextBox() { if (!_histogramUpdateInProgress) { _histogramUpdateInProgress = true; UpdateHistogramMaxTrackBar(); _histogramUpdateInProgress = false; } } private void histogramMinTrackBar_ValueChanged(object sender, EventArgs e) { HistogramTrackBarScroll(_container.MinTextBox, histogramMinTrackBar); } private void histogramMaxTrackBar_ValueChanged(object sender, EventArgs e) { HistogramTrackBarScroll(_container.MaxTextBox, histogramMaxTrackBar); } private static readonly Brush HISTOGRAM_BACKGROUND = new SolidBrush(Color.FromArgb(255, 255, 255)); private static readonly Brush HISTOGRAM_USED_BAR = new SolidBrush(Color.FromArgb(3, 100, 151)); private static readonly Brush HISTOGRAM_UNUSED_BAR = new SolidBrush(Color.FromArgb(205, 205, 205)); private static readonly Brush MIN_ICON_COLOR = new SolidBrush(Color.FromArgb(255, 0, 192, 192)); private static readonly Brush MAX_ICON_COLOR = new SolidBrush(Color.FromArgb(255, 158, 0, 192)); private delegate bool BarIsIncluded(int index); /// /// This function tells you whether or not a bar in the histogram will be included in the /// window based on the current filter values. 0 is the bar on the far left. This can /// be used to draw the bars in a different color to show the user what currently is or /// is not included. /// private BarIsIncluded _barIsIncluded = delegate { return false; }; /// /// This says which data we want to highlight on our histogram, the number of symbols /// between any two consecutive data points or the number of symbols at each data point. /// /// Up days for example would probably be labeled as "At". We will have a small number /// of values to choose from, all integers. For example, maybe -5, -4, -3, -2, -1, 0, /// 1, 2, 3, 4, 5. The user will be able to select any one of those numbers from the /// track bar. And we want to see one bar on the histogram for each of those values. /// /// RSI is on the other extreme. We might be able to select values like 0.0, 0.2, 0.4, /// 06, 0.8, and 1.0. But we don't expect any stocks to exactly match these values. /// Each bar on the histogram will correspond to an adjacent pair of these. Bar 0 will /// show how many stocks have an RSI "Between" 0.0 and 0.2. So if the track bar can /// select 6 values, we only display 5 bars in the histogram. /// /// Of course, these are the extremes. Price will be similar to RSI, but we actually /// expect to see some stocks which exactly match the given price. And it could even /// happen for RSI, although that might be rare. And, up days could also have some /// values in between the numbers we select. If there are too many actual value, we /// might combine some into the same bar. But this answers the basic question, if the /// track bar can display N points, how many bars are on the histogram. /// private enum HistogramStyle { Continuous, Discrete }; private HistogramStyle _histogramStyle; private void mainHistogramPictureBox_Paint(object sender, PaintEventArgs e) { Rectangle everything = new Rectangle(0, 0, mainHistogramPictureBox.Width, mainHistogramPictureBox.Height); e.Graphics.FillRectangle(HISTOGRAM_BACKGROUND, everything); if (_histogramStyle == HistogramStyle.Continuous) { // Draw one bar between each consecutive pair of data points. if (_histogramSelections.Count < 2) return; } else { // Draw one bar for each data point. if (_histogramSelections.Count < 1) return; } float selectorReservedHeight = 6.0f; if (everything.Height < selectorReservedHeight * 3) return; List barHeights = new List(); if (_histogramStyle == HistogramStyle.Continuous) { for (int left = 0; left < _histogramSelections.Count - 1; left++) barHeights.Add(_histogramSelections[left + 1].cumulativeLessCount - _histogramSelections[left].cumulativeLessCount); } else { foreach (HistogramSelection hs in _histogramSelections) { // For simplicity I'm just ignoring what's between the bars. It would be an inexact approximation // no matter what. I suppose we could add a third style which shows bars for both the exact positions // and the stuff between these positions. That would only make sense if there was a lot of data // in both places, otherwise it would probably add more confusion than it solved (even if it was // more precise and accurate). Our general assumption at this time is that one of the existing // styles will be a close match for all of our data. barHeights.Add(hs.cumulativeMoreCount - hs.cumulativeLessCount); } } int maxCount = barHeights.Max(); if (maxCount <= 0) return; RectangleF body = new RectangleF(); body.X = everything.X; body.Y = everything.Y; body.Height = everything.Height - selectorReservedHeight; body.Width = everything.Width; float totalBarWidth = body.Width / (barHeights.Count + 1); float barPadding = totalBarWidth / 5.0f; float filledBarWidth = totalBarWidth - barPadding; // The selector should look like an L. It should touch the side and bottom of // the bar. float selectorWidth = Math.Min(2 * selectorReservedHeight, barPadding); float selectorHeight = selectorReservedHeight * 3; HistogramSelection? min = HistogramGetSelection(histogramMinTrackBar.Value); HistogramSelection? max = HistogramGetSelection(histogramMaxTrackBar.Value); if ((_histogramStyle == HistogramStyle.Continuous) && min.HasValue && max.HasValue && (histogramMinTrackBar.Value == histogramMaxTrackBar.Value)) { // The drawing code has to treat this as a special case. The min and max are so close that // we expect little or no data between them. If we didn't make this a special case, it would // look like we were only excluding a very small range, rather than only including a very // small range. RectangleF selector = new RectangleF(); selector.Width = selectorWidth / 2; selector.X = body.X + (histogramMinTrackBar.Value + 0.5f) * totalBarWidth - selector.Width; selector.Height = selectorHeight; selector.Y = everything.Y + (everything.Height - selectorHeight); e.Graphics.FillRectangle(MIN_ICON_COLOR, selector); selector.X = selector.X + selector.Width; e.Graphics.FillRectangle(MAX_ICON_COLOR, selector); } else { if (min.HasValue) { // Draw the min bracket. RectangleF selector = new RectangleF(); // First draw the taller part, just to the left of the bar. selector.X = body.X + (histogramMinTrackBar.Value + 0.5f) * totalBarWidth + barPadding * 0.5f - selectorWidth / 2; selector.Width = selectorWidth / 2; selector.Height = selectorHeight; selector.Y = everything.Y + (everything.Height - selectorHeight); e.Graphics.FillRectangle(MIN_ICON_COLOR, selector); // Then draw the shorter part right under the bar. selector.X = selector.X + selectorWidth / 2; selector.Height = selectorReservedHeight; selector.Y = everything.Y + (everything.Height - selector.Height); selector.Width = filledBarWidth / 2; e.Graphics.FillRectangle(MIN_ICON_COLOR, selector); } if (max.HasValue) { // Draw the max bracket. RectangleF selector = new RectangleF(); // First draw the shorter part right under the bar. selector.X = body.X + (histogramMaxTrackBar.Value + 0.5f) * totalBarWidth - totalBarWidth / 2; if (_histogramStyle == HistogramStyle.Discrete) selector.X += totalBarWidth; selector.Width = filledBarWidth / 2; selector.Height = selectorReservedHeight; selector.Y = everything.Y + (everything.Height - selector.Height); e.Graphics.FillRectangle(MAX_ICON_COLOR, selector); // Then draw the taller part to the right of the bar. selector.X = selector.X + filledBarWidth / 2; selector.Height = selectorHeight; selector.Y = everything.Y + (everything.Height - selectorHeight); selector.Width = selectorWidth / 2; e.Graphics.FillRectangle(MAX_ICON_COLOR, selector); } } int index = 0; foreach (int h in barHeights) { RectangleF bar = new RectangleF(); bar.X = body.X + (index + 0.5f) * totalBarWidth + barPadding * 0.5f; bar.Width = filledBarWidth; bar.Height = h / (float)maxCount * body.Height; bar.Y = body.Y + (body.Height - bar.Height); e.Graphics.FillRectangle(_barIsIncluded(index) ? HISTOGRAM_USED_BAR : HISTOGRAM_UNUSED_BAR, bar); index++; } } private void HistogramRequestDataNow() { // There is no explicit way to cancel an old request. When we get a response, we compare the request to // _pendingHistogramRequest and ignore it if it is out of date. _pendingHistogramRequest = new HistogramRequestBuilder(_configurationWindowManager, _container.ConnectionMaster.SendManager); _pendingHistogramRequest.FilterString = _container.InternalCode; _pendingHistogramRequest.ResponseFromServer += OnHistogramResponse; switch (histogramBackgroundFiltersComboBox.SelectedIndex) { case HISTOGRAM_FILTER_TYPE_MARKET_IN_GENERAL: // Leave the defaults. break; case HISTOGRAM_FILTER_TYPE_MY_EXCHANGES: _pendingHistogramRequest.BaseStrategy = new TopListStrategy(); _pendingHistogramRequest.BaseStrategy.Exchanges = new HashSet(_currentStrategy.Exchanges); break; case HISTOGRAM_FILTER_TYPE_ALL_MY_FILTERS: _pendingHistogramRequest.BaseStrategy = new TopListStrategy(_currentStrategy); _pendingHistogramRequest.BaseStrategy.History = false; break; } _pendingHistogramRequest.SendRequest(); } private void histogramBackgroundFiltersComboBox_SelectedIndexChanged(object sender, EventArgs e) { HistogramRequestDataNow(); } /// /// This is a value for histogramBackgroundFiltersComboBox.SelectedIndex. /// This means that we are ignoring all of the users filters filling in the histogram /// based on a large number of stocks. /// private const int HISTOGRAM_FILTER_TYPE_MARKET_IN_GENERAL = 0; /// /// This is a value for histogramBackgroundFiltersComboBox.SelectedIndex. /// This means to look at all stocks on one of the exchanges selected by the user. /// private const int HISTOGRAM_FILTER_TYPE_MY_EXCHANGES = 1; /// /// This is a value for histogramBackgroundFiltersComboBox.SelectedIndex. /// This means to look at all of the users settings (filters, exchanges, symbol /// lists) except for the filter we want to build a histogram on. /// private const int HISTOGRAM_FILTER_TYPE_ALL_MY_FILTERS = 2; string IChild.Name { get { return "Histogram"; } } Control IChild.Body { get { return this; } } bool IChild.CanShowExample { get { return false; } } void IChild.OnShow() { } } }