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 SendManager = TradeIdeas.TIProData.SendManager; using System.Xml; using TradeIdeas.XML; using TradeIdeas.TIProGUI; using System.IO; using LumenWorks.Framework.IO.Csv; using TradeIdeas.ServerConnection; using System.Windows.Forms.DataVisualization.Charting; using System.Collections.Specialized; using System.Threading; using TradeIdeas.MiscSupport; //using System.Net.Http; namespace TIProChartsExtension { public struct ColumnDataPoint { public int column; public double point; public string startTime; } public enum TIChartType { Candlestick, Line, Bar }; public enum ChartTimeframe { OneMinute, TwoMinute, ThreeMinute, FiveMinute, TenMinute, FifteenMinute, ThirtyMinute, SixtyMinute, Daily }; public partial class Chart : Form, IAcceptSymbolLinking, ISaveLayout, ISnapToGrid { /// /// How many bars to request initially from the chart server /// private readonly int INITIAL_BARS_TO_LOAD = 350; /// /// How many bars are beyond the rightmost edge of the chart. 0 = current /// private int _rightEdgeBarsFromCurrent = 0; private string _symbol = ""; public string Symbol { get { return _symbol; } set { if (_symbol != value) { _supportResistanceLines.Clear(); _annotations.Clear(); chart1.Series.Clear(); } _symbol = value; } } private TIChartType _chartType = TIChartType.Candlestick; public TIChartType ChartType { get { return _chartType; } set { _chartType = value; } } private ChartTimeframe _timeframe = ChartTimeframe.Daily; public ChartTimeframe ChartTimeframe { get { return _timeframe; } set { _timeframe = value; } } SendManager _sendManager; public Chart(SendManager sendManager) { _sendManager = sendManager; InitializeComponent(); MouseWheel += Chart_MouseWheel; WindowIconCache.SetIcon(this); BuildContextMenu(); UpdateContextMenuState(); SetSnapToGrid(GuiEnvironment.SnapToGrid); ChartEnvironment.CrossHairSynchronization.Add(CrossHairSync); } private void CrossHairSync(DateTime start, DateTime end, double yValue, string symbol, Form source) { if (this == source || !_crossHairSyncEnabled) return; if (null != _priceSeries && null != _priceSeries.Points && _priceSeries.Points.Count > 0) { double xValue = -1; double yValueToUse = -1; TIDataPoint recentPoint = (TIDataPoint)_priceSeries.Points.Last(); for (int i = _priceSeries.Points.Count - 2; i >= 0; i--) { TIDataPoint priorPoint = (TIDataPoint)_priceSeries.Points[i]; if (end >= recentPoint.End || start >= recentPoint.Start) { xValue = recentPoint.XValue; if (_symbol == symbol) yValueToUse = yValue; else yValueToUse = recentPoint.Close; break; } recentPoint = priorPoint; } if (xValue != -1) DoCrossHairs(xValue, yValueToUse); } } protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { if ((ModifierKeys & Keys.Control) == Keys.Control) { if ((keyData & Keys.Left) == Keys.Left && _pixelsPerBarMinimum > 1) { _pixelsPerBarMinimum--; ResizeChart(); } else if ((keyData & Keys.Right) == Keys.Right) { _pixelsPerBarMinimum++; ResizeChart(); } } else { if (keyData == Keys.Left) { _rightEdgeBarsFromCurrent += 1; ResizeChart(); } else if (keyData == Keys.Right && _rightEdgeBarsFromCurrent > 0) { _rightEdgeBarsFromCurrent -= 1; ResizeChart(); } } return base.ProcessCmdKey(ref msg, keyData); } private readonly int MIN_BUFFER_SIZE = 100; private bool _backfillRequestInProcess = false; void Chart_MouseWheel(object sender, MouseEventArgs e) { if ((ModifierKeys & Keys.Control) == Keys.Control) { if (e.Delta < 0 && _pixelsPerBarMinimum > 1) { _pixelsPerBarMinimum--; ResizeChart(); } else if (e.Delta > 0) { _pixelsPerBarMinimum++; ResizeChart(); } } else { if (e.Delta < 0) { int scrollCancelIncrement = GetScrollCancelIncrement(); _rightEdgeBarsFromCurrent += scrollCancelIncrement; ResizeChart(); } else if (e.Delta > 0 && _rightEdgeBarsFromCurrent > 0) { int scrollCancelIncrement = GetScrollCancelIncrement(); _rightEdgeBarsFromCurrent -= scrollCancelIncrement; if (_rightEdgeBarsFromCurrent < 0) _rightEdgeBarsFromCurrent = 0; ResizeChart(); } } if (_hiddenPricePoints.Count < MIN_BUFFER_SIZE && !_backfillRequestInProcess) RequestBackfill(); //else //{ // if (_hiddenPricePoints.Count < MIN_BUFFER_SIZE) // System.Diagnostics.Debug.WriteLine("[Scrolling] Backfill request in process even though " + _hiddenPricePoints.Count + " < " + MIN_BUFFER_SIZE); //} } /// /// Returns the amount to scroll when mousewheel used /// /// private int GetScrollCancelIncrement() { int increment = (int)Math.Floor(_priceSeries.Points.Count * 0.05); if (increment == 0) increment = 1; return increment; } private void RequestBackfill() { _backfillRequestInProcess = true; RequestData(_oldestDate); } public Chart() { InitializeComponent(); } private const string FORM_TYPE = "CHART"; public static readonly WindowIconCache WindowIconCache = new WindowIconCache("CHARTS"); private bool _pinned = true; public bool Pinned { get { return _pinned; } set { _pinned = value; } } private bool _isDarkTheme = false; public bool IsDarkTheme { get { return _isDarkTheme; } set { _isDarkTheme = value; } } private bool _crossHairSyncEnabled = true; public bool CrossHairSyncEnabled { get { return _crossHairSyncEnabled; } set { _crossHairSyncEnabled = value; } } public void SaveLayout(XmlNode parent) { XmlNode description = LayoutManager.SaveBase(parent, this, FORM_TYPE); description.SetProperty("SYMBOL", _symbol); description.SetProperty("TIMEFRAME", _timeframe); description.SetProperty("SHOW_PREMARKET", _showPreMarket); description.SetProperty("SHOW_POSTMARKET", _showPostMarket); description.SetProperty("PINNED", _pinned); description.SetProperty("ISDARKTHEME", _isDarkTheme); description.SetProperty("SYMBOL_LINKING", _linkingEnabled); description.SetProperty("CROSS_HAIR_SYNC", _crossHairSyncEnabled); description.SetProperty("PIXELS_PER_BAR", _pixelsPerBarMinimum); description.SetProperty("SMALL_BORDERS", _useSmallBorders); } public void Restore(XmlNode description, bool ignorePosition, bool cascadePosition) { LayoutManager.RestoreBase(description, this, ignorePosition, cascadePosition); _timeframe = description.PropertyEnum("TIMEFRAME", TIProChartsExtension.ChartTimeframe.Daily); _showPreMarket = description.Property("SHOW_PREMARKET", false); _showPostMarket = description.Property("SHOW_POSTMARKET", false); _pinned = description.Property("PINNED", true); _isDarkTheme = description.Property("ISDARKTHEME", false); _linkingEnabled = description.Property("SYMBOL_LINKING", true); _crossHairSyncEnabled = description.Property("CROSS_HAIR_SYNC", true); _pixelsPerBarMinimum = description.Property("PIXELS_PER_BAR", 5); _useSmallBorders = description.Property("SMALL_BORDERS", false); SetTheSymbol(description.Property("SYMBOL", "SPY"), null); UpdateContextMenuState(); SetSmallBorders(); } private List _supportResistanceLines = new List(); public void AddSupportLine(DateTime from, double level, DateTime to) { _supportResistanceLines.Add(new SupportResistanceLine(from, level, true, to)); } public void AddResistanceLine(DateTime from, double level, DateTime to) { _supportResistanceLines.Add(new SupportResistanceLine(from, level, false, to)); } private void StreamingUpdateResponse(Int64 id, XmlNode message) { try { this.InvokeIfRequired((MethodInvoker)delegate { gatherUpdateData(message.Text()); }); } catch (Exception e) { string debugView = e.ToString(); } } /// /// Contains periodic update bar data /// List _updateList = new List(); private void gatherUpdateData(string body) { try { _updateList.Clear(); TextReader textReader = new StringReader(body); CsvReader csvRead = new CsvReader(textReader, true); string[] headers = csvRead.GetFieldHeaders(); while (csvRead.ReadNextRecord()) { RowData pointData = new RowData(); for (int i = 0; i < headers.Length; i++) { pointData.Data.Add(headers[i].ToString(), csvRead[headers[i]]); } _updateList.Add(pointData); } System.Diagnostics.Debug.WriteLine("GatherData: found " + _updateList.Count + " bars"); this.InvokeIfRequired(delegate() { UpdateRecentBars(); SetLastPriceAnnotation(true); SetVolumeAnnotation(true); chart1.Invalidate(); }); } catch (Exception e) { string debugView = e.ToString(); } } private void UpdateRecentBars() { List recentRecords = _updateList.Skip(Math.Max(0, _updateList.Count() - 2)).ToList(); foreach (RowData rowData in recentRecords) { TIDataPoint updatePoint = MakeTIDataPoint(rowData); if (null != updatePoint) { if (_futurePricePoints.Count > 0) { for (int i = _futurePricePoints.Count - 1; i > 0; i--) { TIDataPoint dataPoint = (TIDataPoint)_futurePricePoints[i][0]; if (dataPoint.StartTimeT == updatePoint.StartTimeT) { dataPoint.UpdateFromPoint(updatePoint); _futurePricePoints[i][1] = updatePoint.GetVolumePoint(); _futurePricePoints[i][2] = updatePoint.GetVolumeAvgPoint(); StyleVolumePoint(dataPoint, _futurePricePoints[i][2]); } } TIDataPoint lastPoint = (TIDataPoint)_futurePricePoints.Last()[0]; if (lastPoint.StartTimeT < updatePoint.StartTimeT) { List points = new List(); double lastXValue = _futurePricePoints.Last()[0].XValue; updatePoint.XValue = lastXValue + 1; points.Add(updatePoint); DataPoint volumePoint = updatePoint.GetVolumePoint(); StyleVolumePoint(updatePoint, volumePoint); points.Add(volumePoint); points.Add(updatePoint.GetVolumeAvgPoint()); _futurePricePoints.Add(points); } } for (int i = _priceSeries.Points.Count - 1; i > 0; i--) { TIDataPoint dataPoint = (TIDataPoint)_priceSeries.Points[i]; if (dataPoint.StartTimeT == updatePoint.StartTimeT) { System.Diagnostics.Debug.WriteLine("Update point: " + dataPoint.Start.ToString("yyyy-MM-dd HH:mm") + ", close=" + updatePoint.Close + " was " + dataPoint.Close); dataPoint.UpdateFromPoint(updatePoint); _volumeSeries.Points[i] = dataPoint.GetVolumePoint(); _volumeAvgSeries.Points[i] = dataPoint.GetVolumeAvgPoint(); StyleVolumePoint(dataPoint, _volumeSeries.Points[i]); } if (dataPoint.StartTimeT < updatePoint.StartTimeT) break; } TIDataPoint lastChartPoint = (TIDataPoint)_priceSeries.Points.Last(); if (_futurePricePoints.Count == 0 && lastChartPoint.StartTimeT < updatePoint.StartTimeT) { List points = new List(); double lastXValue = _priceSeries.Points.Last().XValue; _priceSeries.Points.Add(updatePoint); updatePoint.XValue = lastXValue + 1; DataPoint volumePoint = updatePoint.GetVolumePoint(); StyleVolumePoint(updatePoint, volumePoint); _volumeSeries.Points.Add(volumePoint); _volumeAvgSeries.Points.Add(updatePoint.GetVolumeAvgPoint()); SetChartMaxAndMin(); } } } } private void StreamingResponse(Int64 id, XmlNode message) { try { this.InvokeIfRequired((MethodInvoker)delegate {/* if (id == _streamingUpdateId) gatherUpdateData(message.Text()); else gatherChartData(message.Text());*/ }); } catch (Exception e) { string debugView = e.ToString(); } } List _dataList = new List(); private void gatherChartData(string body) { try { _dataList.Clear(); //List columnHeadings = prototypeEditor1.GetColumnHeadings(); TextReader textReader = new StringReader(body); CsvReader csvRead = new CsvReader(textReader, true); string[] headers = csvRead.GetFieldHeaders(); while (csvRead.ReadNextRecord()) { RowData pointData = new RowData(); for (int i = 0; i < headers.Length; i++) { pointData.Data.Add(headers[i].ToString(), csvRead[headers[i]]); } _dataList.Add(pointData); } System.Diagnostics.Debug.WriteLine("GatherData: found " + _dataList.Count + " bars"); if (_dataList.Count < 20) { string debugView = _dataList.ToString(); } if (_backfillRequestInProcess) { _backfillRequestInProcess = false; UpdateBackfill(); } else { chart1.Annotations.Clear(); UpdateChart(); } } catch (Exception e) { string debugView = e.StackTrace; } } /// /// Takes list of RowData returned and makes candles out of them and inserts them into the backfill buffer. /// private void UpdateBackfill() { for (int i = _dataList.Count - 1; i > 0; i--) { RowData rowData = _dataList[i]; TIDataPoint dataPoint = MakeTIDataPoint(rowData); int firstIndex = (int)_priceSeries.Points[0].XValue; DateTime oldestReceivedBar = ((TIDataPoint)_priceSeries.Points[0]).Start; if (_hiddenPricePoints.Count > 0) { firstIndex = (int)_hiddenPricePoints[0][0].XValue; oldestReceivedBar = ((TIDataPoint)_hiddenPricePoints[0][0]).Start; _oldestDate = oldestReceivedBar; } if (null != dataPoint) { if (dataPoint.Start < oldestReceivedBar) { dataPoint.XValue = firstIndex - 1; List points = new List(); points.Add(dataPoint); DataPoint volumePoint = dataPoint.GetVolumePoint(); points.Add(volumePoint); points.Add(dataPoint.GetVolumeAvgPoint()); _hiddenPricePoints.Insert(0, points); } } } } private void StyleVolumePoint(TIDataPoint dataPoint, DataPoint volumePoint) { volumePoint.Color = _colorVolumeUpBar; volumePoint.BorderColor = _colorVolumeUpBarBorder; if (dataPoint.Open > dataPoint.Close) { volumePoint.Color = _colorVolumeDownBar; volumePoint.BorderColor = _colorVolumeDownBarBorder; } } private TIDataPoint MakeTIDataPoint(RowData rowData) { double? startTime = rowData.GetAsDouble("START", double.NaN); double? endTime = rowData.GetAsDouble("END", double.NaN); double? open = rowData.GetAsDouble("OPEN", double.NaN); double? high = rowData.GetAsDouble("HIGH", double.NaN); double? low = rowData.GetAsDouble("LOW", double.NaN); double? close = rowData.GetAsDouble("CLOSE", double.NaN); double? volume = rowData.GetAsDouble("VOLUME", double.NaN); double? volumeAvg = rowData.GetAsDouble("AVG_VOLUME", double.NaN); if (!startTime.HasValue || !open.HasValue || !high.HasValue || !low.HasValue || !close.HasValue || !volume.HasValue || !volumeAvg.HasValue) return null; TIDataPoint dataPoint = new TIDataPoint(startTime.Value, open.Value, high.Value, low.Value, close.Value, (int)volume.Value, volumeAvg.Value, rowData, endTime.Value); return dataPoint; } private const int START_TIME_INDEX = 0; private const int OPEN_INDEX = 1; private const int HIGH_INDEX = 2; private const int LOW_INDEX = 3; private const int CLOSE_INDEX = 4; private const int VOLUME_INDEX = 5; private void InitializeChart() { _chartArea = chart1.ChartAreas[0]; chart1.ChartAreas.Clear(); chart1.ChartAreas.Add(_chartArea); chart1.Series.Clear(); chart1.Annotations.Clear(); _barDescriptionAnnotation.Visible = false; _hiddenPricePoints.Clear(); _futurePricePoints.Clear(); _chartArea.AxisX.StripLines.Clear(); SetThemeColors(); _rightEdgeBarsFromCurrent = 0; } private DateTime _oldestDate; private Series _priceSeries = new Series(); private Series _volumeSeries = new Series(); private Series _volumeAvgSeries = new Series(); private ChartArea _chartArea = new ChartArea(); private bool _chartDrawn = false; public void UpdateChart() { _chartDrawn = false; if (_dataList.Count > 0) { InitializeChart(); double highestHigh = 0; double lowestLow = 0; if (_chartType == TIChartType.Candlestick) { _priceSeries = new Series(); _priceSeries.ChartType = SeriesChartType.Candlestick; _priceSeries["PriceUpColor"] = "Lime"; // <<== use text indexer for series _priceSeries["PriceDownColor"] = "Red"; // <<== use text indexer for series _priceSeries.BorderColor = Color.Black; _priceSeries.Color = Color.Black; _priceSeries.YAxisType = AxisType.Secondary; _volumeSeries = new Series(); _volumeSeries.ChartType = SeriesChartType.Column; _volumeSeries.BorderColor = LINE_COLOR; chart1.Series.Add(_volumeSeries); _volumeAvgSeries = new Series(); _volumeAvgSeries.ChartType = SeriesChartType.Area; _volumeAvgSeries.Color = _colorVolumeAvg; chart1.Series.Add(_volumeAvgSeries); // add _priceSeries last so it goes on top chart1.Series.Add(_priceSeries); SetThemeColors(); _chartArea.AxisY2.LabelStyle.Format = "C2"; //_chartArea.AxisX.LabelStyle.Format = ""; _chartArea.AxisX.LabelStyle.Angle = 0; _chartArea.AxisX.LabelStyle.IsEndLabelVisible = false; foreach (RowData rowData in _dataList) { TIDataPoint dataPoint = MakeTIDataPoint(rowData); _extraDataPoints.Clear(); _extraDataPoints.Add("VOLUME"); _extraDataPoints.Add("AVG_VOLUME"); if (dataPoint.High != double.NaN && dataPoint.High > highestHigh) highestHigh = dataPoint.High; if (dataPoint.Low != double.NaN && dataPoint.Low > 0 && (lowestLow == 0 || dataPoint.Low < lowestLow)) lowestLow = dataPoint.Low; if (isEmpty(dataPoint.Open, dataPoint.High, dataPoint.Low, dataPoint.Close)) { /* TIDataPoint dataPoint = new TIDataPoint(startTime, open, high, low, close, (int)volume); priceSeries.Points.Add(dataPoint); dataPoint.XValue = priceSeries.Points.Count - 1; priceSeries.Points[priceSeries.Points.Count - 1].IsEmpty = true; */ } else { _priceSeries.Points.Add(dataPoint); dataPoint.XValue = _priceSeries.Points.Count - 1; _priceSeries.Points[_priceSeries.Points.Count - 1].ToolTip = GetTooltip(dataPoint.Start, dataPoint.Open, dataPoint.High, dataPoint.Low, dataPoint.Close, (double)dataPoint.Volume); DataPoint volumePoint = dataPoint.GetVolumePoint(); volumePoint.Color = _colorVolumeUpBar; volumePoint.BorderColor = _colorVolumeUpBarBorder; if (dataPoint.Open > dataPoint.Close) { volumePoint.Color = _colorVolumeDownBar; volumePoint.BorderColor = _colorVolumeDownBarBorder; } _volumeSeries.Points.Add(volumePoint); DataPoint volumeAvgPoint = dataPoint.GetVolumeAvgPoint(); _volumeAvgSeries.Points.Add(volumeAvgPoint); } } _chartArea.AxisX.IntervalOffset = 0; _chartArea.AxisX.Interval = _priceSeries.Points.Count + 1; } else if (_chartType == TIChartType.Line) { } _oldestDate = ((TIDataPoint)_priceSeries.Points[0]).Start; UpdateWindowTitle(); SetChartMaxAndMin(lowestLow, highestHigh); if (_annotations.Count > 0) AddBarAnnotations(); if (_supportResistanceLines.Count > 0) DrawSupportResistance(); SetChartMaxAndMin(lowestLow, highestHigh); SetGridLines(); SetDateLabels(); if (_showPostMarket || _showPreMarket) HighlightPrePostMarket(); ResizeChart(); StartUpdates(); } else { //string debugView = "Updating chart but no data"; } } private System.Windows.Forms.Timer _updateTimer = new System.Windows.Forms.Timer(); private void StartUpdates() { _updateTimer.Tick -= _updateTimer_Tick; _updateTimer.Tick += _updateTimer_Tick; int chartUpdateFrequency = GuiEnvironment.AppConfig.Node("CHARTING").Property("CHART_UPDATE_FREQUENCY", 10); _updateTimer.Interval = chartUpdateFrequency * 1000; _updateTimer.Enabled = true; } void _updateTimer_Tick(object sender, EventArgs e) { RequestUpdate(); } private void HighlightPrePostMarket() { // draw striplines for pre and post market bars. add one stripline for each contiguous group of bars _chartArea.AxisX.StripLines.Clear(); if (chart1.Series.Count > 0 && null != _priceSeries && _priceSeries.Points.Count > 0) { StripLine currentStripline = new StripLine(); bool stripLineInProgress = false; TIDataPoint previousPoint = null; int i = 0; int currentStripStartIndex = 0; foreach (TIDataPoint point in _priceSeries.Points.OfType()) { i++; if (point.Start.IsPreOrPostMarket()) { if (!stripLineInProgress) { stripLineInProgress = true; currentStripline.Interval = _priceSeries.Points.Count + 1; currentStripline.IntervalOffset = i - 0.5; currentStripline.BackColor = _currentPrePostMarketColor; currentStripStartIndex = i; } if (i == _priceSeries.Points.Count) { currentStripline.StripWidth = Math.Max(i - currentStripStartIndex + 1, 1); _chartArea.AxisX.StripLines.Add(currentStripline); currentStripline = new StripLine(); stripLineInProgress = false; } } else if (stripLineInProgress) { // finish the stripline currentStripline.StripWidth = i - currentStripStartIndex; _chartArea.AxisX.StripLines.Add(currentStripline); currentStripline = new StripLine(); stripLineInProgress = false; } previousPoint = point; } } } private void DrawSupportResistance() { if (_priceSeries.Points.Count > 0) { foreach (SupportResistanceLine line in _supportResistanceLines) { HorizontalLineAnnotation lineAnnotation = new HorizontalLineAnnotation(); lineAnnotation.IsSizeAlwaysRelative = false; lineAnnotation.AxisX = _chartArea.AxisX; lineAnnotation.AxisY = _chartArea.AxisY2; lineAnnotation.AnchorX = GetXValue(line.From); double endXValue = _priceSeries.Points.Last().XValue; if (line.To.HasValue) endXValue = GetXValue(line.To.Value); lineAnnotation.Width = endXValue - lineAnnotation.AnchorX + 1; lineAnnotation.AnchorY = line.Level; lineAnnotation.LineWidth = 1; if (line.IsSupport) lineAnnotation.LineColor = Color.Green; else lineAnnotation.LineColor = Color.Red; chart1.Annotations.Add(lineAnnotation); } } } private double GetXValue(DateTime dateTime) { for (int i = _priceSeries.Points.Count - 1; i > 0; i--) { TIDataPoint dataPoint = _priceSeries.Points[i] as TIDataPoint; TIDataPoint previousDataPoint = null; if (null != dataPoint) { if (i - 1 >= 0) previousDataPoint = _priceSeries.Points[i - 1] as TIDataPoint; if (null == previousDataPoint || dataPoint.Start <= dateTime) return dataPoint.XValue; } } return 0; } private ArrowAnnotation _arrowAnnotation = new ArrowAnnotation(); private void AddBarAnnotations() { try { foreach (TIAnnotation annotation in _annotations) { for (int i = 0; i < _priceSeries.Points.Count; i++) { TIDataPoint dataPoint = _priceSeries.Points[i] as TIDataPoint; TIDataPoint nextDataPoint = null; if (null != dataPoint) { if (i + 1 < _priceSeries.Points.Count) nextDataPoint = _priceSeries.Points[i + 1] as TIDataPoint; if (AlertAnnotationOnThisBar(dataPoint, nextDataPoint, annotation.Date)) { _arrowAnnotation.IsSizeAlwaysRelative = true; _arrowAnnotation.AxisX = _chartArea.AxisX; _arrowAnnotation.AxisY = _chartArea.AxisY2; _arrowAnnotation.Width = 0; _arrowAnnotation.AnchorX = dataPoint.XValue; if (annotation.Up) { _arrowAnnotation.Height = GetAnnotationHeight(); _arrowAnnotation.AnchorY = GetArrowPosition(dataPoint.Low, annotation.Up); } else { _arrowAnnotation.Height = -GetAnnotationHeight(); _arrowAnnotation.AnchorY = GetArrowPosition(dataPoint.High, annotation.Up); } _arrowAnnotation.ToolTip = "Alert from " + annotation.Date.ToString("yyyy-MM-dd HH:mm:ss") + "\n" + annotation.Description; _arrowAnnotation.ArrowSize = 4; _arrowAnnotation.BackColor = _colorArrow; chart1.Annotations.Add(_arrowAnnotation); /* ImageAnnotation imageAnnotation = new ImageAnnotation(); imageAnnotation.Image = _imageName; imageAnnotation.IsSizeAlwaysRelative = true; imageAnnotation.AxisX = _chartArea.AxisX; imageAnnotation.AxisY = _chartArea.AxisY2; imageAnnotation.Width = 0; imageAnnotation.AnchorX = dataPoint.XValue; if (annotation.Up) { imageAnnotation.Height = GetAnnotationHeight(); imageAnnotation.AnchorY = GetArrowPosition(dataPoint.Low, annotation.Up); } else { imageAnnotation.Height = -GetAnnotationHeight(); imageAnnotation.AnchorY = GetArrowPosition(dataPoint.High, annotation.Up); } imageAnnotation.Visible = true; chart1.Annotations.Add(imageAnnotation); */ } } } } } catch (Exception e) { string debugView = e.ToString(); } } private void SetArrowTheme() { _arrowAnnotation.BackColor = _colorArrow; } private double GetAnnotationHeight() { return 5; /* double max = _chartArea.AxisY2.Maximum; double min = _chartArea.AxisY2.Minimum; double range = max - min; return range * 0.10; */ } private bool AlertAnnotationOnThisBar(TIDataPoint thisBar, TIDataPoint nextBar, DateTime annotationDateTime) { bool toReturn = false; // if not last bar if (thisBar.Start <= annotationDateTime && null != nextBar && nextBar.Start > annotationDateTime) return true; else if (_timeframe == ChartTimeframe.Daily && thisBar.Start.ToString("yyyy-MM-dd") == annotationDateTime.ToString("yyyy-MM-dd")) return true; else if (TimeframeIntraday() && thisBar.Start <= annotationDateTime && null == nextBar && GetNextBarStart(thisBar.Start) > annotationDateTime) // last bar return true; return toReturn; } private DateTime GetNextBarStart(DateTime thisBarStart) { if (_timeframe == ChartTimeframe.OneMinute) return thisBarStart.AddMinutes(1); else if (_timeframe == ChartTimeframe.TwoMinute) return thisBarStart.AddMinutes(2); else if (_timeframe == ChartTimeframe.ThreeMinute) return thisBarStart.AddMinutes(3); else if (_timeframe == ChartTimeframe.FiveMinute) return thisBarStart.AddMinutes(5); else if (_timeframe == ChartTimeframe.TenMinute) return thisBarStart.AddMinutes(10); else if (_timeframe == ChartTimeframe.FifteenMinute) return thisBarStart.AddMinutes(15); else if (_timeframe == ChartTimeframe.ThirtyMinute) return thisBarStart.AddMinutes(30); else if (_timeframe == ChartTimeframe.SixtyMinute) return thisBarStart.AddMinutes(60); else return thisBarStart.AddDays(1); } private double GetArrowPosition(double price, bool up) { double range = _chartArea.AxisY2.Maximum - _chartArea.AxisY2.Minimum; double offset = range * 0.02; return up? price - offset: price + offset; } private string GetTooltip(DateTime? start, double open, double high, double low, double close, double volume) { string timeDisplay = ""; //DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0); //DateTime start = epoch.AddSeconds(startTime); if (start.HasValue) { if (TimeframeIntraday()) timeDisplay = start.Value.ToString("yyyy-MM-dd HH:mm"); else timeDisplay = start.Value.ToString("yyyy-MM-dd"); return "Time \t= " + timeDisplay + "\nOpen \t= " + open.ToString("C2") + "\nHigh \t= " + high.ToString("C2") + "\nLow \t= " + low.ToString("C2") + "\nClose \t= " + close.ToString("C2") + "\nVolume \t= " + ((int)volume).ToString("N0"); } else return "Open \t= " + open.ToString("C2") + "\nHigh \t= " + high.ToString("C2") + "\nLow \t= " + low.ToString("C2") + "\nClose \t= " + close.ToString("C2") + "\nVolume \t= " + ((int)volume).ToString("N0"); } private void UpdateWindowTitle() { Text = _symbol + " " + GetTimeframe(_timeframe); } private void SetChartMaxAndMin() { SetChartMaxAndMin(_priceSeries.Points.Min(x => x.YValues[1]), _priceSeries.Points.Max(x => x.YValues[0])); } private void SetChartMaxAndMin(double low, double high) { double range = high - low; double chartMin = low - (range * 0.05); double chartMax = high + (range * 0.05); foreach (Annotation annotation in chart1.Annotations) { if (annotation.AnchorY > high) { high = annotation.AnchorY; range = high - low; chartMax = annotation.AnchorY + (range * 0.05); } if (annotation.AnchorY < low) { low = annotation.AnchorY; range = high - low; chartMin = annotation.AnchorY - (range * 0.05); } } _chartArea.AxisY2.Maximum = chartMax; _chartArea.AxisY2.Minimum = chartMin; double highestVolume = Math.Max(_volumeSeries.Points.Max(x => x.YValues[0]), _volumeAvgSeries.Points.Max(x => x.YValues[0])); _chartArea.AxisY.Maximum = highestVolume * 5; _chartArea.AxisY.Minimum = 0; _chartArea.AxisX.Minimum = 0; _chartArea.AxisX.Minimum = _priceSeries.Points.First().XValue - 1; _chartArea.AxisX.Maximum = _priceSeries.Points.Last().XValue + 1; } private double GetTotalHoursInChart() { double toReturn = 0; if (chart1.Series.Count > 0 && null != _priceSeries && _priceSeries.Points.Count > 0) { TIDataPoint firstPoint = _priceSeries.Points.OfType().First(); TIDataPoint lastPoint = _priceSeries.Points.OfType().Last(); if (null != firstPoint && null != lastPoint) return (lastPoint.Start - firstPoint.Start).TotalHours; } return toReturn; } private void SetDateLabels() { Axis axis = _chartArea.AxisX; axis.CustomLabels.Clear(); List annotations = chart1.Annotations.ToList(); foreach (Annotation annotation in annotations) { if (annotation.Tag != null && annotation.Tag.ToString() == "gridline") chart1.Annotations.Remove(annotation); } double hoursInChart = GetTotalHoursInChart(); bool showQuarterHours = false; bool showAllHours = false; bool showNoonAndThreePm = false; if (hoursInChart < 3) { showQuarterHours = true; showAllHours = true; } else if (hoursInChart < 8) showAllHours = true; else if (hoursInChart < 96) showNoonAndThreePm = true; if (!TimeframeIntraday()) { TIDataPoint previousPoint = null; foreach (TIDataPoint point in _priceSeries.Points.OfType()) { if (previousPoint != null && previousPoint.Start.ToString("yyyy") != point.Start.ToString("yyyy")) { axis.CustomLabels.Add(point.XValue - 10, point.XValue + 10, point.Start.ToString("M/d/yy")); AddCustomVerticalGridline(point.XValue); } else if (previousPoint != null && previousPoint.Start.ToString("yyyy-MM") != point.Start.ToString("yyyy-MM")) { axis.CustomLabels.Add(point.XValue - 10, point.XValue + 10, point.Start.ToString("M/d")); AddCustomVerticalGridline(point.XValue); } previousPoint = point; } } else { TIDataPoint previousPoint = null; foreach (TIDataPoint point in _priceSeries.Points.OfType()) { if (previousPoint != null && previousPoint.Start.ToString("yyyy") != point.Start.ToString("yyyy")) { CustomLabel customLabel = new CustomLabel(point.XValue - 3, point.XValue + 3, point.Start.ToString("M/d/yy"), 0, LabelMarkStyle.None); customLabel.ForeColor = Color.Black; axis.CustomLabels.Add(customLabel); AddCustomVerticalGridline(point.XValue); } else if (previousPoint != null && previousPoint.Start.ToString("yyyy-MM-dd") != point.Start.ToString("yyyy-MM-dd")) { CustomLabel customLabel = new CustomLabel(point.XValue - 3, point.XValue + 3, point.Start.ToString("M/d"), 0, LabelMarkStyle.None); customLabel.ForeColor = Color.Black; axis.CustomLabels.Add(customLabel); AddCustomVerticalGridline(point.XValue); } // TODO make this smarter to show the next bar in the event there is not a 15 minute bar for example else if ((showAllHours && point.Start.Minute == 0) || (showNoonAndThreePm && point.Start.Minute == 0 && (point.Start.ToMarketTimeZone().Hour == 12 || point.Start.ToMarketTimeZone().Hour == 15)) || (showQuarterHours && (point.Start.Minute == 15 || point.Start.Minute == 30 || point.Start.Minute == 45))) { CustomLabel customLabel = new CustomLabel(point.XValue - 3, point.XValue + 3, point.Start.ToString("HH:mm"), 0, LabelMarkStyle.None); if ((showNoonAndThreePm && (point.Start.ToMarketTimeZone().Hour == 12 || point.Start.ToMarketTimeZone().Hour == 15)) || (showQuarterHours && (point.Start.Minute == 15 || point.Start.Minute == 30 || point.Start.Minute == 45))) customLabel.ForeColor = Color.Gray; axis.CustomLabels.Add(customLabel); AddCustomVerticalGridline(point.XValue); } previousPoint = point; } } } /// /// Adds a vertical line annotation that appears to be a grid line. /// /// private void AddCustomVerticalGridline(double value) { VerticalLineAnnotation verticalLineAnnotation = new VerticalLineAnnotation(); verticalLineAnnotation.X = value; verticalLineAnnotation.Y = _chartArea.AxisY2.Minimum; verticalLineAnnotation.IsSizeAlwaysRelative = false; verticalLineAnnotation.Height = _chartArea.AxisY2.Maximum - _chartArea.AxisY2.Minimum; verticalLineAnnotation.LineColor = Color.FromArgb(50, Color.Gray); verticalLineAnnotation.AxisX = _chartArea.AxisX; verticalLineAnnotation.AxisY = _chartArea.AxisY2; verticalLineAnnotation.Visible = true; verticalLineAnnotation.Tag = "gridline"; chart1.Annotations.Add(verticalLineAnnotation); verticalLineAnnotation.SendToBack(); } /// /// Adds a horizontal line annotation that appears to be a grid line /// /// private void AddCustomHorizontalGridline(double value) { HorizontalLineAnnotation horizontalLineAnnotation = new HorizontalLineAnnotation(); horizontalLineAnnotation.X = _chartArea.AxisX.Minimum; horizontalLineAnnotation.Y = value; horizontalLineAnnotation.IsSizeAlwaysRelative = false; horizontalLineAnnotation.Width = _chartArea.AxisX.Maximum - _chartArea.AxisX.Minimum; horizontalLineAnnotation.LineColor = Color.FromArgb(50, Color.Gray); horizontalLineAnnotation.AxisX = _chartArea.AxisX; horizontalLineAnnotation.AxisY = _chartArea.AxisY2; horizontalLineAnnotation.Visible = true; chart1.Annotations.Add(horizontalLineAnnotation); horizontalLineAnnotation.SendToBack(); } /// /// This uses custom annotations and custom labels to simulate grid lines since we were having problems with the native grid lines occasionally. /// private void SetGridLines() { List annotations = chart1.Annotations.ToList(); foreach (Annotation annotation in annotations) { HorizontalLineAnnotation horizontalLineAnnotation = annotation as HorizontalLineAnnotation; if (null != horizontalLineAnnotation) chart1.Annotations.Remove(horizontalLineAnnotation); } _chartArea.AxisY2.Interval = 0; _chartArea.AxisY2.CustomLabels.Clear(); _chartArea.AxisY2.IntervalOffset = 10000; _chartArea.AxisY2.StripLines.Clear(); _chartArea.RecalculateAxesScale(); double range = _chartArea.AxisY2.Maximum - _chartArea.AxisY2.Minimum; double interval = GetInterval(range); double firstGridLine = GetFirstGridLine(_chartArea.AxisY2.Minimum, interval); double intervalOffset = firstGridLine - _chartArea.AxisY2.Minimum; for (double i = firstGridLine; i <= _chartArea.AxisY2.Maximum; i += interval) { CustomLabel customLabel = new CustomLabel(i - 3, i + 3, i.ToString("C"), 0, LabelMarkStyle.None); customLabel.ForeColor = Color.Gray; _chartArea.AxisY2.CustomLabels.Add(customLabel); AddCustomHorizontalGridline(i); } } /// /// This uses the native chart Interval, IntervalOffset properties to set the gridlines /// private void SetGridLinesNative() { try { // Y-Axis gridlines _chartArea.AxisY2.StripLines.Clear(); _chartArea.RecalculateAxesScale(); double range = _chartArea.AxisY2.Maximum - _chartArea.AxisY2.Minimum; double interval = GetInterval(range); _chartArea.AxisY2.Interval = interval; _chartArea.AxisY2.LabelStyle.Interval = interval; _chartArea.AxisY2.IntervalAutoMode = IntervalAutoMode.VariableCount; _chartArea.AxisY2.IntervalType = DateTimeIntervalType.Number; double firstGridLine = GetFirstGridLine(_chartArea.AxisY2.Minimum, interval); double intervalOffset = firstGridLine - _chartArea.AxisY2.Minimum; _chartArea.AxisY2.IntervalOffset = intervalOffset; _chartArea.AxisY2.LabelStyle.IntervalOffset = intervalOffset; System.Diagnostics.Debug.WriteLine("[SetGridLines] range=" + range + ", firstGridLine=" + firstGridLine + ", intervalOffset=" + intervalOffset + ", interval=" + interval); // X-Axis gridlines } catch (Exception e) { string debugView = e.ToString(); } } private double GetFirstGridLine(double min, double interval) { double level = Math.Round(min, 2) + 0.01; while (level <= min + interval) { int thisLevel = (int)(level * 100); if (thisLevel % (int)(interval * 100) == 0) return thisLevel / 100; ; level = level + 0.01; } return 0; } private double GetInterval(double range) { if (range > 1000) return 100; else if (range > 250) return 50; else if (range > 100) return 25; else if (range > 50) return 10; else if (range > 25) return 5; else if (range > 10) return 2.5; else if (range > 5) return 2; else if (range > 3) return 1; else if (range > 2) return 0.5; else if (range > 1) return 0.25; else if (range > 0.5) return 0.10; else if (range > 0.2) return 0.05; else if (range > 0.1) return 0.02; else if (range > 0.05) return 0.01; else return 0.01; } private double GetPoint(ColumnDataPoint p) { return p.point; } private bool isEmpty(double h, double j, double k, double l) //for series with 4 values per point.. { //if one of the values is empty, that whole data point shall be empty if (double.IsNaN(h) || double.IsNaN(j) || double.IsNaN(k) || double.IsNaN(l)) { return true; } return false; } public void DisplayChart() { RequestData(); } private void RequestUpdate() { RequestData(null, true); } private SingleGridRequest _outstandingRequest; private void RequestData(DateTime? oldestDate = null, bool updateOnly = false) { if (null != _outstandingRequest) _outstandingRequest.Abort(); try { if (updateOnly) // TODO "first_row_number", "2" _outstandingRequest = new SingleGridRequest(_symbol, GetPrototype(null, true), false, _sendManager, this, delegate(SingleGridRequest.Response response) { gatherUpdateData(response.csvData); }); else // TODO "first_row_number", "100" _outstandingRequest = new SingleGridRequest(_symbol, GetPrototype(oldestDate, false), false, _sendManager, this, delegate(SingleGridRequest.Response response) { gatherChartData(response.csvData); }); } catch (Exception e) { string debugView = e.ToString(); } } private string GetPrototype(DateTime? oldestDate = null, bool updateOnly = false) { string backfill = ""; if (oldestDate.HasValue) backfill = " at_time " + ServerFormats.ToTimeT(oldestDate.Value).ToString(); int barsToRequest = INITIAL_BARS_TO_LOAD; if (updateOnly) barsToRequest = 30; string toReturn = "daily {{START {{current(\"start\")}}} {END {{current(\"end\")}}} {OPEN {{}}} {HIGH {{}}} {LOW {{}}} {CLOSE {{}}} {VOLUME {{}}} {AVG_VOLUME {{average(30, 1, \"VOLUME\", 1)}}}} {row_count " + barsToRequest + backfill + " pack 1}"; if (TimeframeIntraday()) toReturn = "intraday {{START {{current(\"start\")}}} {END {{current(\"end\")}}} {OPEN {{}}} {HIGH {{}}} {LOW {{}}} {CLOSE {{}}} {VOLUME {{}}} {AVG_VOLUME {{average(30, 1, \"VOLUME\", 1)}}}} {row_count " + barsToRequest + backfill + " pack 1 minutes_per_candle " + GetMinutesPerCandle() + " start_offset " + GetPreMarketMinutes() + " end_offset " + GetPostMarketMinutes() + "}"; return toReturn; } private bool TimeframeIntraday() { //return !(_timeframe == ChartTimeframe.Daily || _timeframe == ChartTimeframe.Weekly || _timeframe == ChartTimeframe.Monthly || _timeframe == ChartTimeframe.Yearly); return !(_timeframe == ChartTimeframe.Daily); } private int GetMinutesPerCandle() { if (_timeframe == ChartTimeframe.OneMinute) return 1; else if (_timeframe == ChartTimeframe.TwoMinute) return 2; else if (_timeframe == ChartTimeframe.ThreeMinute) return 3; else if (_timeframe == ChartTimeframe.FiveMinute) return 5; else if (_timeframe == ChartTimeframe.TenMinute) return 10; else if (_timeframe == ChartTimeframe.FifteenMinute) return 15; else if (_timeframe == ChartTimeframe.ThirtyMinute) return 30; else if (_timeframe == ChartTimeframe.SixtyMinute) return 60; else return 0; } private bool _showPreMarket = false; private int GetPreMarketMinutes() { if (_showPreMarket) return 90; else return 0; } private bool _showPostMarket = false; private int GetPostMarketMinutes() { if (_showPostMarket) return 60; else return 0; } private void Chart_FormClosing(object sender, FormClosingEventArgs e) { } private class TimeframeToolStripMenuItem : ToolStripMenuItem { public ChartTimeframe TimeFrame { get; set; } public TimeframeToolStripMenuItem(ChartTimeframe timeFrame) { TimeFrame = timeFrame; } } private class TIDataPoint : DataPoint { public DateTime Start { get; set; } public DateTime End { get; set; } public double Open { get; set; } public double High { get; set; } public double Low { get; set; } public double Close { get; set; } public int Volume { get; set; } public double VolumeAvg { get; set; } public double StartTimeT { get; set; } public double EndTimeT { get; set; } public RowData RowData { get; set; } public TIDataPoint(double start, double open, double high, double low, double close, int volume, double volumeAvg, RowData rowData, double end) { DateTime? dateTimeStart = ServerFormats.DecodeServerTime(start.ToString()); if (dateTimeStart.HasValue) Start = dateTimeStart.Value; StartTimeT = start; DateTime? dateTimeEnd = ServerFormats.DecodeServerTime(end.ToString()); if (dateTimeEnd.HasValue) End = dateTimeEnd.Value; EndTimeT = end; Open = open; High = high; Low = low; Close = close; Volume = volume; VolumeAvg = volumeAvg; double[] yValues = { high, low, open, close }; YValues = yValues; RowData = rowData; } /// /// This updates the point from another point. /// /// Point to update data from public void UpdateFromPoint(TIDataPoint point) { Open = point.Open; High = point.High; Low = point.Low; Close = point.Close; double[] yValues = { High, Low, Open, Close }; YValues = yValues; Volume = point.Volume; if (!double.IsNaN(point.VolumeAvg)) VolumeAvg = point.VolumeAvg; RowData = point.RowData; Start = point.Start; StartTimeT = point.StartTimeT; } public DataPoint GetVolumePoint() { DataPoint volumePoint = new DataPoint(XValue, Volume); return volumePoint; } public DataPoint GetVolumeAvgPoint() { DataPoint volumeAvgPoint = new DataPoint(XValue, VolumeAvg); return volumeAvgPoint; } } private void UpdateContextMenuState() { foreach (TimeframeToolStripMenuItem item in contextMenuStrip1.Items.OfType()) { item.Checked = item.TimeFrame == _timeframe; } _toolStripMenuItemPinned.Checked = _pinned; _toolStripMenuItemSymbolLinking.Checked = _linkingEnabled; _toolStripMenuItemDarkTheme.Checked = _isDarkTheme; _toolStripMenuItemPreMarket.Checked = _showPreMarket; _toolStripMenuItemPostMarket.Checked = _showPostMarket; } private string GetTimeframeShort(ChartTimeframe timeFrame) { string toReturn = ""; if (timeFrame == TIProChartsExtension.ChartTimeframe.Daily) toReturn = "D"; else if (timeFrame == TIProChartsExtension.ChartTimeframe.OneMinute) toReturn = "1"; else if (timeFrame == TIProChartsExtension.ChartTimeframe.TwoMinute) toReturn = "2"; else if (timeFrame == TIProChartsExtension.ChartTimeframe.ThreeMinute) toReturn = "3"; else if (timeFrame == TIProChartsExtension.ChartTimeframe.FiveMinute) toReturn = "5"; else if (timeFrame == TIProChartsExtension.ChartTimeframe.TenMinute) toReturn = "10"; else if (timeFrame == TIProChartsExtension.ChartTimeframe.FifteenMinute) toReturn = "15"; else if (timeFrame == TIProChartsExtension.ChartTimeframe.ThirtyMinute) toReturn = "30"; else if (timeFrame == TIProChartsExtension.ChartTimeframe.SixtyMinute) toReturn = "60"; return toReturn; } private string GetTimeframe(ChartTimeframe timeFrame) { return timeFrame.ToString().Replace("Minute", " Minute"); } private ToolStripMenuItem _toolStripMenuItemSendToStockTwits = new ToolStripMenuItem(); private ToolStripMenuItem _toolStripMenuItemPinned = new ToolStripMenuItem(); private ToolStripMenuItem _toolStripMenuItemSymbolLinking = new ToolStripMenuItem(); private ToolStripMenuItem _toolStripMenuItemDarkTheme = new ToolStripMenuItem(); private ToolStripMenuItem _toolStripMenuItemCrossHairSync = new ToolStripMenuItem(); private ToolStripMenuItem _toolStripMenuItemPreMarket = new ToolStripMenuItem(); private ToolStripMenuItem _toolStripMenuItemPostMarket = new ToolStripMenuItem(); private ToolStripMenuItem _toolStripMenuItemSmallBorders = new ToolStripMenuItem(); private void BuildContextMenu() { foreach (ChartTimeframe timeFrame in Enum.GetValues(typeof(ChartTimeframe))) { TimeframeToolStripMenuItem menuItem = new TimeframeToolStripMenuItem(timeFrame); string timeFrameDisplay = GetTimeframe(timeFrame); menuItem.Text = timeFrameDisplay; menuItem.Click += menuItem_Click; contextMenuStrip1.Items.Add(menuItem); } ToolStripSeparator separator = new ToolStripSeparator(); contextMenuStrip1.Items.Add(separator); _toolStripMenuItemPreMarket.Text = "Show Pre Market"; _toolStripMenuItemPreMarket.Checked = _showPreMarket; _toolStripMenuItemPreMarket.CheckOnClick = true; _toolStripMenuItemPreMarket.Click += toolStripMenuItemPreMarket_Click; contextMenuStrip1.Items.Add(_toolStripMenuItemPreMarket); _toolStripMenuItemPostMarket.Text = "Show Post Market"; _toolStripMenuItemPostMarket.Checked = _showPostMarket; _toolStripMenuItemPostMarket.CheckOnClick = true; _toolStripMenuItemPostMarket.Click += toolStripMenuItemPostMarket_Click; contextMenuStrip1.Items.Add(_toolStripMenuItemPostMarket); contextMenuStrip1.Items.Add(new ToolStripSeparator()); /* ToolStripMenuItem toolStripMenuItemInsertTextAnnotation = new ToolStripMenuItem(); toolStripMenuItemInsertTextAnnotation.Text = "Insert Text Annotation"; toolStripMenuItemInsertTextAnnotation.Checked = false; toolStripMenuItemInsertTextAnnotation.Click += toolStripMenuItemInsertTextAnnotation_Click; contextMenuStrip1.Items.Add(toolStripMenuItemInsertTextAnnotation); */ _toolStripMenuItemSendToStockTwits.Text = "Send to StockTwits"; _toolStripMenuItemSendToStockTwits.Checked = false; _toolStripMenuItemSendToStockTwits.Click += toolStripMenuItemSendToStockTwits_Click; contextMenuStrip1.Items.Add(_toolStripMenuItemSendToStockTwits); ToolStripMenuItem toolStripMenuItemDuplicate = new ToolStripMenuItem(); toolStripMenuItemDuplicate.Text = "Duplicate"; toolStripMenuItemDuplicate.Checked = false; toolStripMenuItemDuplicate.Click += toolStripMenuItemDuplicate_Click; contextMenuStrip1.Items.Add(toolStripMenuItemDuplicate); _toolStripMenuItemPinned.Text = "Pinned"; _toolStripMenuItemPinned.Checked = _pinned; _toolStripMenuItemPinned.CheckOnClick = true; _toolStripMenuItemPinned.Click += toolStripMenuItemPinned_Click; contextMenuStrip1.Items.Add(_toolStripMenuItemPinned); _toolStripMenuItemSymbolLinking.Text = "Symbol Linking"; _toolStripMenuItemSymbolLinking.Checked = _linkingEnabled; _toolStripMenuItemSymbolLinking.CheckOnClick = true; _toolStripMenuItemSymbolLinking.Click += toolStripMenuItemSymbolLinking_Click; contextMenuStrip1.Items.Add(_toolStripMenuItemSymbolLinking); _toolStripMenuItemDarkTheme.Text = "Use Dark Theme"; _toolStripMenuItemDarkTheme.Checked = _isDarkTheme; _toolStripMenuItemDarkTheme.CheckOnClick = true; _toolStripMenuItemDarkTheme.Click += _toolStripMenuItemDarkTheme_Click; contextMenuStrip1.Items.Add(_toolStripMenuItemDarkTheme); _toolStripMenuItemCrossHairSync.Text = "Use Cross Hair Sync"; _toolStripMenuItemCrossHairSync.Checked = _crossHairSyncEnabled; _toolStripMenuItemCrossHairSync.CheckOnClick = true; _toolStripMenuItemCrossHairSync.Click += _toolStripMenuItemCrossHairSync_Click; contextMenuStrip1.Items.Add(_toolStripMenuItemCrossHairSync); _toolStripMenuItemSmallBorders.Text = "Use Small Borders"; _toolStripMenuItemSmallBorders.Checked = _useSmallBorders; _toolStripMenuItemSmallBorders.CheckOnClick = true; _toolStripMenuItemSmallBorders.Click += _toolStripMenuItemSmallBorders_Click; contextMenuStrip1.Items.Add(_toolStripMenuItemSmallBorders); chart1.ContextMenuStrip = contextMenuStrip1; } private bool _useSmallBorders = false; void _toolStripMenuItemSmallBorders_Click(object sender, EventArgs e) { _useSmallBorders = _toolStripMenuItemSmallBorders.Checked; SetSmallBorders(); } private void SetSmallBorders() { if (_useSmallBorders) FormBorderStyle = System.Windows.Forms.FormBorderStyle.SizableToolWindow; else FormBorderStyle = System.Windows.Forms.FormBorderStyle.Sizable; _toolStripMenuItemSmallBorders.Checked = _useSmallBorders; } void _toolStripMenuItemCrossHairSync_Click(object sender, EventArgs e) { _crossHairSyncEnabled = _toolStripMenuItemCrossHairSync.Checked; } private void toolStripMenuItemSendToStockTwits_Click(object sender, EventArgs e) { Image screenshot = GetScreenshot(); RowData rowData = new RowData(); rowData.Data.Add("SYMBOL", _symbol); SendToForm sendToForm = new SendToForm(AlertForm.SocialNetwork.STWITS, rowData, "", ""); sendToForm.Chart = screenshot; sendToForm.ShowDialog(); } private Image GetScreenshot() { var screenshot = new Bitmap(this.Width, this.Height); chart1.DrawToBitmap(screenshot, new Rectangle(0, 0, screenshot.Width, screenshot.Height)); return (Image)screenshot; } void _toolStripMenuItemDarkTheme_Click(object sender, EventArgs e) { _isDarkTheme = _toolStripMenuItemDarkTheme.Checked; SetThemeColors(); } private readonly Color LINE_COLOR = Color.Gray; private Color _watermarkColor = Color.FromArgb(230, 230, 230); private readonly Color THEME_BACKGROUND_COLOR_DARK = Color.FromArgb(23, 23, 23); private readonly Color THEME_BACKGROUND_COLOR_LIGHT = Color.FromArgb(255, 255, 255); private static readonly Color THEME_AXIS_COLOR_DARK = Color.FromArgb(50, 50, 50); private static readonly Color THEME_AXIS_COLOR_LIGHT = Color.Gray; private static readonly Color THEME_GRIDLINES_COLOR_DARK = Color.FromArgb(36, 36, 36); private static readonly Color THEME_GRIDLINES_COLOR_LIGHT = Color.Gray; private static readonly Color THEME_WATERMARK_COLOR_DARK = Color.FromArgb(31, 31, 31); private static readonly Color THEME_WATERMARK_COLOR_LIGHT = Color.FromArgb(230, 230, 230); private static readonly Color THEME_AXISLABEL_COLOR_DARK = Color.FromArgb(120, 120, 120); private static readonly Color THEME_AXISLABEL_COLOR_LIGHT = Color.Gray; private static readonly Color THEME_WICK_COLOR_DARK = Color.FromArgb(115, 115, 117); private static readonly Color THEME_WICK_COLOR_LIGHT = Color.FromArgb(0, 0, 0); private static readonly Color THEME_VOLUMEUP_BAR_COLOR_DARK = Color.FromArgb(0, 102, 0); private static readonly Color THEME_VOLUMEUP_BAR_COLOR_LIGHT = Color.FromArgb(220, 238, 223); private static readonly Color THEME_VOLUMEUP_BORDER_COLOR_DARK = Color.FromArgb(0, 30, 4); private static readonly Color THEME_VOLUMEUP_BORDER_COLOR_LIGHT = Color.FromArgb(191, 223, 196); private static readonly Color THEME_VOLUMEDOWN_BAR_COLOR_DARK = Color.FromArgb(155, 19, 19); private static readonly Color THEME_VOLUMEDOWN_BAR_COLOR_LIGHT = Color.FromArgb(238, 226, 226); private static readonly Color THEME_VOLUMEDOWN_BORDER_COLOR_DARK = Color.FromArgb(32, 9, 9); private static readonly Color THEME_VOLUMEDOWN_BORDER_COLOR_LIGHT = Color.FromArgb(223, 202, 202); private static readonly Color THEME_VOLUMEAVG_COLOR_DARK = Color.FromArgb(200, 1, 42, 76); private static readonly Color THEME_VOLUMEAVG_COLOR_LIGHT = Color.FromArgb(100, 120, 187, 253); private static readonly Color THEME_ARROW_COLOR_LIGHT = Color.Blue; private static readonly Color THEME_ARROW_COLOR_DARK = Color.White; private Color _colorVolumeAvg = THEME_VOLUMEAVG_COLOR_LIGHT; private Color _colorVolumeUpBar = THEME_VOLUMEUP_BAR_COLOR_DARK; private Color _colorVolumeUpBarBorder = THEME_VOLUMEUP_BORDER_COLOR_LIGHT; private Color _colorVolumeDownBar = THEME_VOLUMEDOWN_BAR_COLOR_DARK; private Color _colorVolumeDownBarBorder = THEME_VOLUMEDOWN_BORDER_COLOR_LIGHT; private Color _colorArrow = THEME_ARROW_COLOR_LIGHT; // premarket background colors private readonly Color THEME_PREMARKET_COLOR_DARK = Color.FromArgb(75, 128, 128, 128); private readonly Color THEME_PREMARKET_COLOR_LIGHT = Color.FromArgb(75, 255, 245, 245); private Color _currentPrePostMarketColor = Color.Gray; private Color _barDescriptionForeColor = THEME_AXISLABEL_COLOR_LIGHT; private void SetThemeColors() { if (_isDarkTheme) { chart1.BackColor = THEME_BACKGROUND_COLOR_DARK; _chartArea.BackColor = THEME_BACKGROUND_COLOR_DARK; _chartArea.AxisX.LineColor = THEME_AXIS_COLOR_DARK; _chartArea.AxisY2.LineColor = THEME_AXIS_COLOR_DARK; _chartArea.AxisX.MajorGrid.LineColor = THEME_AXIS_COLOR_DARK; _chartArea.AxisY2.MajorGrid.LineColor = THEME_AXIS_COLOR_DARK; _chartArea.AxisX.MajorTickMark.LineColor = THEME_AXIS_COLOR_DARK; _chartArea.AxisY2.MajorTickMark.LineColor = THEME_AXIS_COLOR_DARK; _chartArea.AxisX.LabelStyle.ForeColor = THEME_AXISLABEL_COLOR_DARK; _chartArea.AxisY2.LabelStyle.ForeColor = THEME_AXISLABEL_COLOR_DARK; if (null != _priceSeries) { _priceSeries.Color = THEME_WICK_COLOR_DARK; _priceSeries.BorderColor = THEME_WICK_COLOR_DARK; } _watermarkColor = THEME_WATERMARK_COLOR_DARK; _currentPrePostMarketColor = THEME_PREMARKET_COLOR_DARK; _barDescriptionForeColor = THEME_AXISLABEL_COLOR_DARK; SetBarDescriptionTheme(); _colorVolumeUpBar = THEME_VOLUMEUP_BAR_COLOR_DARK; _colorVolumeUpBarBorder = THEME_VOLUMEUP_BORDER_COLOR_DARK; _colorVolumeDownBar = THEME_VOLUMEDOWN_BAR_COLOR_DARK; _colorVolumeDownBarBorder = THEME_VOLUMEDOWN_BORDER_COLOR_DARK; _colorVolumeAvg = THEME_VOLUMEAVG_COLOR_DARK; _volumeAvgSeries.Color = _colorVolumeAvg; _colorArrow = THEME_ARROW_COLOR_DARK; SetArrowTheme(); } else { chart1.BackColor = THEME_BACKGROUND_COLOR_LIGHT; _chartArea.BackColor = THEME_BACKGROUND_COLOR_LIGHT; _chartArea.AxisX.LineColor = THEME_AXIS_COLOR_LIGHT; _chartArea.AxisY2.LineColor = THEME_AXIS_COLOR_LIGHT; _chartArea.AxisX.MajorGrid.LineColor = THEME_AXIS_COLOR_LIGHT; _chartArea.AxisY2.MajorGrid.LineColor = THEME_AXIS_COLOR_LIGHT; _chartArea.AxisX.MajorTickMark.LineColor = THEME_GRIDLINES_COLOR_LIGHT; _chartArea.AxisY2.MajorTickMark.LineColor = THEME_GRIDLINES_COLOR_LIGHT; _chartArea.AxisX.LabelStyle.ForeColor = THEME_AXISLABEL_COLOR_LIGHT; _chartArea.AxisY2.LabelStyle.ForeColor = THEME_AXISLABEL_COLOR_LIGHT; if (null != _priceSeries) { _priceSeries.Color = THEME_WICK_COLOR_LIGHT; _priceSeries.BorderColor = THEME_WICK_COLOR_LIGHT; } _watermarkColor = THEME_WATERMARK_COLOR_LIGHT; _currentPrePostMarketColor = THEME_PREMARKET_COLOR_LIGHT; _barDescriptionForeColor = THEME_AXISLABEL_COLOR_LIGHT; SetBarDescriptionTheme(); _colorVolumeUpBar = THEME_VOLUMEUP_BAR_COLOR_LIGHT; _colorVolumeUpBarBorder = THEME_VOLUMEUP_BORDER_COLOR_LIGHT; _colorVolumeDownBar = THEME_VOLUMEDOWN_BAR_COLOR_LIGHT; _colorVolumeDownBarBorder = THEME_VOLUMEDOWN_BORDER_COLOR_LIGHT; _colorVolumeAvg = THEME_VOLUMEAVG_COLOR_LIGHT; _volumeAvgSeries.Color = _colorVolumeAvg; _colorArrow = THEME_ARROW_COLOR_LIGHT; SetArrowTheme(); } SetVolumeBarTheme(); Invalidate(); } private void SetVolumeBarTheme() { if (null != _volumeSeries) { for (int i = 0; i < _volumeSeries.Points.Count; i++) { DataPoint volumeBar = _volumeSeries.Points[i]; bool downBar = false; TIDataPoint pricePoint = _priceSeries.Points[i] as TIDataPoint; if (null != pricePoint) { double open = pricePoint.Open; double close = pricePoint.Close; downBar = open > close; } if (downBar) { volumeBar.Color = _colorVolumeDownBar; volumeBar.BorderColor = _colorVolumeDownBarBorder; } else { volumeBar.Color = _colorVolumeUpBar; volumeBar.BorderColor = _colorVolumeUpBarBorder; } } } if (_hiddenPricePoints.Count > 0) { for (int i = 0; i < _hiddenPricePoints.Count; i++) { List dataPoints = _hiddenPricePoints[i]; DataPoint volumeBar = dataPoints[1]; bool downBar = false; TIDataPoint pricePoint = dataPoints[0] as TIDataPoint; if (null != pricePoint) { double open = pricePoint.Open; double close = pricePoint.Close; downBar = open > close; } if (downBar) { volumeBar.Color = _colorVolumeDownBar; volumeBar.BorderColor = _colorVolumeDownBarBorder; } else { volumeBar.Color = _colorVolumeUpBar; volumeBar.BorderColor = _colorVolumeUpBarBorder; } } } } void toolStripMenuItemSymbolLinking_Click(object sender, EventArgs e) { ToolStripMenuItem menuItem = sender as ToolStripMenuItem; if (null != menuItem) _linkingEnabled = menuItem.Checked; } void toolStripMenuItemPinned_Click(object sender, EventArgs e) { ToolStripMenuItem menuItem = sender as ToolStripMenuItem; if (null != menuItem) _pinned = menuItem.Checked; } void toolStripMenuItemDuplicate_Click(object sender, EventArgs e) { Duplicate(); } private void Duplicate() { GuiEnvironment.RecordUseCase("Chart.RightClick.Duplicate", GuiEnvironment.FindConnectionMaster("").SendManager); LayoutManager.Instance().Duplicate(this); } void toolStripMenuItemInsertTextAnnotation_Click(object sender, EventArgs e) { AddTextAnnotation(_lastContextMenuLocationX, _lastContextMenuLocationY); } void toolStripMenuItemPostMarket_Click(object sender, EventArgs e) { ToolStripMenuItem menuItem = sender as ToolStripMenuItem; if (null != menuItem) { _showPostMarket = menuItem.Checked; DisplayChart(); } } void toolStripMenuItemPreMarket_Click(object sender, EventArgs e) { ToolStripMenuItem menuItem = sender as ToolStripMenuItem; if (null != menuItem) { _showPreMarket = menuItem.Checked; DisplayChart(); } } void menuItem_Click(object sender, EventArgs e) { TimeframeToolStripMenuItem menuItem = sender as TimeframeToolStripMenuItem; if (null != menuItem && menuItem.TimeFrame != _timeframe) { _timeframe = menuItem.TimeFrame; ResetData(); UpdateContextMenuState(); DisplayChart(); } } private void ResetData() { _priceSeries.Points.Clear(); _volumeSeries.Points.Clear(); _volumeAvgSeries.Points.Clear(); chart1.Annotations.Clear(); _chartArea.AxisX.CustomLabels.Clear(); _chartArea.AxisY2.CustomLabels.Clear(); } private class SupportResistanceLine { public DateTime From { get; set; } public DateTime? To { get; set; } public double Level { get; set; } public bool IsSupport { get; set; } public SupportResistanceLine(DateTime from, double level, bool isSupport, DateTime? to = null) { From = from; Level = level; IsSupport = isSupport; To = to; } } void IAcceptSymbolLinking.ChangeSymbol(string symbol, RowData rowData) { SetTheSymbol(symbol, rowData); } public void SetTheSymbol(string symbol, RowData rowData) { if (Symbol != symbol) { Symbol = symbol; UpdateWindowTitle(); DoOpenChart(rowData); } } string IAcceptSymbolLinking.CurrentSymbol() { return _symbol; } private string _linkChannel = ""; string IAcceptSymbolLinking.LinkChannel { get { return _linkChannel; } set { _linkChannel = value; } } private bool _linkingEnabled = true; bool IAcceptSymbolLinking.LinkEnabled { get { return _linkingEnabled; } set { _linkingEnabled = value; } } public void SetSymbol(RowData rowData) { if (null == rowData) return; string symbol = rowData.GetAsString("SYMBOL", ""); if (symbol == "") symbol = rowData.GetAsString("symbol", ""); if (symbol == "") return; Symbol = symbol; } public void DoOpenChart(RowData rowData) { Show(); if (null != rowData) { SetSymbol(rowData); double? time_t = rowData.GetAsDouble("TIME", 0); if (time_t.HasValue) { DateTime? alertTime = ServerFormats.DecodeServerTime(time_t.Value.ToString()); if (alertTime.HasValue) _annotations.Add(new TIAnnotation(alertTime.Value, rowData.GetAsString("DESCRIPTION"), 0.0, true, false)); string alertCode = rowData.GetAsString("TYPE", ""); string alertDescription = rowData.GetAsString("ALT_DESCRIPTION", ""); if (alertCode == "NHP" || alertCode == "NHPF" || alertCode == "HPRE" || alertCode == "HPOST") { NameValueCollection query = TradeIdeas.TIProGUI.DescriptionFormatter.ParseEncodedDescription(alertDescription); double price = 0.0; ServerFormats.TryParse(query["p"], out price); DateTime? newHighFrom = GetAltDescriptionDateTime(query["mdy"]); if (newHighFrom.HasValue && price != 0.0) AddResistanceLine(newHighFrom.Value, price, alertTime.Value); } } else if (null != rowData.GetAsObject("ENTRYTIME")) { DateTime? entryTime = rowData.GetAsObject("ENTRYTIME") as DateTime?; if (entryTime.HasValue) { double? price = rowData.GetAsDouble("ENTRYPRICE", 0.0); _annotations.Add(new TIAnnotation(entryTime.Value, "Entry at " + price, price.Value, true, true)); } DateTime? exitTime = rowData.GetAsObject("EXITTIME") as DateTime?; if (exitTime.HasValue) { double? price = rowData.GetAsDouble("EXITPRICE", 0.0); _annotations.Add(new TIAnnotation(exitTime.Value, "Exit at " + price, price.Value, false, true)); } } } DisplayChart(); BringToFront(); } private DateTime? GetAltDescriptionDateTime(string mdy) { DateTime? toReturn = null; try { string[] dateParts = mdy.Split('/'); if (dateParts.Length == 3) { int month = 0; int.TryParse(dateParts[0], out month); int day = 0; int.TryParse(dateParts[1], out day); int year = 0; int.TryParse(dateParts[2], out year); toReturn = new DateTime(year, month, day); } } catch { } return toReturn; } private List _annotations = new List(); private class TIAnnotation { public DateTime Date { get; set; } public string Description { get; set; } public double Price { get; set; } public bool Up { get; set; } public bool IsPriceSpecific { get; set; } public TIAnnotation(DateTime date, string description, double price, bool up, bool isPriceSpecific) { Date = date; Description = description; Price = price; Up = up; IsPriceSpecific = IsPriceSpecific; } } private VerticalLineAnnotation _crossHairsVertical = new VerticalLineAnnotation(); private HorizontalLineAnnotation _crossHairsHorizontal = new HorizontalLineAnnotation(); private RectangleAnnotation _yAxisAnnotation = new RectangleAnnotation(); private RectangleAnnotation _xAxisAnnotation = new RectangleAnnotation(); private RectangleAnnotation _lastPriceAnnotation = new RectangleAnnotation(); private RectangleAnnotation _volumeAnnotation = new RectangleAnnotation(); private RectangleAnnotation _volumeAvgAnnotation = new RectangleAnnotation(); private RectangleAnnotation _barDescriptionAnnotation = new RectangleAnnotation(); private void chart1_MouseMove(object sender, MouseEventArgs e) { try { if (_chartDrawn) { _chartArea.RecalculateAxesScale(); double xValue = _chartArea.AxisX.PixelPositionToValue(e.X); double yValue = _chartArea.AxisY2.PixelPositionToValue(e.Y); DoCrossHairs(xValue, yValue); xValue = Math.Round(xValue); TIDataPoint dataPoint = GetTIDataPoint(xValue); if (null != dataPoint) ChartEnvironment.CrossHairMoved(dataPoint.Start, dataPoint.End, yValue, _symbol, this); // check for annotation HitTestResult hitTestResult = chart1.HitTest(e.X, e.Y); if (hitTestResult.ChartElementType == ChartElementType.Annotation && !(hitTestResult.Object is VerticalLineAnnotation) && !(hitTestResult.Object is HorizontalLineAnnotation)) { //System.Diagnostics.Debug.WriteLine("On annotation in hittest"); } } } catch (Exception exception) { string debugView = exception.ToString(); } } /// /// Get the data point corresponding to the specified xValue. /// /// /// private TIDataPoint GetTIDataPoint(double xValue) { foreach (DataPoint dataPoint in _priceSeries.Points) { if (dataPoint.XValue == xValue) return (TIDataPoint)dataPoint; } return null; } private void DoCrossHairs(double xValue, double yValue) { if (xValue < _chartArea.AxisX.Minimum) xValue = _chartArea.AxisX.Minimum; else if (xValue > _chartArea.AxisX.Maximum) xValue = _chartArea.AxisX.Maximum; if (yValue < _chartArea.AxisY2.Minimum) yValue = _chartArea.AxisY2.Minimum; else if (yValue > _chartArea.AxisY2.Maximum) yValue = _chartArea.AxisY2.Maximum; _crossHairsVertical.Name = "crossHairsVertical"; _crossHairsVertical.IsSizeAlwaysRelative = false; _crossHairsVertical.LineDashStyle = ChartDashStyle.Dash; _crossHairsVertical.X = Math.Round(xValue, 0); _crossHairsVertical.Y = _chartArea.AxisY2.Minimum; _crossHairsVertical.Height = _chartArea.AxisY2.Maximum - _chartArea.AxisY2.Minimum; _lastContextMenuLocationX = xValue; _crossHairsVertical.LineColor = LINE_COLOR; _crossHairsVertical.AxisX = _chartArea.AxisX; _crossHairsVertical.AxisY = _chartArea.AxisY2; _crossHairsVertical.Visible = true; _crossHairsHorizontal.Name = "crossHairsHorizontal"; _crossHairsHorizontal.IsSizeAlwaysRelative = false; _crossHairsHorizontal.LineDashStyle = ChartDashStyle.Dash; _crossHairsHorizontal.X = _chartArea.AxisX.Minimum; _crossHairsHorizontal.Y = yValue; _crossHairsHorizontal.Width = _chartArea.AxisX.Maximum - _chartArea.AxisX.Minimum; _crossHairsHorizontal.LineColor = LINE_COLOR; _crossHairsHorizontal.AxisX = _chartArea.AxisX; _crossHairsHorizontal.AxisY = _chartArea.AxisY2; _crossHairsHorizontal.Visible = true; if (!chart1.Annotations.Contains(_crossHairsVertical)) chart1.Annotations.Add(_crossHairsVertical); if (!chart1.Annotations.Contains(_crossHairsHorizontal)) chart1.Annotations.Add(_crossHairsHorizontal); _yAxisAnnotation.Name = "yAxisAnnotation"; _yAxisAnnotation.Tag = yValue; _yAxisAnnotation.Text = yValue.ToString("N2"); _yAxisAnnotation.X = _chartArea.AxisX.Maximum; SizeF textSize = chart1.CreateGraphics().MeasureString(_yAxisAnnotation.Text, _yAxisAnnotation.Font); //_yAxisAnnotation.Height = textSize.Height * 2; _yAxisAnnotation.Y = GetTextAnnotationYPosition(textSize.Height, yValue, _chartArea.AxisY2); _lastContextMenuLocationY = yValue; _yAxisAnnotation.IsSizeAlwaysRelative = false; _yAxisAnnotation.BackColor = LINE_COLOR; _yAxisAnnotation.LineWidth = 0; _yAxisAnnotation.ForeColor = Color.White; _yAxisAnnotation.AxisX = _chartArea.AxisX; _yAxisAnnotation.AxisY = _chartArea.AxisY2; SizePriceAnnotation(_yAxisAnnotation); _yAxisAnnotation.Visible = true; _xAxisAnnotation.Name = "xAxisAnnotation"; _barDescriptionAnnotation.Name = "barDescriptionAnnotation"; TIDataPoint point = GetDataPoint(xValue); if (null != point) { _xAxisAnnotation.Text = point.Start.ToString(GetAnnotationDateFormat()); _xAxisAnnotation.Tag = xValue; SizeF textSizeDate = chart1.CreateGraphics().MeasureString(_xAxisAnnotation.Text, _xAxisAnnotation.Font); _xAxisAnnotation.IsSizeAlwaysRelative = false; _xAxisAnnotation.BackColor = LINE_COLOR; _xAxisAnnotation.LineWidth = 0; _xAxisAnnotation.ForeColor = Color.White; _xAxisAnnotation.AxisX = _chartArea.AxisX; _xAxisAnnotation.AxisY = _chartArea.AxisY2; _xAxisAnnotation.Visible = true; SizeDateAnnotation(_xAxisAnnotation); Axis yAxis = _chartArea.AxisY2; _barDescriptionAnnotation.Font = new Font(FontFamily.GenericMonospace, _barDescriptionAnnotation.Font.Size); _barDescriptionAnnotation.Alignment = ContentAlignment.MiddleLeft; SetBarDescriptionTheme(); _barDescriptionAnnotation.Text = GetBarDescription(point); _barDescriptionAnnotation.IsSizeAlwaysRelative = false; _barDescriptionAnnotation.AxisX = _chartArea.AxisX; _barDescriptionAnnotation.AxisY = yAxis; _barDescriptionAnnotation.Visible = true; //if (!_barDescriptionLocationSet) SetBarDescriptionLocation(); SizeBarDescription(); if (!chart1.Annotations.Contains(_barDescriptionAnnotation)) chart1.Annotations.Add(_barDescriptionAnnotation); } if (!chart1.Annotations.Contains(_xAxisAnnotation)) chart1.Annotations.Add(_xAxisAnnotation); if (!chart1.Annotations.Contains(_yAxisAnnotation)) chart1.Annotations.Add(_yAxisAnnotation); } private List _extraDataPoints = new List(); private string GetBarDescription(TIDataPoint point) { string toReturn = "D: " + point.Start.ToString(GetAnnotationDateFormatLong()) + "\nO: " + point.Open.ToString("N2") + "\nH: " + point.High.ToString("N2") + "\nL: " + point.Low.ToString("N2") + "\nC: " + point.Close.ToString("N2"); int index = _priceSeries.Points.IndexOf(point); foreach (string dataName in _extraDataPoints) { toReturn += "\n" + dataName + ": " + point.RowData.GetAsDouble(dataName, double.NaN).ToFriendlyString(); } return toReturn; } private void SetBarDescriptionTheme() { _barDescriptionAnnotation.BackColor = _watermarkColor; _barDescriptionAnnotation.ForeColor = _barDescriptionForeColor; } private void SetBarDescriptionLocation() { int pointsPartiallyHiddenNormal = GetPointsPartiallyHidden(GetBarDescriptionRectangle(true)); int pointsPartiallyHiddenAlternate = GetPointsPartiallyHidden(GetBarDescriptionRectangle(false)); //System.Diagnostics.Debug.WriteLine("SetBarDescriptionLocation: normal points hidden=" + pointsPartiallyHiddenNormal + ", alt points hidden=" + pointsPartiallyHiddenAlternate); _barDescriptionNormal = pointsPartiallyHiddenNormal == 0 || pointsPartiallyHiddenAlternate > pointsPartiallyHiddenNormal; } private int GetPointsPartiallyHidden(ChartRectangle rectangle) { int pointsPartiallyHidden = 0; double top = rectangle.Y + rectangle.Height; double bottom = rectangle.Y; double right = rectangle.X + rectangle.Width; foreach (TIDataPoint point in _priceSeries.Points.OfType()) { if (point.XValue <= right) { if ((point.High < top && point.High > bottom) || (point.Low < top && point.Low > bottom)) pointsPartiallyHidden++; } } return pointsPartiallyHidden; } private bool _barDescriptionNormal = true; private class ChartRectangle { public double X { get; set; } public double Y { get; set; } public double Width { get; set; } public double Height { get; set; } public ChartRectangle(double x, double y, double width, double height) { X = x; Y = y; Width = width; Height = height; } } private ChartRectangle GetBarDescriptionRectangle(bool normal) { int margin = 5; SizeF textSize = chart1.CreateGraphics().MeasureString(_barDescriptionAnnotation.Text, _barDescriptionAnnotation.Font); Axis yAxis = _chartArea.AxisY2; Axis xAxis = _chartArea.AxisX; double pixelHeight = textSize.Height * 1.1; double valueHeight = GetAxisDistance(pixelHeight, yAxis); double pixelWidth = textSize.Width * 1.1; double valueWidth = GetAxisDistance(pixelWidth, xAxis); double verticalMargin = GetAxisDistance(margin, yAxis); double horizontalMargin = GetAxisDistance(margin, xAxis); double x = xAxis.Minimum + horizontalMargin; double y = yAxis.Maximum - valueHeight - verticalMargin; if (!normal) y = yAxis.Minimum + verticalMargin; ChartRectangle toReturn = new ChartRectangle(x, y, valueWidth, valueHeight); return toReturn; } private void SizeBarDescription() { ChartRectangle rectangle = GetBarDescriptionRectangle(_barDescriptionNormal); _barDescriptionAnnotation.Width = rectangle.Width; _barDescriptionAnnotation.Height = rectangle.Height; _barDescriptionAnnotation.X = rectangle.X; _barDescriptionAnnotation.Y = rectangle.Y; double verticalChartPercentCovered = GetPercentOfAxisDistance(rectangle.Width, chart1.ChartAreas[0].AxisX); double horizontalChartPercentCovered = GetPercentOfAxisDistance(rectangle.Height, chart1.ChartAreas[0].AxisY2); _barDescriptionAnnotation.Visible = verticalChartPercentCovered < 30 && horizontalChartPercentCovered < 30; } private double GetPercentOfAxisDistance(double axisPoints, Axis axis) { try { double minValue = axis.Minimum; double maxValue = axis.Maximum; double axisLength = maxValue - minValue; return axisPoints / axisLength * 100; } catch { } return 0; } private double GetAxisDistance(double pixels, Axis axis) { try { if (axis.AxisName == AxisName.X) { double minPixel = axis.ValueToPixelPosition(axis.Minimum); double rightPixel = minPixel + pixels; double rightValue = axis.PixelPositionToValue(rightPixel); return rightValue - axis.Minimum; } else { double maxPixel = axis.ValueToPixelPosition(axis.Maximum); double bottomPixel = maxPixel + pixels; double bottomValue = axis.PixelPositionToValue(bottomPixel); return axis.Maximum - bottomValue; } } catch { } return 0; } private void SetLastPriceAnnotation(bool clear = false) { try { if (_chartDrawn) { this.InvokeIfRequired(delegate() { if (clear && chart1.Annotations.Contains(_lastPriceAnnotation)) chart1.Annotations.Remove(_lastPriceAnnotation); if (!chart1.Annotations.Contains(_lastPriceAnnotation)) { _chartArea.RecalculateAxesScale(); double lastPrice = GetLastPrice(); if (lastPrice > 0) { _lastPriceAnnotation.Name = "lastPriceAnnotation"; _lastPriceAnnotation.Tag = lastPrice; _lastPriceAnnotation.Text = lastPrice.ToString("N2"); _lastPriceAnnotation.X = _chartArea.AxisX.Maximum; SizeF textSizeLastPrice = chart1.CreateGraphics().MeasureString(_lastPriceAnnotation.Text, _lastPriceAnnotation.Font); _lastPriceAnnotation.Y = GetTextAnnotationYPosition(textSizeLastPrice.Height, lastPrice, _chartArea.AxisY2); _lastPriceAnnotation.IsSizeAlwaysRelative = false; _lastPriceAnnotation.BackColor = Color.LimeGreen; //_lastPriceAnnotation.Width = textSizeLastPrice.Width; _lastPriceAnnotation.LineWidth = 1; _lastPriceAnnotation.LineColor = LINE_COLOR; _lastPriceAnnotation.ForeColor = Color.White; _lastPriceAnnotation.AxisX = _chartArea.AxisX; _lastPriceAnnotation.AxisY = _chartArea.AxisY2; SizePriceAnnotation(_lastPriceAnnotation); _lastPriceAnnotation.Visible = true; chart1.Annotations.Add(_lastPriceAnnotation); } } }); } } catch (Exception e) { string debugView = e.ToString(); } } private double GetLastPrice() { double toReturn = 0.0; if (chart1.Series.Count > 0 && null != _priceSeries && _priceSeries.Points.Count > 0) return _priceSeries.Points.Last().YValues[3]; return toReturn; } private void SetVolumeAnnotation(bool clear = false) { try { if (_chartDrawn) { this.InvokeIfRequired(delegate() { if (clear && chart1.Annotations.Contains(_volumeAnnotation)) chart1.Annotations.Remove(_volumeAnnotation); if (!chart1.Annotations.Contains(_volumeAnnotation) && chart1.Series.Count > 2 && null != _volumeSeries) { _chartArea.RecalculateAxesScale(); double lastVolume = _volumeSeries.Points.Last().YValues[0]; if (lastVolume > 0) { _volumeAnnotation.Name = "volumeAnnotation"; _volumeAnnotation.Tag = lastVolume; _volumeAnnotation.Text = lastVolume.ToFriendlyString(); _volumeAnnotation.X = _chartArea.AxisX.Maximum; SizeF textSizeLastPrice = chart1.CreateGraphics().MeasureString(_volumeAnnotation.Text, _volumeAnnotation.Font); _volumeAnnotation.Y = GetTextAnnotationYPosition(textSizeLastPrice.Height, lastVolume, _chartArea.AxisY); _volumeAnnotation.IsSizeAlwaysRelative = false; _volumeAnnotation.BackColor = Color.Green; //_volumeAnnotation.Width = textSizeLastPrice.Width; _volumeAnnotation.LineWidth = 1; _volumeAnnotation.ForeColor = Color.White; _volumeAnnotation.AxisX = _chartArea.AxisX; _volumeAnnotation.AxisY = _chartArea.AxisY; SizePriceAnnotation(_volumeAnnotation); _volumeAnnotation.Visible = true; chart1.Annotations.Add(_volumeAnnotation); } } }); } } catch (Exception e) { string debugView = e.ToString(); } } private string GetAnnotationDateFormat() { if (TimeframeIntraday()) return "hh:mm"; else return "MM/dd"; } private string GetAnnotationDateFormatLong() { if (TimeframeIntraday()) return "MM/dd hh:mm"; else return "MM/dd/yy"; } private TIDataPoint GetDataPoint(double x) { TIDataPoint toReturn = null; if (chart1.Series.Count > 0 && null != _priceSeries) { foreach (TIDataPoint point in _priceSeries.Points.OfType()) { if (point.XValue == Math.Round(x, 0)) return point; } } return toReturn; } /// /// Returns the actual X position for the top left corner of the text annotation to be placed so that it is centered at value. /// /// Height of text /// Actual X value /// private double GetTextAnnotationXPosition(double textWidth, double value, Axis axis) { // measuring text returns pixel height but we need to set the location of the text annotation by // point on the axis, not by pixels double toReturn = value; try { double maxPosition = axis.Maximum; double minPosition = axis.Minimum; double maxPixel = axis.ValueToPixelPosition(maxPosition); double minPixel = axis.ValueToPixelPosition(minPosition); double pixelForValue = axis.ValueToPixelPosition(value); double pixelTarget = pixelForValue - textWidth / 1.5; if (pixelTarget > maxPixel) toReturn = maxPosition; else if (pixelTarget < minPixel) toReturn = minPosition; else toReturn = axis.PixelPositionToValue(pixelTarget); } catch { } return toReturn; } /// /// Returns the actual Y position for the top left corner of the text annotation to be placed so that it is centered at value. /// /// Height of text /// Actual Y value /// private double GetTextAnnotationYPosition(double textHeight, double value, Axis axis) { // measuring text returns pixel height but we need to set the location of the text annotation by // point on the axis, not by pixels double toReturn = value; try { double maxPosition = axis.Maximum; double minPosition = axis.Minimum; double maxPixel = axis.ValueToPixelPosition(maxPosition); double minPixel = axis.ValueToPixelPosition(minPosition); double pixelForValue = axis.ValueToPixelPosition(value); double pixelTarget = pixelForValue + textHeight / 2; if (pixelTarget < maxPixel) toReturn = maxPosition; else if (pixelTarget > minPixel) toReturn = minPosition; else toReturn = axis.PixelPositionToValue(pixelTarget); } catch { } return toReturn; } private void AddTextAnnotation(double x, double y) { try { //double xValue = _chartArea.AxisX.PixelPositionToValue(x); //double yValue = _chartArea.AxisY2.PixelPositionToValue(y); TextAnnotation textAnnotation = new TextAnnotation(); textAnnotation.IsSizeAlwaysRelative = false; textAnnotation.X = x; textAnnotation.Y = y; textAnnotation.AxisX = _chartArea.AxisX; textAnnotation.AxisY = _chartArea.AxisY2; textAnnotation.Text = "Type here"; textAnnotation.AllowTextEditing = true; textAnnotation.AllowMoving = true; textAnnotation.AllowSelecting = true; textAnnotation.AllowResizing = true; chart1.Annotations.Add(textAnnotation); textAnnotation.BeginTextEditing(); } catch { } } private double _lastContextMenuLocationX; private double _lastContextMenuLocationY; private void contextMenuStrip1_Opened(object sender, EventArgs e) { //_lastContextMenuLocationX = contextMenuStrip1.Bounds.X; //_lastContextMenuLocationY = contextMenuStrip1.Bounds.Y; //_lastContextMenuLocationX = contextMenuStrip1.Left - chart1.Left; //_lastContextMenuLocationY = contextMenuStrip1.Top - chart1.Top; } private void chart1_PostPaint(object sender, ChartPaintEventArgs e) { // there is a problem with PixelPositionToValue - if it is called before the chart is loaded it returns garbage (0). // the problem is there's no event to capture for "chart ready" that would be when it works properly. // You're supposed to be able to call RecalculateAxes() but that doesn't work all the time. So this ugly hack gets // around it for now. After the chart is initialized we set a timer for 100 milliseconds and then call ResizeChart() // so that it can be drawn properly. There is still a delay where sometimes the chart looks weird but right now // this is as good as we can get until we figure out another way. if (!_chartDrawn) { ResizeChart(); SetInitializedTimer(); } if (!chart1.Annotations.Contains(_lastPriceAnnotation)) SetLastPriceAnnotation(); if (!chart1.Annotations.Contains(_volumeAnnotation)) SetVolumeAnnotation(); } private System.Windows.Forms.Timer _initializedTimer = new System.Windows.Forms.Timer(); private void SetInitializedTimer() { if (!_initializedTimer.Enabled) { _initializedTimer.Interval = 100; _initializedTimer.Tick -= initializedTimer_Tick; _initializedTimer.Tick += initializedTimer_Tick; _initializedTimer.Enabled = true; _initializedTimer.Start(); } } void initializedTimer_Tick(object sender, EventArgs e) { this.InvokeIfRequired(delegate() { _initializedTimer.Dispose(); ResizeChart(); if (!_chartDrawn) SetInitializedTimer(); }); } private void chart1_PrePaint(object sender, ChartPaintEventArgs e) { if (e.ChartElement is ChartArea) { if (_symbol != "" && ((ChartArea)e.ChartElement).Name == "ChartArea1") { RectangleF rect = e.ChartGraphics.GetAbsoluteRectangle(e.Chart.ChartAreas[0].InnerPlotPosition.ToRectangleF()); Font font = new Font(Font.FontFamily, 10); string waterMarkText = _symbol + ", " + GetTimeframeShort(_timeframe); SizeF textSize = e.ChartGraphics.Graphics.MeasureString(waterMarkText, font); while (textSize.Width < rect.Width - rect.Width * 0.4) { font = new Font(Font.FontFamily, font.Size * 1.01f); textSize = e.ChartGraphics.Graphics.MeasureString(waterMarkText, font); } float x = (rect.Width / 2) - (textSize.Width / 2); float y = (rect.Height / 2) - (textSize.Height / 2); //e.ChartGraphics.Graphics.DrawString(waterMarkText, font, new SolidBrush(_watermarkColor), (float)(rect.X + rect.Width) * 0.1f, (float)(rect.Y + rect.Height) * 0.3f); e.ChartGraphics.Graphics.DrawString(waterMarkText, font, new SolidBrush(_watermarkColor), x, y); Bitmap logo = TIProChartsExtension.Properties.Resources.logo_bw; int xAsInt = (int)x; int yAsInt = (int)y; float targetWidth = textSize.Width * (float)0.67; if (targetWidth > 300) targetWidth = 300; float targetHeight = (logo.Height * targetWidth) / logo.Width; if (targetWidth > 80) { float targetX = (rect.Width / 2) - (targetWidth / 2); float targetY = (rect.Height / 2) - (targetHeight / 2) + (textSize.Height * 0.75f); if (targetY + targetHeight > rect.Height) targetY = rect.Height - targetHeight; RectangleF targetRect = new RectangleF(targetX, targetY, targetWidth, targetHeight); e.ChartGraphics.Graphics.DrawImage(logo, targetRect); } } } } /// /// hidden price points stores points in the past history buffer. these are points that aren't displayed /// because of the settings for _pixelsPerBarMinimum and _rightEdgeBarsFromCurrent. Oldest point is first /// private List> _hiddenPricePoints = new List>(); /// /// future price points is the buffer for future history bars. /// private List> _futurePricePoints = new List>(); /// /// Minimum width of candles /// private double _pixelsPerBarMinimum = 5; private void chart1_Resize(object sender, EventArgs e) { if (_chartDrawn) ResizeChart(); } private void AdjustFutureBarsIfRequired() { if (_futurePricePoints.Count > _rightEdgeBarsFromCurrent) { while (_futurePricePoints.Count > _rightEdgeBarsFromCurrent) { List pointsToAdd = _futurePricePoints.First(); DataPoint pricePoint = pointsToAdd[0]; DataPoint volumePoint = pointsToAdd[1]; DataPoint volumeAvgPoint = pointsToAdd[2]; _priceSeries.Points.Add(pricePoint); _volumeSeries.Points.Add(volumePoint); _volumeAvgSeries.Points.Add(volumeAvgPoint); _futurePricePoints.Remove(pointsToAdd); } } else if (_futurePricePoints.Count < _rightEdgeBarsFromCurrent) { int startIndexFromEnd = _rightEdgeBarsFromCurrent - _futurePricePoints.Count; while (_futurePricePoints.Count < _rightEdgeBarsFromCurrent) { List removedPoints = new List(); DataPoint pricePoint = _priceSeries.Points.Last(); DataPoint volumePoint = _volumeSeries.Points.Last(); DataPoint volumeAvgPoint = _volumeAvgSeries.Points.Last(); removedPoints.Add(pricePoint); removedPoints.Add(volumePoint); removedPoints.Add(volumeAvgPoint); _priceSeries.Points.RemoveAt(_priceSeries.Points.Count - 1); _volumeSeries.Points.RemoveAt(_volumeSeries.Points.Count - 1); _volumeAvgSeries.Points.RemoveAt(_volumeAvgSeries.Points.Count - 1); _futurePricePoints.Insert(0, removedPoints); } } } private void ResizeChart() { try { AdjustFutureBarsIfRequired(); double pixelsPerBar = 0; int numPoints = 0; double chartAreaWidth = 0; if (chart1.Series.Count > 0) { numPoints = _priceSeries.Points.Count; _chartArea.RecalculateAxesScale(); double pixelMin = _chartArea.AxisX.ValueToPixelPosition(_chartArea.AxisX.Minimum); double pixelMax = _chartArea.AxisX.ValueToPixelPosition(_chartArea.AxisX.Maximum); chartAreaWidth = pixelMax - pixelMin; pixelsPerBar = chartAreaWidth / numPoints; } int maximumPointsToShow = (int)Math.Floor(chartAreaWidth / _pixelsPerBarMinimum); //System.Diagnostics.Debug.WriteLine("Resizing: Points=" + numPoints + ", Width=" + chartAreaWidth + ", barsPerPixel=" + pixelsPerBar + ", maxptstoshow=" + maximumPointsToShow); if (chartAreaWidth <= 0) { chart1.Invalidate(); return; } // remove points from the beginning of the chart until we're displaying < maximumPointsToShow if (pixelsPerBar != 0 && pixelsPerBar < _pixelsPerBarMinimum) { int startIndex = _priceSeries.Points.Count - maximumPointsToShow - 1; System.Diagnostics.Debug.WriteLine("below threshold: startIndex=" + startIndex + ", pointsToShow=" + maximumPointsToShow); while (_priceSeries.Points.Count > maximumPointsToShow) { List removedPoints = new List(); DataPoint pricePoint = _priceSeries.Points[0]; DataPoint volumePoint = _volumeSeries.Points[0]; DataPoint volumeAvgPoint = _volumeAvgSeries.Points[0]; removedPoints.Add(pricePoint); removedPoints.Add(volumePoint); removedPoints.Add(volumeAvgPoint); _priceSeries.Points.RemoveAt(0); _volumeSeries.Points.RemoveAt(0); _volumeAvgSeries.Points.RemoveAt(0); _hiddenPricePoints.Add(removedPoints); } } else if (_hiddenPricePoints.Count > 0 && _priceSeries.Points.Count < maximumPointsToShow) { // unhide them while (_priceSeries.Points.Count < maximumPointsToShow && _hiddenPricePoints.Count > 0) { List pointsToAdd = _hiddenPricePoints.Last(); DataPoint pricePoint = pointsToAdd[0]; DataPoint volumePoint = pointsToAdd[1]; DataPoint volumeAvgPoint = pointsToAdd[2]; _priceSeries.Points.Insert(0, pricePoint); StyleVolumePoint((TIDataPoint)pricePoint, volumePoint); _volumeSeries.Points.Insert(0, volumePoint); _volumeAvgSeries.Points.Insert(0, volumeAvgPoint); _hiddenPricePoints.Remove(pointsToAdd); } } SetChartMaxAndMin(_priceSeries.Points.Min(x => x.YValues[1]), _priceSeries.Points.Max(x => x.YValues[0])); SetBarDescriptionLocation(); SizeBarDescription(); if (chart1.Annotations.Contains(_lastPriceAnnotation)) SizePriceAnnotation(_lastPriceAnnotation); if (chart1.Annotations.Contains(_yAxisAnnotation)) SizePriceAnnotation(_yAxisAnnotation); if (chart1.Annotations.Contains(_xAxisAnnotation)) SizeDateAnnotation(_xAxisAnnotation); if (chart1.Annotations.Contains(_volumeAnnotation)) SizePriceAnnotation(_volumeAnnotation); HighlightPrePostMarket(); SetVolumeAnnotation(true); SetLastPriceAnnotation(true); SizeInnerPlot(); SetGridLines(); SetDateLabels(); chart1.Invalidate(); _chartDrawn = true; } catch (Exception e) { string debugView = e.ToString(); } } private void SizeInnerPlot() { int DESIRED_AXIS_WIDTH = 60; chart1.ChartAreas[0].InnerPlotPosition.Auto = false; chart1.ChartAreas[0].InnerPlotPosition.Auto = true; chart1.ChartAreas[0].InnerPlotPosition.Height = 96; float percentWidth = ((float)chart1.Width - DESIRED_AXIS_WIDTH) / (float)chart1.Width * 100; chart1.ChartAreas[0].InnerPlotPosition.Width = percentWidth; //int DESIRED_TICK_WIDTH = 10; //float percentTickWidth = DESIRED_TICK_WIDTH / (float)chart1.Width * 100; //chart1.ChartAreas[0].AxisY2.MinorTickMark.Size = percentTickWidth; } private void SizeDateAnnotation(RectangleAnnotation annotation) { SizeF textSize = chart1.CreateGraphics().MeasureString(annotation.Text, annotation.Font); Axis xAxis = annotation.AxisX; Axis yAxis = annotation.AxisY; double? xValue = annotation.Tag as double?; double pixelHeight = textSize.Height * 1.1; double valueHeight = GetAxisDistance(pixelHeight, yAxis); double pixelWidth = textSize.Width * 1.3; double valueWidth = GetAxisDistance(pixelWidth, annotation.AxisX); if (xValue.HasValue) { annotation.X = GetTextAnnotationXPosition(textSize.Width, Math.Round(xValue.Value, 0), xAxis); annotation.Y = yAxis.Minimum - valueHeight; annotation.Height = valueHeight; annotation.Width = Math.Abs(valueWidth); } } private void SizePriceAnnotation(RectangleAnnotation annotation) { SizeF textSize = chart1.CreateGraphics().MeasureString(annotation.Text, annotation.Font); Axis yAxis = annotation.AxisY; double? yValue = annotation.Tag as double?; double pixelHeight = textSize.Height * 1.1; double valueHeight = GetAxisDistance(pixelHeight, yAxis); double pixelWidth = textSize.Width * 1.3; double valueWidth = GetAxisDistance(pixelWidth, annotation.AxisX); annotation.Width = Math.Abs(valueWidth); if (yValue.HasValue) { annotation.Height = valueHeight; annotation.Y = GetTextAnnotationYPosition(textSize.Height, yValue.Value, yAxis); } } private void chart1_KeyPress(object sender, KeyPressEventArgs e) { string key = e.KeyChar.ToString().ToUpper(); ChangeSymbol changeSymbol = new ChangeSymbol(key); DialogResult result = changeSymbol.ShowDialog(); if (result == System.Windows.Forms.DialogResult.OK) { SetTheSymbol(changeSymbol.Symbol, null); List chartsToLink = Application.OpenForms.OfType().ToList(); foreach (Chart chart in chartsToLink) { IAcceptSymbolLinking chartLink = chart as IAcceptSymbolLinking; if (null != chartLink && chartLink.LinkEnabled) chartLink.ChangeSymbol(changeSymbol.Symbol, null); } } } WindowIconCache ISaveLayout.WindowIconCache { get { return WindowIconCache; } } public void SetSnapToGrid(bool enabled) { if (enabled) formSnapper1.Enabled = true; else formSnapper1.Enabled = false; } } /// /// This class requests a grid from the server. This takes care of a lot of details, like automatic /// retries. The public interface to this class should only be called from the GUI thread, and the /// callback will return in the GUI thread. /// /// This required data from the TIQ server. This will only work if you are connected to the proxy. /// /// This could be used in other places. I almost put this into the TIProData library. But this /// doesn't follow the conventions of that library. In particular, this class uses the Control /// class, and that library doesn't reference any GUI stuff, so it wouldn't compile. /// class SingleGridRequest { public struct Response { public String symbol; public String csvData; }; public readonly String Symbol; private Dictionary _message; private Control _owner; private SendManager _sendManager; private Action _callBack; /// /// Send a request to the server. /// /// Request data for this symbol. /// Probably the output of PrototypeEditor.GetTcl(). /// /// If a formula returns an error and this is true we print an error message. If it's /// false we display "". False is better for things aimed at an end user. The error /// messages are crude and really only helpful to a programmer. /// /// Use this to send the request. /// /// Use this to move server responses into the correct thread. Also, if this is a /// form and the form closes, the callback and any automatic retries are /// automatically canceled. If this is null, everything will still work except for /// the automatic cancel. /// /// /// This will be called when the data comes from the server. This will only be called /// once. This will be called in the GUI thread. /// public SingleGridRequest(String symbol, String prototype, Boolean showErrors, SendManager sendManager, Control owner, Action callBack) { Symbol = symbol; _owner = owner ?? new Control(); _sendManager = sendManager; _callBack = callBack; StringBuilder symbols = new StringBuilder(); symbols.LAppend(symbol); object[] message = new object[] { "command", "flex_command", "subcommand", "make_and_show", "prototype", prototype, "symbols", symbols.ToString(), "show_errors", showErrors?"1":null }; _message = TalkWithServer.CreateMessage(message); _sendManager.SendMessage(_message, GetDataResponse); } // Cancel any callbacks. Unlike the TalkWithServer messages, this takes // effect immediately. public void Abort() { _callBack = null; } private void GetDataResponse(byte[] body, object clientId) { _owner.BeginInvokeIfRequired((MethodInvoker)delegate { ResponseInThread(body); }); } private void ResponseInThread(byte[] body) { if (null == _callBack) // Aborted return; if (null == body) // Communication problem. Try again. _sendManager.SendMessage(_message, GetDataResponse); else { Response response; XmlNode message = XmlHelper.Get(body).Node(0); String type = message.Property("type"); if (type == "result") response.csvData = message.Text(); else // Error reported by server. response.csvData = ""; response.symbol = Symbol; _callBack(response); } } } }