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