Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
132 changes: 52 additions & 80 deletions Technical/AccountInfoDisplay.cs
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
namespace ATAS.Indicators.Technical;

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Drawing;
using System.Text;

using ATAS.DataFeedsCore;

Expand All @@ -29,12 +29,9 @@ public class AccountInfoDisplay : Indicator
private Color _positiveColor = Color.FromArgb(0, 230, 118);
private Color _negativeColor = Color.FromArgb(255, 82, 82);
private Color _neutralColor = Color.FromArgb(150, 150, 150);
private RenderPen _borderPen = new(Color.Gray, 1);
private RenderFont _font = new("Arial", 11);
private RenderStringFormat _stringFormat = new()
{
LineAlignment = StringAlignment.Near,
Alignment = StringAlignment.Near
};
private const int Padding = 10;

private Portfolio _currentPortfolio;

Expand Down Expand Up @@ -88,7 +85,11 @@ public CrossColor NeutralColor
public float FontSize
{
get => _font.Size;
set => _font = new RenderFont("Arial", value);
set
{
if (Math.Abs(_font.Size - value) < 0.01f) return;
_font = new RenderFont("Arial", value);
}
}

[Display(ResourceType = typeof(Strings), Name = nameof(Strings.ShowAccountId),
Expand Down Expand Up @@ -210,7 +211,7 @@ protected override void OnCalculate(int bar, decimal value)

protected override void OnRender(RenderContext context, DrawingLayouts layout)
{
if (ChartInfo == null || Container?.Region == null)
if (ChartInfo == null || Container == null)
return;

// Get current portfolio
Expand All @@ -219,35 +220,29 @@ protected override void OnRender(RenderContext context, DrawingLayouts layout)
return;

// Build display text
var text = BuildDisplayText(portfolio);
if (string.IsNullOrEmpty(text))
var lines = BuildLines(portfolio);
if (lines.Count == 0)
return;

// Calculate proper dimensions for table layout
var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var lineHeight = context.MeasureString("A", _font).Height;

var maxLabelWidth = 0;
var maxValueWidth = 0;

foreach (var line in lines)
{
var parts = line.Split('|');
if (parts.Length == 2)
{
var labelWidth = (int)context.MeasureString(parts[0], _font).Width;
var valueWidth = (int)context.MeasureString(parts[1], _font).Width;

if (labelWidth > maxLabelWidth)
maxLabelWidth = labelWidth;
if (valueWidth > maxValueWidth)
maxValueWidth = valueWidth;
}
var labelWidth = (int)context.MeasureString(line.Label, _font).Width;
var valueWidth = (int)context.MeasureString(line.Value, _font).Width;

if (labelWidth > maxLabelWidth)
maxLabelWidth = labelWidth;
if (valueWidth > maxValueWidth)
maxValueWidth = valueWidth;
}

var padding = 10;
var rectWidth = maxLabelWidth + ColumnSpacing + maxValueWidth + padding * 2;
var rectHeight = lines.Length * lineHeight + padding * 2;
var rectWidth = maxLabelWidth + ColumnSpacing + maxValueWidth + Padding * 2;
var rectHeight = lines.Count * lineHeight + Padding * 2;

// Calculate position
var x = CalculateXPosition(rectWidth);
Expand All @@ -258,11 +253,11 @@ protected override void OnRender(RenderContext context, DrawingLayouts layout)
context.FillRectangle(_backgroundColor, rectangle);

// Draw border
context.DrawRectangle(new RenderPen(Color.Gray, 1), rectangle);
context.DrawRectangle(_borderPen, rectangle);

// Draw text
var textRect = new Rectangle(x + padding, y + padding, rectWidth - padding * 2, rectHeight - padding * 2);
DrawColoredText(context, text, textRect, portfolio, maxLabelWidth);
var textRect = new Rectangle(x + Padding, y + Padding, rectWidth - Padding * 2, rectHeight - Padding * 2);
DrawColoredText(context, lines, textRect, maxLabelWidth);
}

#endregion
Expand All @@ -275,43 +270,53 @@ private void OnPortfolioSelected(Portfolio portfolio)
RedrawChart();
}

private string BuildDisplayText(Portfolio portfolio)
private sealed record DisplayLine(string Label, string Value, decimal? RawForColoring);

private List<DisplayLine> BuildLines(Portfolio p)
{
var sb = new StringBuilder();
var lines = new List<DisplayLine>();

if (ShowAccountId)
sb.AppendLine($"Account|{portfolio.AccountID}");
lines.Add(new("Account", p.AccountID, null));

if (ShowCurrency && portfolio.Currency.HasValue)
sb.AppendLine($"Currency|{portfolio.Currency}");
if (ShowCurrency && p.Currency.HasValue)
lines.Add(new("Currency", p.Currency.Value.ToString(), null));

if (ShowBalance)
sb.AppendLine($"Balance|{FormatCurrency(portfolio.Balance)}");
lines.Add(new("Balance", FormatCurrency(p.Balance), null));

if (ShowAvailableBalance && portfolio.BalanceAvailable.HasValue)
sb.AppendLine($"Available|{FormatCurrency(portfolio.BalanceAvailable.Value)}");
if (ShowAvailableBalance && p.BalanceAvailable.HasValue)
lines.Add(new("Available", FormatCurrency(p.BalanceAvailable.Value), null));

if (ShowMargin)
sb.AppendLine($"Blocked Margin|{FormatCurrency(portfolio.BlockedMargin)}");
lines.Add(new("Blocked Margin", FormatCurrency(p.BlockedMargin), null));

if (ShowLeverage && portfolio.Leverage != 1)
sb.AppendLine($"Leverage|{portfolio.Leverage:F2}x");
if (ShowLeverage && p.Leverage != 1)
lines.Add(new("Leverage", $"{p.Leverage:F2}x", null));

if (ShowOpenPnL)
sb.AppendLine($"Open PnL|{FormatCurrency(portfolio.OpenPnL)}");
lines.Add(new("Open PnL", FormatCurrency(p.OpenPnL), p.OpenPnL));

if (ShowClosedPnL)
sb.AppendLine($"Closed PnL|{FormatCurrency(portfolio.ClosedPnL)}");
lines.Add(new("Closed PnL", FormatCurrency(p.ClosedPnL), p.ClosedPnL));

if (ShowTotalPnL)
sb.AppendLine($"Total PnL|{FormatCurrency(portfolio.ClosedPnL + portfolio.OpenPnL)}");
// Session-level total: OpenPnL + ClosedPnL (current session) - NOT
// Portfolio.TotalPnL, which uses TotalClosedPnL (cumulative across sessions).
lines.Add(new("Total PnL", FormatCurrency(p.ClosedPnL + p.OpenPnL), p.ClosedPnL + p.OpenPnL));

return sb.ToString().TrimEnd();
return lines;
}

private void DrawColoredText(RenderContext context, string text, Rectangle textRect, Portfolio portfolio, int maxLabelWidth)
private System.Drawing.Color ColorFor(decimal? raw)
=> raw is null ? _textColor
: raw > 0m ? _positiveColor
: raw < 0m ? _negativeColor
: _neutralColor;

private void DrawColoredText(RenderContext context, List<DisplayLine> lines,
Rectangle textRect, int maxLabelWidth)
{
var lines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
var lineHeight = context.MeasureString("A", _font).Height;

// Calculate value column position
Expand All @@ -321,45 +326,12 @@ private void DrawColoredText(RenderContext context, string text, Rectangle textR
// Draw lines
foreach (var line in lines)
{
var parts = line.Split('|');
if (parts.Length == 2)
{
var label = parts[0];
var valueStr = parts[1];

// Draw label
context.DrawString(label, _font, _textColor, textRect.X, currentY);

// Determine color for value
var valueColor = _textColor;
if (line.Contains("PnL"))
{
var value = ExtractNumericValue(valueStr);
valueColor = value > 0 ? _positiveColor : (value < 0 ? _negativeColor : _neutralColor);
}

// Draw value
context.DrawString(valueStr, _font, valueColor, valueColumnX, currentY);
}
else
{
// Fallback for malformed lines
context.DrawString(line, _font, _textColor, textRect.X, currentY);
}

context.DrawString(line.Label, _font, _textColor, textRect.X, currentY);
context.DrawString(line.Value, _font, ColorFor(line.RawForColoring), valueColumnX, currentY);
currentY += lineHeight;
}
}

private decimal ExtractNumericValue(string valueStr)
{
// Remove currency symbols and try to parse
var cleanStr = valueStr.Replace(",", "").Trim();
if (decimal.TryParse(cleanStr, out var result))
return result;
return 0;
}

private string FormatCurrency(decimal value)
{
return value.ToString("N2");
Expand Down