Hello everyone,
I've encountered an issue while trying to dynamically change the color of an custom indicator. The code snippet related to color change looks something like this:
Despite this implementation, the indicator's color does not change as expected. What mistake am I making? It's not possible to change the color this way, correct? :(
I've encountered an issue while trying to dynamically change the color of an custom indicator. The code snippet related to color change looks something like this:
CODE:
public override void Populate() { // Other initializations var marketGroup = Enum.Parse<MarketGroups>(Parameters[1].AsString); var timeZone = Parameters[2].AsInt; // Loop through data points for (int n = 0; n < source.Count; n++) { // Calculations and value assignments } Color = MarketColor(marketGroup); // Attempt to change color based on the market group }
Despite this implementation, the indicator's color does not change as expected. What mistake am I making? It's not possible to change the color this way, correct? :(
Rename
I just tried this with one of my custom indicators, and it works fine.
For example, in the code, I have:
And at the end of the Populate method...
When I use the indicator in a strategy, then the indicator color is Yellow as expected.
My suggestion is to check your MarketColor method to be sure it is returning the color you expect.
For example, in the code, I have:
CODE:
public override WLColor DefaultColor => WLColor.Gray;
And at the end of the Populate method...
CODE:
Color = WLColor.Yellow;
When I use the indicator in a strategy, then the indicator color is Yellow as expected.
My suggestion is to check your MarketColor method to be sure it is returning the color you expect.
I'm a little confused by this code below. It's returning the wrong datatype for WL8.
CODE:I think you want it to return WLColor and not Color. The latter is for .NET, not WL8.
Color = MarketColor(marketGroup); // Attempt to change color based on the market group
I'm not sure where your confusion arises. Color is a property of IndicatorBase. The original poster (OP) is making an assignment to property Color from the result of calling method MarketColor(...). MarketColor is not a member of IndicatorBase nor its parent chain. I can't find it as part of any WealthLab DLL, so I presume it is a method created by the OP.
No, unfortunately the color still doesn't change. :(
CODE:
using WealthLab.Core; namespace WealthLab.Indicators { public enum MarketGroups { America_USD_CAD, Europe_EUR_GBP_CHF, Asia_AUD_JPY_NZD } public class MarketHours : IndicatorBase { //parameterless constructor public MarketHours() : base() { } //for code based construction public MarketHours( TimeSeries source, MarketGroups marketGroup, int timeZone ) : base() { Parameters[0].Value = source; Parameters[1].Value = marketGroup; Parameters[2].Value = timeZone; Populate(); } private WLColor MarketColor(MarketGroups marketGroup) { switch (marketGroup) { case MarketGroups.America_USD_CAD: return WLColor.Red; case MarketGroups.Europe_EUR_GBP_CHF: return WLColor.Green; case MarketGroups.Asia_AUD_JPY_NZD: return WLColor.Blue; default: return WLColor.Gray; } } //static method public static MarketHours Series( TimeSeries source, MarketGroups marketGroup, int timeZone ) { string key = CacheKey("MH" + marketGroup.ToString(), marketGroup); if (source.Cache.ContainsKey(key)) return (MarketHours)source.Cache[key]; var mh = new MarketHours(source, marketGroup, timeZone); source.Cache[key] = mh; return mh; } //name public override string Name => "MarketHours"; //abbreviation public override string Abbreviation => "MH"; //description public override string HelpDescription => "Draws a normal curve representing the opening of a market."; //price pane public override string PaneTag => "MarketHours"; //it's a smoother public override bool IsSmoother => false; //public override WLColor DefaultColor => WLColor.White; //populate public override void Populate() { TimeSeries source = Parameters[0].AsTimeSeries; var marketGroup = Enum.Parse<MarketGroups>(Parameters[1].AsString); var timeZone = Parameters[2].AsInt; DateTimes = source.DateTimes; for (int n = 0; n < source.Count; n++) Values[n] = Value(n, DateTimes, timeZone, marketGroup); Color = MarketColor(marketGroup); } //calculate an ad-hoc SMA at a specific point public static double Value( int idx, List<DateTime> dateTimes, int timeZone, MarketGroups marketGroup ) { // Transform market opening times into a normal curve // The value is zero from market closing time to opening time // The value is 1 at the median market hour Math.Round((opening hour + closing hour) / 2) // At opening and closing times it's the y for 2 standard deviations (+/-2σ) DateTimeOffset openingTime, closingTime; switch (marketGroup) { case MarketGroups.America_USD_CAD: openingTime = new DateTimeOffset(2000, 1, 1, 12, 0, 0, 0, TimeSpan.Zero); closingTime = new DateTimeOffset(2000, 1, 1, 21, 0, 0, 0, TimeSpan.Zero); break; case MarketGroups.Europe_EUR_GBP_CHF: openingTime = new DateTimeOffset(2000, 1, 1, 8, 0, 0, 0, TimeSpan.Zero); closingTime = new DateTimeOffset(2000, 1, 1, 17, 0, 0, 0, TimeSpan.Zero); break; case MarketGroups.Asia_AUD_JPY_NZD: openingTime = new DateTimeOffset(2000, 1, 1, 22, 0, 0, 0, TimeSpan.Zero); closingTime = new DateTimeOffset(2000, 1, 2, 9, 0, 0, 0, TimeSpan.Zero); break; default: throw new Exception("Unrecognized market group."); } double duration = (closingTime - openingTime).TotalHours; double standardDeviation = duration / 4; double mean = duration / 2; DateTimeOffset currentTime = new DateTimeOffset( 2000, 1, 1, dateTimes[idx].Hour, dateTimes[idx].Minute, 0, 0, new TimeSpan(timeZone, 0, 0) ).ToUniversalTime(); bool isOpen = currentTime >= openingTime && currentTime <= closingTime; if (!isOpen) { return 0; } double x = (currentTime - openingTime).TotalHours; var y = CalculateProbabilityDensity(x, mean, standardDeviation); return y; } public static double CalculateProbabilityDensity(double x, double mean, double standardDeviation) { double sqrtTwoPi = Math.Sqrt(2 * Math.PI); double diff = x - mean; double expPart = Math.Exp(-(diff * diff / (2 * standardDeviation * standardDeviation))); return 1 / (standardDeviation * sqrtTwoPi) * expPart; } //calculate partial value public override bool CalculatePartialValue() { StreamingValue = double.NaN; TimeSeries source = Parameters[0].AsTimeSeries; if (double.IsNaN(source.StreamingValue)) return false; int period = Parameters[1].AsInt; if (period >= source.Count) return false; double sum = 0; for (int n = 0; n < period - 1; n++) { var i = source.Count - 1 - n; sum += source[i]; } sum += source.StreamingValue; StreamingValue = sum / period; return true; } //generate parameters protected override void GenerateParameters() { AddParameter("Source", ParameterType.TimeSeries, PriceComponent.Close); AddParameter( "Market Group", ParameterType.StringChoice, Enum.GetNames(typeof(MarketGroups))[0] ).Choices = Enum.GetNames(typeof(MarketGroups)).ToList(); AddParameter("Time Zone", ParameterType.Int32, 1); } } }
As you've seen, assigning to Color in the IndicatorBase itself has no effect, it's intended to be able to be changed in other places, like in Strategy code or by the WL8 chart, etc.
Your Response
Post
Edit Post
Login is required