Example of Implementation |
Let us implement MaxGain technical indicator, which calculates gain of price in amount of percents during the period for each series and calculates max gain among the series.
We need only one single value – current price (it may be close, typical price or something else), so indicator type is BaseSimplePortfolioIndicator.
We want to store one value for each series so our indicator return value is double[], so we inherit our indicator as following:
1using System; 2using System.Collections.Generic; 3using FinAnalysis.Base; 4 5namespace FinAnalysis.Tests.IndicatorTests 6{ 7 class MaxGain : BaseSimplePortfolioIndicator<double> 8 { 9 } 10}
The next step is to implement appropriate constructors. We will pass the parameters to base class constructor AbsenceSkip to skip update until each series will have values, AggregateLast to pas only the last value to the indicator, ValidateAll to validate all input values. We will use SimpleDataQueue to store prices for each series, so we create our constructors:
1private SimpleDataQueue[] valuesQueue; 2private double maxGain; 3public MaxGain(int seriesCount, int valuesPerSerie) 4 : this(10, seriesCount, valuesPerSerie) 5{ 6} 7 8public MaxGain(TimeSpan period, int seriesCount, int valuesPerSerie) 9 : base(period, PortfolioAbsenceStrategy.AbsenceSkip, seriesCount, valuesPerSerie, 10 SimpleAggregationType.AggregateLast, InputValidationStrategy.ValidateAll) 11{ 12 EnsureDateTime(); 13 for (int i = 0; i < seriesCount; i++) 14 { 15 valuesQueue[i] = new SimpleDataQueue(period, true); 16 } 17 unstablePeriod = period; 18} 19 20public MaxGain(int period, int seriesCount, int valuesPerSerie) 21 : base(period, PortfolioAbsenceStrategy.AbsenceSkip, seriesCount, valuesPerSerie, 22 SimpleAggregationType.AggregateLast, InputValidationStrategy.ValidateAll) 23{ 24 for (int i = 0; i < seriesCount; i++) 25 { 26 valuesQueue[i] = new SimpleDataQueue(period, true); 27 } 28 unstableValuesCount = period; 29}
Period of instability indicator values equal to indicator long period. Do not forget to call EnsureDateTime() for time based constructor.
Going further we need to implement update method, all we need it’s supply data for our queues, and store gain values:
1protected override bool Update(double[] value, DateTime time, double[] result) 2{ 3 result = new double[value.Length]; 4 maxGain = 0; 5 for (int i = 0; i < value.Length; i++) 6 { 7 valuesQueue[i].Put(value[i], time); 8 result[i] = 100 * (valuesQueue[i].Last - valuesQueue[i].First) / valuesQueue[i].First; 9 if (maxGain < result[i]) maxGain = result[i]; 10 } 11 return true; 12}
And now we can specify output property for max gain:
1public Double MG { get { return maxGain; } }
And the last step (this step is necessary only for charting) is setting up Attributes.
Indicator name, short name, description and type before indicator definition:
1[IndicatorCategory(IndicatorCategory.BandIndicator)] 2[Indicator("MaxGain", "MaxGain", "MaxGain indicates max price gain on update period.")] 3public class MaxGain : BaseSimplePortfolioIndicator<double> 4….
Mark constructors and constructor’s parameters with appropriate attributes:
1[Constructor("Point Window")] 2public MaxGain([Parameter("Update Period", 10)]int period, [Parameter("Series Count")]int seriesCount, [Parameter("Values Per Series")]int valuesPerSerie) 3... 4 5[Constructor("Time Window")] 6public MaxGain([Parameter("Update Period", null)]TimeSpan period, [Parameter("Series Count")]int seriesCount, [Parameter("Values Per Series")]int valuesPerSerie) 7...
And specify attributes for indicator’s output values:
1[OutputProperty("Max Gain", ChartPlacement.PriceChart)] 2public Double MG { get { return maxGain; } }
That’s all. Our indicator is ready for usage. Compile it and use.
One more example of PortfolioIndicator usage:
1using System; 2using System.Collections.Generic; 3using System.Linq; 4using System.Text; 5using FinAnalysis.Ranging; 6 7namespace SimplePortfolioIndicatorSample 8{ 9 class SimplePortfolioIndicatorSample 10 { 11 const int Steps = 64; 12 const int Series = 8; 13 14 static void Main() 15 { 16 Random r = new Random(); 17 18 Ranker ranker = new Ranker(Series, 12, true); 19 20 for (int i = 0; i < Steps; ++i) 21 { 22 double []values = new double[Series]; 23 for(int j = 0; j < Series; ++j) 24 values[j] = r.NextDouble(); 25 26 if (ranker.Add(values)) 27 { 28 double[] ranks = ranker.Ranks; 29 30 string ranks_string = string.Empty; 31 for (int p = 0; p < ranks.Length; ++p) 32 { 33 if (p > 0) 34 ranks_string += ","; 35 36 ranks_string += ranks[p].ToString("0"); 37 } 38 39 Console.WriteLine("Ranks = {0}", ranks_string); 40 } 41 } 42 43 44 Ranker rankerTimeBased = new Ranker(Series, 12, true); 45 for (int i = 0; i < Steps; ++i) 46 { 47 double[] values = new double[Series]; 48 for (int j = 0; j < Series; ++j) 49 values[j] = r.NextDouble(); 50 51 if (rankerTimeBased.Add(values, DateTime.Now)) 52 { 53 double[] ranks = rankerTimeBased.Ranks; 54 55 string ranks_string = string.Empty; 56 for (int p = 0; p < ranks.Length; ++p) 57 { 58 if (p > 0) 59 ranks_string += ","; 60 61 ranks_string += ranks[p].ToString("0"); 62 } 63 64 Console.WriteLine("Ranks Time Based = {0}", ranks_string); 65 } 66 67 68 } 69 70 } 71 } 72}