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);
}
}
}
}