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...
Here is the test strategy class to let you try the HintAggregator class...
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(); } } }
Rename
Currently there are no replies yet. Please check back later.
Your Response
Post
Edit Post
Login is required