diff --git a/Technical/AccountInfoDisplay.cs b/Technical/AccountInfoDisplay.cs index 2d702fd6..7ee86d2a 100644 --- a/Technical/AccountInfoDisplay.cs +++ b/Technical/AccountInfoDisplay.cs @@ -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; @@ -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; @@ -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), @@ -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 @@ -219,12 +220,11 @@ 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; @@ -232,22 +232,17 @@ protected override void OnRender(RenderContext context, DrawingLayouts layout) 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); @@ -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 @@ -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 BuildLines(Portfolio p) { - var sb = new StringBuilder(); + var lines = new List(); 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 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 @@ -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");