Note: this is for C# strategies, not for block strategies.
Below is code for a BarHistory Cache monitor. Its purpose is to allow you to use more calls to Series methods instead of new() when creating indicators in your strategy code. By using Series() you get the advantages of having indicators cached in the BarHistory cache for later re-use, but with the potential drawback of increased memory usage. Using Series can speed things up, especially during optimizations. However, a problem with Series is that you have no automatic way to clean-up the caches during or after optimization when their content is no longer (potentially) useful.
So, to use this class, include it your library (I'm assuming you knowledge of how to build your own library for use with Wealth-Lab). Its written for Wealth-Lab 8 and C# version 12.
To use the cache monitor, just place a call to BarHistoryCacheMonitor.Reference(...) at the end of your strategy's Initialize() method:
BarHistoryCacheMonitor.Reference(bars);
The full signature is:
How it works:
- There is a single instance of the BarHistoryCacheMonitor for a run of Wealth-Lab 8.
- When you call Reference, the cache manager retains a reference to the BarHistory and when that occurred. If another call is not made to Reference within a specified number of seconds then the BarHistory's cache is cleared when the monitor's maintenance method runs. Maintenance runs at configurable, specified intervals (e.g. 60 seconds).
- If the monitor clears any cache(s), it will also optionally run the system garbage collector.
For full details about Reference see the method in the code. Other pertinent methods include SetAllowGarbageCollection and SetTimerInterval. See the code for details.
Below is code for a BarHistory Cache monitor. Its purpose is to allow you to use more calls to Series methods instead of new() when creating indicators in your strategy code. By using Series() you get the advantages of having indicators cached in the BarHistory cache for later re-use, but with the potential drawback of increased memory usage. Using Series can speed things up, especially during optimizations. However, a problem with Series is that you have no automatic way to clean-up the caches during or after optimization when their content is no longer (potentially) useful.
So, to use this class, include it your library (I'm assuming you knowledge of how to build your own library for use with Wealth-Lab). Its written for Wealth-Lab 8 and C# version 12.
To use the cache monitor, just place a call to BarHistoryCacheMonitor.Reference(...) at the end of your strategy's Initialize() method:
BarHistoryCacheMonitor.Reference(bars);
The full signature is:
CODE:
public static void Reference(BarHistory bars, int expirationSeconds = DefaultExpirationSeconds, int maintenanceIntervalSeconds = DefaultMaintenanceIntervalSeconds)
How it works:
- There is a single instance of the BarHistoryCacheMonitor for a run of Wealth-Lab 8.
- When you call Reference, the cache manager retains a reference to the BarHistory and when that occurred. If another call is not made to Reference within a specified number of seconds then the BarHistory's cache is cleared when the monitor's maintenance method runs. Maintenance runs at configurable, specified intervals (e.g. 60 seconds).
- If the monitor clears any cache(s), it will also optionally run the system garbage collector.
For full details about Reference see the method in the code. Other pertinent methods include SetAllowGarbageCollection and SetTimerInterval. See the code for details.
CODE:
using System; using System.Collections.Concurrent; using System.Text; using System.Timers; using WealthLab.Core; using WealthLab.Indicators; using Timer = System.Timers.Timer; namespace WLUtility; /// <summary> /// Using a timer, monitor BarHistory caches to clear them when they are no longer being referenced. /// To use: Call BarHistoryCacheMonitor.Reference(...) to monitor a BarHistory's cache. /// You would typically call BarHistoryCacheMonitor.Reference(...) from the last line of the Initialize() method /// of a strategy. /// Please see the BarHistoryCacheMonitor.Reference(...) method, below, for more information. /// Note: There is a single instance of this class for the entire application. /// Note: written for C# version 12 /// </summary> public class BarHistoryCacheMonitor { /// <summary> /// The default interval in seconds to check for expired BarHistory caches. /// </summary> private const int DefaultMaintenanceIntervalSeconds = 60; /// <summary> /// The default maximum number of seconds to keep a BarHistory's cache after the BarHistory has not been referenced /// via the call to BarHistoryCacheMonitor.Reference(...). /// </summary> private const int DefaultExpirationSeconds = 30; /// <summary> /// Singleton instance of this class for the entire application. /// </summary> private static readonly BarHistoryCacheMonitor Instance; /// <summary> /// We want only one instance of this class for the entire application, hence /// the static constructor. /// </summary> static BarHistoryCacheMonitor() { Instance = new BarHistoryCacheMonitor(); } private BarHistoryCacheMonitor() { AllowGarbageCollection = true; BarHistoryTrackers = new ConcurrentDictionary<string, BarHistoryTracker>(); Timer = new Timer(DefaultMaintenanceIntervalSeconds * 1000.0); Timer.Elapsed += TimerElapsed; Timer.Start(); } private Timer Timer { get; } private bool InTimer { get; set; } private int MaintenanceIntervalSeconds { get; set; } private bool AllowGarbageCollection { get; set; } /// <summary> /// Tracks the BarHistories that are being referenced. /// </summary> private ConcurrentDictionary<string, BarHistoryTracker> BarHistoryTrackers { get; } /// <summary> /// Set whether to allow garbage collection after clearing stale cache(s). /// </summary> /// <param name="allowGarbageCollection">Set to true to allow garbage collection after cache maintenance.</param> public static void SetAllowGarbageCollection(bool allowGarbageCollection) { Instance.AllowGarbageCollection = allowGarbageCollection; } /// <summary> /// Call this method to add or update a BarHistory as being referenced. This method should be called /// from the last line of the Initialize() method of a strategy. By referencing a BarHistory, the BarHistory's /// cache will be maintained. If the BarHistory is not referenced (via a call to this method) for /// expiration number of seconds, the cache will be cleared. /// This method is thread-safe. /// </summary> /// <param name="bars">The bars being referenced</param> /// <param name="expirationSeconds"> /// The maximum number of seconds to keep the BarHistory's cached objects after the BarHistory /// has NOT been referenced via call to this method. /// If not referenced for this number of seconds, the BarHistory's cache will be cleared on the /// next run of the cache maintenance. /// </param> /// <param name="maintenanceIntervalSeconds"> /// How often the timer runs to maintain the caches. The timer's interval will only /// change if this value changes from the value that is currently in-use. /// It is not recommended to keep changing this value because there is a single instance of this class. /// </param> public static void Reference(BarHistory bars, int expirationSeconds = DefaultExpirationSeconds, int maintenanceIntervalSeconds = DefaultMaintenanceIntervalSeconds) { // add or update the BarHistory in the cache Instance.BarHistoryTrackers.AddOrUpdate(BarHistoryKey(bars), new BarHistoryTracker(bars, expirationSeconds), (_, barHistoryTracker) => { barHistoryTracker.LastReferenceTime = DateTime.Now; barHistoryTracker.ExpirationSeconds = expirationSeconds; return barHistoryTracker; }); if (Instance.MaintenanceIntervalSeconds == maintenanceIntervalSeconds) { return; } // the maintenance interval has changed, stop the timer, change the interval, and start the timer SetTimerInterval(maintenanceIntervalSeconds); } /// <summary> /// Sets the interval of the timer that checks for expired BarHistory caches. /// This is handy if you want to set the interval in one place, perhaps on WL startup /// in a static constructor. /// </summary> /// <param name="seconds">The number of seconds between invocation of cache maintenance.</param> public static void SetTimerInterval(int seconds = DefaultMaintenanceIntervalSeconds) { // don't waste time if the interval is already set to the same value if (seconds == Instance.MaintenanceIntervalSeconds) { return; } Instance.Timer.Stop(); Instance.Timer.Interval = seconds * 1000.0; Instance.MaintenanceIntervalSeconds = seconds; Instance.Timer.Start(); } /// <summary> /// When the timer elapses, we check for expired BarHistory caches. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void TimerElapsed(object sender, ElapsedEventArgs e) { // If we are already in the timer, don't do anything. // This is to prevent multiple timer events from running concurrently // in case the timer interval is too short or we made a timer interval change. if (InTimer) { return; } InTimer = true; var clearedSomething = false; var now = DateTime.Now; // search for BarHistories that have not been referenced for a while // and clear their cache foreach (var (key, tracker) in BarHistoryTrackers) { if (tracker.LastReferenceTime.AddSeconds(tracker.ExpirationSeconds) > now) { // some time left, not expired continue; } tracker.BarHistory.Cache.Clear(); BarHistoryTrackers.TryRemove(key, out _); clearedSomething = true; } // if we cleared something, we should collect garbage if (clearedSomething && AllowGarbageCollection) { GC.Collect(); } InTimer = false; } /// <summary> /// Identify the BarHistory by its symbol, scale, count, start date, and end date. /// Ideally this should be unique for each BarHistory. /// Ideally, the BarHistory would have a GUID identity property, but it doesn't. /// </summary> /// <param name="bars"></param> /// <returns></returns> private static string BarHistoryKey(BarHistory bars) => $"{bars.Symbol}:{(int) bars.Scale.Frequency}:{bars.Scale.Interval}:{bars.Count}:{bars.StartDate.Ticks}:{bars.EndDate.Ticks}"; /// <summary> /// Get a terse (default) or verbose summary of the BarHistory caches that are being monitored. /// </summary> /// <param name="verbose"> /// Set to true for detailed summary of the caches. Set to false for brief summary of cache /// information. The default is false. /// </param> /// <returns>A string containing the cache statistics.</returns> public static string GetStats(bool verbose = false) { var count = 0; foreach (var item in Instance.BarHistoryTrackers.Values) { count += item.BarHistory.Cache.Count; } var general = $"Bar History Cache Monitor as of {DateTime.Now}..." + Environment.NewLine + $"\tCache Monitor timer interval: {Instance.MaintenanceIntervalSeconds} seconds, Allow garbage collection: {Instance.AllowGarbageCollection}" + Environment.NewLine + $"\tMonitored BarHistory count: {Instance.BarHistoryTrackers.Count}, Total cached items: {count}."; if (!verbose) { return general; } var sb = new StringBuilder(100000); sb.AppendLine(general); foreach (var (key, barHistoryTracker) in Instance.BarHistoryTrackers) { var bars = barHistoryTracker.BarHistory; var barIdentity = $"\t\tSymbol {bars.Symbol}: Frequency: {bars.Scale.Frequency}, Interval: {bars.Scale.Interval}, Bar count: {bars.Count}, Start date: {bars.StartDate}, End date: {bars.EndDate}"; var cacheBasics = $"\t\t\tCached items count: {bars.Cache.Count}, Last reference time: {barHistoryTracker.LastReferenceTime}, Expiration: {barHistoryTracker.ExpirationSeconds} seconds."; sb.AppendLine(barIdentity); sb.AppendLine(cacheBasics); foreach (var (s, value) in bars.Cache) { var oType = value.GetType(); if (oType.IsAssignableTo(typeof(IndicatorBase))) { var indicator = (IndicatorBase) value; sb.AppendLine( $"\t\t\t\tCache Key: {s}, Type: {oType.Name}, Name: {indicator.Name}"); if (indicator.Parameters.Count > 0) { sb.AppendLine("\t\t\t\t\tParameters:"); foreach (var indicatorParameter in indicator.Parameters) { sb.AppendLine( $"\t\t\t\t\t\t{indicatorParameter.Name}: {indicatorParameter.Value}"); } } } else { sb.AppendLine($"\t\t\t\tCache Key: {s}, Type: {oType.Name}"); } } } return sb.ToString(); } /// <summary> /// Small helper class to track a BarHistory and its last reference time. /// </summary> /// <param name="bars">The BarHistory being tracked so we can clear its cache when appropriate</param> /// <param name="expirationSeconds"> /// Number of seconds of no reference to the BarHistory that must occur before /// clearing its cache. /// </param> private class BarHistoryTracker(BarHistory bars, int expirationSeconds) { public DateTime LastReferenceTime { get; set; } = DateTime.Now; public BarHistory BarHistory { get; } = bars; public int ExpirationSeconds { get; set; } = expirationSeconds; } }
Rename
Currently there are no replies yet. Please check back later.
Your Response
Post
Edit Post
Login is required