- ago
In another post, user DenisPolezhaka suggested some enhancements for method UserStrategyBase.DrawHint. So, I took a shot at implementing some of the ideas DenisPolezhaka suggested. Below is a class named HintAggregator that allows you to aggregate the hint and body text of multiple hints for a bar into one hint for the bar. The aggregation can be useful if you have multiple hints for a bar spread throughout your strategy logic, and you want just one hint, a combination of the multiple hints, to be shown for the bar.

The code assumes you'll place the HintAggregator class into a library. It is assumed the reader is familiar with building and deploying their own library for use with strategies. The code requires C# 12.

Please see the details of the HintAggregator.AddHint and HintAggregator.DrawHints method for various ways you can affect the final hint output.

Also, after the HintAggregator class is a small strategy that you can use to try it out.

HintAggregator class...

CODE:
using System.Collections.Generic; using System.Linq; using WealthLab.Backtest; using WealthLab.Core; namespace WLUtility { /// <summary> /// HintAggregator is a class that aggregates hints per bar index and draws them all at once /// using a single hint. /// To use: /// ---------------- /// In your strategy, create a new instance of HintAggregator in the Initialize method. /// Store the instance in a field or property of your strategy class. /// Call AddHint for each hint you want to add (see method description for details). /// Call DrawHints at the end of your strategy's Cleanup method. /// Note: this code requires use of C# 12 or later. /// </summary> public class HintAggregator { /// <summary> /// Constructor for HintAggregator /// </summary> /// <param name="myStrategy">The strategy class instance that will provide the hints</param> public HintAggregator(UserStrategyBase myStrategy) { MyStrategy = myStrategy; BarHints = new Dictionary<int, List<HintItem>>(5); } private UserStrategyBase MyStrategy { get; } /// <summary> /// Stores the hints for each bar for which hints have been added /// </summary> private Dictionary<int, List<HintItem>> BarHints { get; } /// <summary> /// Add a hint for the given bar index to the aggregator /// </summary> /// <param name="hintText">See UserStrategyBase.DrawHint description for details</param> /// <param name="bodyText">See UserStrategyBase.DrawHint description for details</param> /// <param name="idx">Hint will be drawn at this bar index</param> /// <param name="aboveBar"> /// See UserStrategyBase.DrawHint description for details. The last aboveBar in a set of hints for a /// particular bar index will be used. /// </param> /// <param name="hintTextColor"> /// See UserStrategyBase.DrawHint description for details. The last non-null hintTextColor in a /// set of hints for the bar will be used. /// </param> /// <param name="hintBkgColor"> /// See UserStrategyBase.DrawHint description for details. The last non-null hintBkgColor in a /// set of hints for the bar will be used. /// </param> /// <param name="bodyTextColor"> /// See UserStrategyBase.DrawHint description for details. The last non-null bodyTextColor in a /// set of hints for the bar will be used. /// </param> /// <param name="isSoleHintText"> /// Set to true if you want the given hintText to be the only hintText shown for the hint /// block. If there is no sole hintText for the bar index then all hints hintText are combined and each /// separated by a newline. If multiple sole hintText occur then the last is applicable (see code). /// </param> /// <param name="prependHintTextOntoBodyText"> /// Set to true if you want the bodyText to be prepended by the hintText plus a /// colon and space. Set to false if you want only the bodyText. /// </param> public void AddHint(string hintText, string bodyText, int idx, bool aboveBar, WLColor hintTextColor = null, WLColor hintBkgColor = null, WLColor bodyTextColor = null, bool isSoleHintText = false, bool prependHintTextOntoBodyText = false) { // if not interactive then don't waste time (e.g. optimization) if (MyStrategy.ExecutionMode is StrategyExecutionMode.Optimization or StrategyExecutionMode.StrategyMonitor or StrategyExecutionMode.Evolver or StrategyExecutionMode.Rankings) { return; } if (!BarHints.TryGetValue(idx, out var hintItems)) { hintItems = []; BarHints[idx] = hintItems; } hintItems.Add(new HintItem(hintText, bodyText, aboveBar, hintTextColor, hintBkgColor, bodyTextColor, isSoleHintText, prependHintTextOntoBodyText)); } /// <summary> /// Clear all hints that have been added to the aggregator. /// </summary> public void ClearAllHints() { BarHints.Clear(); } /// <summary> /// Clear hints for the given bar index. /// </summary> /// <param name="idx"></param> public void ClearHints(int idx) { BarHints.Remove(idx); } /// <summary> /// Draw all hints that have been added to the aggregator. /// Call this method at the end of your strategy's Cleanup method. /// </summary> public void DrawHints() { // if not interactive then don't waste time (e.g. optimization) if (MyStrategy.ExecutionMode is StrategyExecutionMode.Optimization or StrategyExecutionMode.StrategyMonitor or StrategyExecutionMode.Evolver or StrategyExecutionMode.Rankings) { return; } foreach (var (idx, hintItems) in BarHints) { WLColor hintTextColor = null; WLColor hintBkgColor = null; WLColor bodyTextColor = null; var soleHint = hintItems.LastOrDefault(h => h.IsSoleHintText); var hintText = soleHint is not null ? soleHint.HintText : ""; var body = ""; var aboveBar = false; foreach (var hintItem in hintItems) { if (soleHint == null) { hintText += hintItem.HintText + "\n"; } if (!string.IsNullOrEmpty(hintItem.BodyText)) { if (hintItem.PrependHintTextOntoBodyText) { body += hintItem.HintText + ": "; } body += $"{hintItem.BodyText}\n"; } // last aboveBar wins aboveBar = hintItem.AboveBar; // last non-null wins hintTextColor = hintItem.HintTextColor ?? hintTextColor; hintBkgColor = hintItem.HintBkgColor ?? hintBkgColor; bodyTextColor = hintItem.BodyTextColor ?? bodyTextColor; } MyStrategy.DrawHint(hintText, body, idx, aboveBar, hintTextColor, hintBkgColor, bodyTextColor); } } private record HintItem( string HintText, string BodyText, bool AboveBar, WLColor HintTextColor, WLColor HintBkgColor, WLColor BodyTextColor, bool IsSoleHintText, bool PrependHintTextOntoBodyText); } }


Here is the test strategy class to let you try the HintAggregator class...

CODE:
using System.Globalization; using WealthLab.Backtest; using WealthLab.Core; using WLUtility; namespace WealthLabStrategies.Test { /// <summary> /// Simple class to try out the HintAggregator class. /// </summary> public class TestHintAggregator : UserStrategyBase { private HintAggregator MyHintAggregator { get; set; } public override void Initialize(BarHistory bars) { MyHintAggregator = new HintAggregator(this); } public override void Execute(BarHistory bars, int idx) { // Add a hint every 50 bars if (idx % 50 == 0) { MyHintAggregator.AddHint($"Bar {idx}", $"I am bar {idx} for symbol {bars.Symbol}", idx, false, WLColor.Aqua, WLColor.Black, isSoleHintText: true, prependHintTextOntoBodyText: true); MyHintAggregator.AddHint("Date", bars.DateTimes[idx].ToString(CultureInfo.InvariantCulture), idx, false, prependHintTextOntoBodyText: true); MyHintAggregator.AddHint("Close", bars.Close[idx].ToString("N2"), idx, false, prependHintTextOntoBodyText: true); } // Add a hint every 100 bars if (idx % 100 == 0) { MyHintAggregator.AddHint("Every 100", "I occur only every 100 bars", idx, false, prependHintTextOntoBodyText: true); } } public override void Cleanup(BarHistory bars) { MyHintAggregator.DrawHints(); } } }
1
114
0 Replies

Reply

Bookmark

Sort
Currently there are no replies yet. Please check back later.