using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using System.Windows.Forms.DataVisualization.Charting; using System.Xml; using TradeIdeas.XML; namespace TradeIdeas.TIProGUI.CBT { [ToolboxItem(false)] public partial class ConfigDemoDailyRanges : UserControl, IChild { private readonly IContainer _container; private readonly Series _mainSeries; private readonly DataPoint _today; private readonly Annotation _topFlat; private readonly Annotation _bottomFlat; private readonly Annotation _topAngle; private readonly Annotation _bottomAngle; private readonly int _fixedDays; private MinMaxSummary _minMaxSummary; /// /// How wide is the range. This is set by . /// private int _daysToExamine; /// /// How many candles do we draw before the range. This is set by . /// private int _daysOfContext; /// /// This is set by . /// private ConfigDemoConsolidationDays.Stock _stock; /// /// The top of the range. This is set by /// private double _high; /// /// The bottom of the range. This is set by /// private double _low; /// /// This is the value we are trying to display. This is set by . /// private double? _currentValue; private Random _random = new Random(); /// /// Draw the parts of the chart that only change with each animation step. /// Ignore the parts that will change with the user input. /// Those will be handled by . /// will use some member variables, like /// , which are set by this method. /// private void UpdateAnimation() { _stock = ConfigDemoConsolidationDays.Stock.RANDOM_SAMPLE; stockSymbolLabel.Text = _stock.Symbol; stockDescriptionLabel.Text = _stock.Description; if (Consolidation) { // We know that 0 is the index of the first day of the consolidation pattern. We grab all // of the context that is available. We grab different amounts of the consolidation, so // the ranges will look different. _daysOfContext = -_stock.FirstDay; _daysToExamine = _random.Next(3, _stock.LastDay + 1); } else { // Add or remove days from the end of the chart so the range will look different. _daysToExamine = _fixedDays; int totalDaysAvailable = _stock.LastDay - _stock.FirstDay + 1; int contextAvailable = totalDaysAvailable - _daysToExamine; // Try to show at least 5 days of context. But if we don't have enough days, // show less context. _daysOfContext = _random.Next(Math.Min(5, contextAvailable), contextAvailable); } _mainSeries.Points.Clear(); for (int i = 0; i < _daysOfContext; i++) { ConfigDemoConsolidationDays.Day day = _stock.GetFromZero(i); DataPoint point = new DataPoint(_mainSeries); point.AxisLabel = day.Date; point.YValues = day.Values; point.Color = day.FadedColor(); _mainSeries.Points.Add(point); } _high = double.MinValue; _low = double.MaxValue; for (int i = 0; i < _daysToExamine; i++) { ConfigDemoConsolidationDays.Day day = _stock.GetFromZero(i + _daysOfContext); _high = Math.Max(_high, day.High); _low = Math.Min(_low, day.Low); DataPoint point = new DataPoint(_mainSeries); point.AxisLabel = day.Date; point.YValues = day.Values; point.Color = day.Color(); _mainSeries.Points.Add(point); } _mainSeries.Points.Add(_today); _topFlat.X = _daysOfContext + 0.5; _topFlat.Width = _daysToExamine; _topFlat.Y = _high; _bottomFlat.X = _daysOfContext + 0.5; _bottomFlat.Width = _daysToExamine; _bottomFlat.Y = _low; _topAngle.X = _daysOfContext + _daysToExamine + 0.5; _topAngle.Y = _high; _bottomAngle.X = _daysOfContext + _daysToExamine + 0.5; _bottomAngle.Y = _low; _minMaxSummary.X = _daysOfContext + _daysToExamine + 1; UpdateChartCommon(); } public static void SaveAll() { Dictionary result = new Dictionary(); foreach (XmlNode filter in ConfigDemoUserControl.ConfigDemoXml.Node("DAILY_RANGES").Enum()) { string filterCode = filter.Property("CODE"); int value = filter.Property("VALUE", CONSOLIDATION); ConfigDemoUserControl.Add(filterCode, delegate(IContainer container) { return new ConfigDemoDailyRanges(container, value); }); } } private const int CONSOLIDATION = -1; private bool Consolidation { get { return _fixedDays == CONSOLIDATION; } } public ConfigDemoDailyRanges(IContainer container, int fixedDays = CONSOLIDATION) { _container = container; _fixedDays = fixedDays; InitializeComponent(); int initialHeight = stockSymbolLabel.Height; stockSymbolLabel.Font = new Font(stockSymbolLabel.Font.FontFamily, stockSymbolLabel.Font.SizeInPoints * 2, stockSymbolLabel.Font.Style); stockDescriptionLabel.Top += stockSymbolLabel.Height - initialHeight; _mainSeries = chart1.Series[0]; _today = new DataPoint(_mainSeries); _today.AxisLabel = "Today"; _today.Color = ConfigDemoConsolidationDays.Day.FADED_BLACK; _topFlat = chart1.Annotations["TopFlat"]; _bottomFlat = chart1.Annotations["BottomFlat"]; _topAngle = chart1.Annotations["TopAngle"]; _bottomAngle = chart1.Annotations["BottomAngle"]; _minMaxSummary = new MinMaxSummary(chart1); UpdateAnimation(); UpdateFromTextBox(); _container.MinTextBox.TextChanged += UpdateFromTextBox; _container.MaxTextBox.TextChanged += UpdateFromTextBox; minPictureBox.Image = container.ConnectionMaster.ImageCacheManager.GetFilter("Min" + container.InternalCode); maxPictureBox.Image = container.ConnectionMaster.ImageCacheManager.GetFilter("Max" + container.InternalCode); } /// /// Moving the track bar by one tick will change the value in the text box by this much. /// private const double TICKS_PER_VALUE = 10; /// /// This is called when the user changes the track bar. /// This is not called when the program changes it. /// /// unused /// unused private void trackBar1_Scroll(object sender, EventArgs e) { double value = trackBar1.Value * TICKS_PER_VALUE; if (minRadioButton.Checked) _container.MinTextBox.Text = value.ToString(); else _container.MaxTextBox.Text = value.ToString(); } private void UpdateFromTextBox(object sender, EventArgs e) { UpdateFromTextBox(); } private void UpdateFromTextBox() { if (minRadioButton.Checked) _currentValue = _container.MinValue; else _currentValue = _container.MaxValue; if (_currentValue.HasValue) { double forTrackBar = Math.Round(_currentValue.Value / TICKS_PER_VALUE); if (forTrackBar > trackBar1.Maximum) trackBar1.Value = trackBar1.Maximum; else if (forTrackBar < trackBar1.Minimum) trackBar1.Value = trackBar1.Minimum; else trackBar1.Value = (int)forTrackBar; } UpdateChartCommon(); } private static readonly double[] EMPTY_POINT = new double[] { 0, 0, 0, 0 }; private double FilterValueToPrice(double filterValue) { return filterValue / 100 * (_high - _low) + _low; } private double? FilterValueToPrice(double? filterValue) { if (!filterValue.HasValue) return null; else return FilterValueToPrice(filterValue.Value); } private void UpdateChartCommon() { _minMaxSummary.MinValue = FilterValueToPrice(_container.MinValue); _minMaxSummary.MaxValue = FilterValueToPrice(_container.MaxValue); if ((!_currentValue.HasValue) || (_low >= _high) || (_currentValue > 5000) || (_currentValue < -5000)) { _today.IsEmpty = true; // Before I changed the YValues, I'd often see a think flat gray line where the last price used to be. _today.YValues = EMPTY_POINT; _topAngle.Visible = false; _bottomAngle.Visible = false; } else { double price = FilterValueToPrice(_currentValue.Value); _today.IsEmpty = false; _today.YValues = new double[] { price, price, price, price }; _topAngle.Visible = true; _topAngle.Height = price - _topAngle.Y; _bottomAngle.Visible = true; _bottomAngle.Height = price - _bottomAngle.Y; } // Make sure we recalculate this before the update. Otherwise it gets drawn in the wrong place! // It seems that the update is normally done in the background, possibly in an idle event, and // I can't capture that event. Ideally I'd do my update in that event handler. I actually liked // the effect when we didn't manually call this. If the user moved off the top, we wouldn't // immediately update. chart1.ChartAreas[0].RecalculateAxesScale(); _minMaxSummary.Update(); } string IChild.Name { get { return "Chart"; } } Control IChild.Body { get { return this; } } bool IChild.CanShowExample { get { return true; } } void IChild.OnShow() { // If there is only one interesting value, start by showing that value on the screen. if (_container.MaxValue.HasValue && !_container.MinValue.HasValue) maxRadioButton.Checked = true; else if (_container.MinValue.HasValue && !_container.MaxValue.HasValue) minRadioButton.Checked = true; } private void timer1_Tick(object sender, EventArgs e) { UpdateAnimation(); } } }