using System; using System.Collections.Generic; using System.ComponentModel; using System.Drawing; using System.Data; using System.Linq; using System.Text; using System.Windows.Forms; using TradeIdeas.TIProGUI; using TradeIdeas.TIProData; namespace TIProAutoTradeExtension { public partial class ProfitContinuum : UserControl { /// /// List of filtered positions /// private BindingList _filteredPositions; /// /// list of positions /// private BindingList _positions; /// /// Set the positions. Get the positions filtered /// public BindingList Positions { get { if (_filteredPositions != null) { return new BindingList(_positions.ToList().Where(position => _filteredPositions.Contains(position)).ToList()); } return new BindingList(_positions); } set { _positions = value; }} /// /// Max value for Total profit /// private decimal _maxValueTotal = 1; /// /// Min value for Total profit /// private decimal _minValueTotal = -1; /// /// Max value for a particular position's profit /// private decimal _maxValuePosition = 1; /// /// Min value for a particular position's profit /// private decimal _minValuePosition = -1; private decimal _currentProfit = 0; private Timer _refreshTimer = null; public ProfitContinuum() { InitializeComponent(); _refreshTimer = new Timer(); _refreshTimer.Interval = 1000; _refreshTimer.Tick += _refreshTimer_Tick; _refreshTimer.Enabled = true; _refreshTimer.Start(); Disposed += ProfitContinuum_Disposed; DoubleBuffered = true; } /// /// Filters the positions returned by the Positions property /// /// public void ApplyPositionsFilter(BindingList filteredPositions) { _filteredPositions = new BindingList(filteredPositions.ToList()); } /// /// Remove the filter applied to positions /// public void RemovePositionsFilter() { _filteredPositions = null; } private void SetDefaults() { _maxValueTotal = 1; _minValueTotal = -1; _maxValuePosition = 1; _minValuePosition = -1; _currentProfit = 0; } private void ProfitContinuum_Disposed(object sender, EventArgs e) { if (null != _refreshTimer) { _refreshTimer.Stop(); _refreshTimer = null; } } private void _refreshTimer_Tick(object sender, EventArgs e) { Invalidate(); } private readonly int MAIN_LINE_WIDTH = 5; private readonly int ZERO_LINE_WIDTH = 2; private readonly Color GAIN_COLOR = Color.Green; private readonly Color LOSS_COLOR = Color.Red; private readonly Color ZERO_COLOR = Color.Black; private readonly Color PROFIT_COLOR = Color.Black; private readonly float ZERO_LINE_OFFSET_RATIO = 0.25f; protected override void OnPaint(PaintEventArgs e) { if (DataReady()) UpdateMinMaxValues(); else if (Positions.Count == 0) _currentProfit = 0; base.OnPaint(e); DrawEndPoints(e.Graphics); DrawHorizontalLines(e.Graphics); DrawZeroLine(e.Graphics); _positionLocationsBottom.Clear(); _positionLocationsTop.Clear(); DrawPositions(e.Graphics); DrawCurrentProfit(e.Graphics); } private bool DataReady() { // Return true if one of the positions has yesterday close data. foreach (Position position in Positions.ToList()) { if (position.YesterdayClose > 0) return true; } return false; } private Dictionary _positionLocationsBottom = new Dictionary(); private Dictionary _positionLocationsTop = new Dictionary(); /// /// Draw a rectangle for each position. /// /// private void DrawPositions(Graphics g) { foreach (Position position in Positions.ToList()) { float rectangleCenter = GetDrawableWidth(g) * PercentWidth(position.TodayProfit, false) + GetLeftEdge(g); // draw rectangle underneath center line with profit number double currentProfit = (double)position.TodayProfit; string profitAsString = GetSymbolText(position); SizeF profitSize = g.MeasureString(profitAsString, Font); PointF topLeftProfitRectangle = new PointF(rectangleCenter - profitSize.Width / 2, Height / 2 + 5f); RectangleF profitRectangle = new RectangleF(topLeftProfitRectangle, profitSize); if (OverlapsWithDrawnPosition(profitRectangle)) { topLeftProfitRectangle = new PointF(topLeftProfitRectangle.X, Height / 2 - 5f - profitSize.Height); profitRectangle = new RectangleF(topLeftProfitRectangle, profitSize); _positionLocationsTop[position] = profitRectangle; } else _positionLocationsBottom[position] = profitRectangle; Color backgroundColor = GetBackgroundColor(position); Brush backgroundBrush = new SolidBrush(backgroundColor); g.FillRectangle(backgroundBrush, profitRectangle); //g.DrawRectangle(new Pen(Color.Black, 1), profitRectangle.X, profitRectangle.Y, profitRectangle.Width, profitRectangle.Height); Color foregroundColor = GetForegroundColor(position, backgroundColor); g.DrawString(profitAsString, Font, new SolidBrush(foregroundColor), topLeftProfitRectangle); } } private string GetSymbolText(Position position) { string text = position.Symbol; if (position.MostRecentShares > 0 && position.Shares != 0) text += "▲"; else if (position.MostRecentShares < 0 && position.Shares != 0) text += "▼"; return text; } private Color GetForegroundColor(Position position, Color backgroundColor) { if (position.TodayProfit > 0 && position.Shares != 0) return Color.Green; else if (position.TodayProfit < 0 && position.Shares != 0) return Color.Red; else return GradientInfo.AltColor(backgroundColor); } private Color GetBackgroundColor(Position position) { if (position.Shares == 0) return Color.SlateGray; else return Color.LightGray; } private bool OverlapsWithDrawnPosition(RectangleF rectangle) { bool overlaps = false; foreach (Position position in _positionLocationsBottom.Keys) { RectangleF positionRectangle = _positionLocationsBottom[position]; if (rectangle.X + rectangle.Width > positionRectangle.X && positionRectangle.X + positionRectangle.Width > rectangle.X) overlaps = true; } return overlaps; } /// /// Draw zero line on horizontal line /// /// private void DrawZeroLine(Graphics g) { float zeroWidthRatio = PercentWidth(0); g.DrawLine(new Pen(ZERO_COLOR, ZERO_LINE_WIDTH), GetDrawableWidth(g) * zeroWidthRatio + GetLeftEdge(g), Height * ZERO_LINE_OFFSET_RATIO, GetDrawableWidth(g) * zeroWidthRatio + GetLeftEdge(g), Height * (1 - ZERO_LINE_OFFSET_RATIO)); } /// /// Draw red and green horizontal lines making up main axis. /// /// private void DrawHorizontalLines(Graphics g) { string minValueAsString = "$" + ((double)_minValueTotal).ToFriendlyString(); string maxValueAsString = "$" + ((double)_maxValueTotal).ToFriendlyString(); SizeF minValueSize = g.MeasureString(minValueAsString, Font); SizeF maxValueSize = g.MeasureString(maxValueAsString, Font); float zeroWidthRatio = PercentWidth(0); g.DrawLine(new Pen(LOSS_COLOR, MAIN_LINE_WIDTH), GetLeftEdge(g), Height / 2, GetDrawableWidth(g) * zeroWidthRatio + GetLeftEdge(g), Height / 2); g.DrawLine(new Pen(GAIN_COLOR, MAIN_LINE_WIDTH), GetDrawableWidth(g) * zeroWidthRatio + GetLeftEdge(g), Height / 2, GetRightEdge(g), Height / 2); } /// /// Since Width uses the entire area and we have some margins, this function returns the drawable width for use in calculations. /// /// /// private float GetDrawableWidth(Graphics g) { return GetRightEdge(g) - GetLeftEdge(g); } /// /// Gets the location of the left edge of the drawable area /// /// /// private float GetLeftEdge(Graphics g) { string minValueAsString = "$" + ((double)_minValueTotal).ToFriendlyString(); SizeF minValueSize = g.MeasureString(minValueAsString, Font); return minValueSize.Width / 2; } /// /// Gets the location of the right edge of the drawable area /// /// /// private float GetRightEdge(Graphics g) { string maxValueAsString = "$" + ((double)_maxValueTotal).ToFriendlyString(); SizeF maxValueSize = g.MeasureString(maxValueAsString, Font); return Width - maxValueSize.Width / 2; } /// /// Draws the edge markers. /// /// private void DrawEndPoints(Graphics g) { string minValueAsString = "$" + ((double)_minValueTotal).ToFriendlyString(); string maxValueAsString = "$" + ((double)_maxValueTotal).ToFriendlyString(); SizeF minValueSize = g.MeasureString(minValueAsString, Font); SizeF maxValueSize = g.MeasureString(maxValueAsString, Font); // vertical x-axis maximum/minimum lines g.DrawLine(new Pen(LOSS_COLOR, MAIN_LINE_WIDTH), minValueSize.Width / 2, 0, minValueSize.Width / 2, Height); g.DrawLine(new Pen(GAIN_COLOR, MAIN_LINE_WIDTH), Width - maxValueSize.Width / 2, 0, Width - maxValueSize.Width / 2, Height); } private readonly float PROFIT_HEIGHT_RATIO = 0.1f; /// /// Draws the market for the current total profit level /// /// private void DrawCurrentProfit(Graphics g) { float triangleCenter = GetDrawableWidth(g) * PercentWidth(_currentProfit) + GetLeftEdge(g); PointF[] trianglePoints = { new PointF(triangleCenter, Height / 2 - 3f), new PointF(triangleCenter + 10F, Height * PROFIT_HEIGHT_RATIO), new PointF(triangleCenter - 10F, Height * PROFIT_HEIGHT_RATIO) }; //g.DrawPolygon(new Pen(PROFIT_COLOR, 1), trianglePoints); g.FillPolygon(new SolidBrush(PROFIT_COLOR), trianglePoints, System.Drawing.Drawing2D.FillMode.Winding); // draw rectangle underneath center line with profit number double currentProfit = (double)_currentProfit; string profitAsString = "$" + currentProfit.ToFriendlyString(); SizeF profitSize = g.MeasureString(profitAsString, Font); PointF topLeftProfitRectangle = new PointF(triangleCenter - profitSize.Width / 2, Height / 2 + 5f); RectangleF profitRectangle = new RectangleF(topLeftProfitRectangle, profitSize); _positionLocationsBottom.Add(new Position(), profitRectangle); g.FillRectangle(Brushes.LightGray, profitRectangle); g.DrawString(profitAsString, Font, Brushes.Black, topLeftProfitRectangle); } /// /// Gets the percent in the total span for a certain value passed as a parameter /// /// /// private float PercentWidth(decimal value, bool useTotal = true) { decimal distanceFromMin = value - _minValueTotal; decimal totalSpan = _maxValueTotal - _minValueTotal; if (!useTotal) { distanceFromMin = value - _minValuePosition; totalSpan = _maxValuePosition - _minValuePosition; } if (totalSpan > 0) return (float)distanceFromMin / (float)totalSpan; else return (float)0.5; } /// /// Update private variables with most recent settings in Positions. /// private void UpdateMinMaxValues() { decimal currentProfit = 0; decimal minProfit = 0; decimal maxProfit = 0; foreach (Position position in Positions.ToList()) { if (position.TodayProfit > maxProfit) maxProfit = position.TodayProfit; if (position.TodayProfit > _maxValueTotal) _maxValueTotal = position.TodayProfit; if (position.MaxProfit > _maxValuePosition) _maxValuePosition = position.MaxProfit; if (position.MinProfit < minProfit) minProfit = position.MinProfit; if (position.TodayProfit < _minValueTotal) _minValueTotal = position.TodayProfit; if (position.TodayProfit < _minValuePosition) _minValuePosition = position.TodayProfit; currentProfit += position.TodayProfit; } _currentProfit = currentProfit; if (_currentProfit > _maxValueTotal) _maxValueTotal = _currentProfit; if (_currentProfit < _minValueTotal) _minValueTotal = _currentProfit; } internal void Reset() { SetDefaults(); } } }