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;
namespace TradeIdeas.TIProGUI.CBT
{
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();
private static readonly Color FADED_RED = Color.FromArgb(128+64, 128, 128);
private static readonly Color FADED_GREEN = Color.FromArgb(128, 128 + 64, 128);
private static readonly Color FADED_BLACK = Color.FromArgb(128, 128, 128);
///
/// 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.Color(FADED_GREEN, FADED_RED, FADED_BLACK);
_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();
}
private bool Consolidation { get { return _fixedDays == -1; } }
public ConfigDemoDailyRanges(IContainer container, int fixedDays = -1)
{
_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 = 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();
}
}
}