Click or drag to resize

Simple Data Queue

As a queue-based and resource effective dynamic data container of double numbers, it is intended mostly to use inside the Financial Analysis library but can also conveniently cover several user’s needs such as storing history data, calculation of descriptive statistics and manipulations with data points. The data container also provides a mechanism to add custom listeners which runs automatically user-specific operations when the content is modified.

Implementation and Usage

An implementation of this data container is the SimpleDataQueue class; to access it in any code project, the FinAnalisys.dll must be referenced and the following using statement should precede the code:

C#
1using FinAnalysis.Base;

 To enable custom listeners, the corresponding package should also be referenced:

C#
1using FinAnalysis.Listeners;

Inner data presentation in the class is a two-side managed queue with elements (double numbers) positioned and accessible by indexes. The elements are generally ordered in such a way that the last added value has the minimal index 0 and the very early added element has a maximal index. The elements of the queue can be time-assigned (stored with their associated time stamps) and also accessible by time in this case.

Using Modes and Common Properties

The class implements multimode logic which is not always as simple as the class name suggests.

As a rule, each mode implies using of compatible methods to modify content so that an incompatible call causes an Exception. Some operations can force the instance of the class to change its current mode for flexibility reason.

To assure correct using, the class provides several mode-related properties that inform the careful user about instance current state:

Property

Type

Description

Type

QueueType

Indicates the actual data queue regimen, one of:

  • QueueType.ManualControl
  • QueueType.AutoDynamic
  • QueueType.AutoStatic
  • QueueType.SingleElement

NeedDateTime

bool

Indicates whether Time Assignment is applied; true means that data points are stored with time stamps associated and user has to use only time-assigned modification when working with a given class instance

Count

int

Returns the number of elements actually stored

Ready

bool

This flag is true if and only if the queue contains something (Count > 0)

Initialized

bool

This flag takes true when the very first data point is added to the empty queue and keeps this value even if Count becomes zero; the true value prohibits changing of the Period and Capacity properties when working with a given instance

Period

TimeSpan

Returns time interval (from the newest data point) for which the queue will keep elements.

Applicable only for the AutoDynamic queue regimen, otherwise returns null.

Allows assigning values if Initialized=false and turns queue regimen so that

Type = QueueType.AutoDynamic

LastDateTime

DateTime

Returns the newest data stamp of the elements stored in the queue; if Time Assignment is not applied (NeedDateTime = false) then an Exception is thrown; if no elements stored (Count = 0) then DateTime.MinValue is returned

FirstDateTime

DateTime

Returns the oldest data stamp of the elements stored in the queue; if Time Assignment is not applied (NeedDateTime=false) then an Exception is thrown; if no elements stored (Count = 0) then DateTime.MinValue is returned

Capacity

int

Returns the number of positions reserved in the queue for data.

Allows assigning values if Initialized = false and changes queue regimen from QueueType.AutoStatic to QueueType.SingleElement if the required value is 1 (and vice versa if the value is more than 1).

A current instance mode is characterized by combination of the three features:

  1. Regimen of data queue,
  2. Time Assignment (whether elements are stored with time stamps or not),
  3. A need to compute statistics.

The first feature, Regimen of the data queue, is the main characteristic which mostly affects class logic and usage. Possible regimens are specified by the members of the QueueType enumeration:

  • ManualControl – the data queue realizes user-specific (by default expanding) data set and adopts it’s capacity to store as many elements as it is currently needed; the user is given with ability to add and remove data points at both sides of the queue and no data points are dropped until removed by a suitable method call;
  • AutoDynamic – the data queue realizes sliding data set defined by a time period; adding of new data points forces out and drops the oldest ones if they become strongly out of the time period.  Actual queue capacity (the number of data points reserved) is adapted automatically and depends on data time stamps.
Note Note

This regimen directly defines Time Assignment to be applied and requires using of the compatible modification method (see detailed below).

  • AutoStatic – the data queue realizes sliding data set of a fixed capacity (maximal number of data points) so that adding of a new element forces out and drops the oldest one if the added data point exceeds the capacity.
  • SingleElement – the one-element version of the AutoStatic regimen which is default.

The actual data queue regimen is indicated by the class property Type

Time Assignment feature

The table below shows how the Time Assignment feature corresponds to data queue regimens, where “+” means ability to operate with assigned or non-assigned elements:

Regimen

Time Assignment: Yes

Time Assignment: No

ManualControl

+

+

AutoDynamic

+

-

AutoStatic

+

+

SingleElement

+

+

A very important issue about Time Assignment (if switched on) is time check while adding of new elements which provides time-ordered elements in the data queue. The class instance detects and keeps the latest and the oldest time stamps of the elements and uses these values to apply the following rules:

  • The time stamp of a newly added element must be equal or newer than the latest one in the queue, otherwise the new data point is ignored (not added).
  • When adding an element to the end of the queue (what is possible only with the ManualControl regimen), it’s time stamp must be equal or older than the oldest one in the queue, otherwise adding is rejected.

Every successful adding modifies either the oldest or the latest time stamp of the queue.

Also, time check detects the DateTime.MinValue time stamp and considers it as meaningless. Depending on Initialized status, such a time stamp can lead either to Exception (if true) or to turning Time Assignment OFF (when false).

Class properties associated with the Time Assignment feature are (see the common properties above for details):

  • NeedDateTime,
  • LastDateTime,
  • FirstDateTime.

Time Assignment is switched OFF by default.

Computing of statistics is applicable to any of the queue regimens independently of Time Assignment status. By default statistics are not provided until required via special input parameter while initialization. Statistics with the SingleElement queue regimen are practically useless but also supported for compatibility reason.

Initialization and compatible content modification

The class provides constructors that are generally focused on specifying of the queue regimen and statistical service status. 

With that, one of the queue regimens strongly predetermines Time Assignment status: as the table above shows, the AutoDynamic regimen implies only time-assigned elements.

All other regimens provide the user with ability to turn Time Assignment ON or OFF by using of a suitable method to add the very first data point to the (empty) data queue.

Manual Control of the data queue requires initialization either with the default constructor or its extended version with the input “statistic” flag:

SimpleDataQueue - initializes a new instance of the class that contains an empty data queue of initial capacity for 10 elements and no statistics provided.

SimpleDataQueue(Boolean) - acts like the default constructor but with statistic calculations (if the input flag is true).

Note Note

With the ManualControl regimen, the class adopts data queue capacity automatically what is resource consuming operation. For performance reason, it is a good practice to possibly estimate the maximal number of stored elements and, before adding of data points, define the required capacity with just one line of code like this:

C#
1manualInstance.Capacity = 1000;

An attempt to assign a value less than one or if Initialized=true causes an Exception.

Compatible methods to add data points to the manually controlled queue are PutLast(…) and PutFirst(…) in two versions – either with input time stamps or without.

Using of a time-assigned method for the very first adding to the empty queue turns Time Assignment ON and requires meaningful time stamps for all further data additions when working with a given class instance. Similarly, if the very first adding call was without a time stamp or with the meaningless one, then any attempt to add a time-assigned data point will be rejected with an Exception thrown.

When using of time-assigned methods the user should remember about time check (see about Time Assignment feature above) and take care of valid and meaningful time stamps.

The PutLast(…) methods store an input data point at the beginning of the queue so that it is indexed with zero right after addition:

PutLast(Double)

PutLast(Double, DateTime)

The PutLast(…) methods store an input data point at the opposite side of the queue so that it is indexed at the Count – 1 position in the queue (see also about the Count property above):

PutFirst(Double)

PutFirst(Double, DateTime)

The two methods are to remove data points:

RemoveLast- removes the element at zero position and shifts the others;

RemoveFirst- removes the element at the Count-1 position.

All the methods return true if operation is successful.

The AutoDynamic queue regimen is established by constructors with an input time period:

SimpleDataQueue(TimeSpan) - initializes a new instance of the class which contains an empty data queue to keep elements for the period time interval including interval bounds; the queue reserves positions for 64 elements and computing of statistics is not provided;

SimpleDataQueue(TimeSpan, Boolean) - acts like the previous constructor but with calculations of statistics (if the input flag is true).

Note Note

Like with the ManualControl regimen, the class adopts data queue capacity automatically, so for performance reason it is a good practice to possibly estimate the maximal number of stored elements and, before adding of data points, define the required capacity with an assignment:

C#
1dynamiclInstance.Capacity = maximalNumber;

the maximalNumber value must be more than zero to avoid an Exception.

Another (but not preferable) way to set the AutoDynamic regimen up is to assign a (positive) time interval of TimeSpan type to the Period property with the code line like the following:

C#
1anInstance.Period = desiredPeriod;

The AutoDynamic regimen automatically applies Time Assignment and the only compatible method to add data points is

Put(Double, DateTime)- stores the input number at zero position in the queue and updates the newest and the oldest queue time stamps; the oldest data points strongly out of the time period are dropped. The returned true value indicates successful modification.

Note Note

An attempt to add data with the meaningless DateTime.MinValue time stamp causes an Exception.

To specify the AutoStatic regimen of the data queue, the constructors with an input capacity value should be used:

SimpleDataQueue(Int32) - initializes a new instance of the class which contains an empty data queue to store no more than  capacity  elements and no statistics provided;

SimpleDataQueue(Int32, Boolean) - does the same but sets computing of statistics up.

Using of any of the two constructors with the input capacity of one results in the SingleElement queue regimen, as well as assigning the Capacity property with one before adding of data.

Adding data points with the AutoStatic and SingleElement regimens requires using of the following methods:

Put(Double)- stores the input number at zero position in the queue and drops the very early added data point if the number of elements becomes more than the queue capacity;  returns true if successes;

public bool Put(double data, DateTime time) – does the same but stores also the associated time stamp.

If the first (after initialization) addition of data is done with time-assigned method then Time Assignment is turned ON; this means that the next additions must be also done with meaningful time stamps and time check is applied while adding (see about Time Assignment feature for details). Otherwise, if the first data point is stored without or with the DateTime.MinValue time stamp, Time Assignment is turned OFF and an attempt to add a time-assigned data point causes an Exception.

Access to data

The class provides several ways to access the data stored; access is allowed if class instance signals Initialized = true, otherwise an Exception is thrown. A good practice is to examine the Ready flag before access to the content (see detailed above about the Initialized and Ready properties).

Whatever queue regimen is selected, the class provides access to all the stored elements by indexes with syntax like used to get an element of an array:

C#
1double element = anInstance[index];

With an automatic queue regimen, AutoDynamic or AutoStatic (SingleElement), the zero index always corresponds to the last (newest) data point. This is not true for the ManualControl queue regimen since data can be added at both sides of the queue.

Apparently, maximal index must not exceed the Count- 1 value (see about the Count property), addressing to a non-existent element (i.e. with the index out of [0, Count-1] range) returns the double.NaN value.

If Time Assignment is applied (NeedDateTime is true) and the current queue regimen is not SingleElement, the class instance runs a conformity mechanism which transforms element positions to time stamps and vice versa so that it becomes possible to access elements by time stamps of DateTime type.

The line:

C#
1double element = anInstance[someTime];

returns the historically latest element with the smaller (older) or equal time stamp.

If the addressing time stamp is strongly out of elements’ time interval (what can be examined with the LastDateTime and FirstDateTime properties) the returned value is double.NaN.

Methods that use index-time transformations to convert indexes and time stamps are:

GetTimeByIndex(Int32)- returns the time stamp associated with the element at the specified position (index) or DateTime.MinValue if addresses to non-existent element;

GetIndexByTime(DateTime)- returns the index of the latest element whose time stamp is equal or older than the specified time; of no such element found then -1 is returned.

Note Note

An Exception is thrown while attempt to get index or element by time when NeedDateTime=false or with the SingleElement queue regimen.

In addition to the indexers, the class provides convenient named properties to access the data:

Property

Type

Description

First

double

Returns the first (oldest) stored data element at position Count-1 or double.NaN if the queue is empty.

Last

double

Returns the last (newest) stored data element at zero position or double.NaN if the queue is empty.

Previous

double

Returns the second newest data element at position 1 in the queue or double.NaN if the queue is empty.

To operate with the stored data set as a whole, the class provides two methods:

ToArray- copies the elements of the qeue into a new array and returns the array of originally ordered double numbers;

GetEnumerator- returns System.Collections.IEnumerator object to iterate through the collection of elements.

Note Note

For performance reason, it is a good practice to get the returned value into a local variable of suitable type and then operate with the elements of the local copy.

Statistics provided

If specified while initialization, the class instance computes a number of descriptive statistics and provides them via class properties (the default value of each property is 0.0 which is returned if statistical computations are forbidden):

Property

Description

Sum

Returns the sum of values stored in the queue

SumOfSquares

Returns the sum of squared values stored in the queue

SumOfAbsoluteValues

Returns the sum of absolute values stored in the queue

ArithmeticMean

Returns the average value of the elements accumulated

ExpectedValue

Same as ArithmeticMean

QuadraticMean

Returns the square root of average sum of squares over the elements accumulated

FirstRawMoment

Returns estimation of elements distribution First Raw Moment.

SecondRawMoment

Returns estimation of elements distribution Second Raw Moment.

SecondCentralMoment

Returns estimation of elements distribution Second Central Moment.

VarianceSample

Returns sample estimation of elements distribution Variance.

VariancePopulation

Computes Variance given the stored elements as population

Variance

Same as VariancePopulation.

StandardDeviationSample

Returns sample estimation of elements distribution Standard Deviation

StandardDeviationPopulation

Computes Standard Deviation given the stored elements as population

StandardDeviation

Same as StandardDeviationPopulation

CoefficientOfVariation

Returns Coefficient Of Variation over the stored set of values

Refer to the Distribution Listener section for detailed specifications of the statistics mentioned.

Using of custom listeners

The class obtains additional flexibility with the aid of external event handlers (listeners) that can catch and process added and removed values right after content modification is completed.

Listeners can be implemented either as a special class or via methods-delegates. 

Technique of using a class-listener includes the two stages:

The DistributionListener class is an example of such a listener realized (see the Distribution Listener section for details).  

Delegation approach requires only declaring of delegates and registering them with the methods:

SetOnPopDelegate(SimpleDataQueueOnPopDelegate) – registers the method named onPop to process removed elements;

SetOnPushDelegate(SimpleDataQueueOnPushDelegate) – registers the method named onPush to process added elements.

Both delegate methods must be declared with the following signature:

C#
1public void MethodName(double data, DateTime time)

Every time queue content is modified the methods-delegates will be automatically called to procees added and removed values.

The code snippet below demonstrates delegation technique.

C#
 1//declare delegates
 2public void OnDelete(double data, DateTime time)
 3{
 4    // ...
 5}
 6public void OnAdd(double data, DateTime time)
 7{
 8    // ...
 9}
10
11//init SimpleDataQueue instance:
12SimpleDataQueue gueue = new SimpleDataQueue();
13//register delegates:
14gueue.SetOnPopDelegate(OnDelete);
15gueue.SetOnPushDelegate(OnAdd);

In the examples part of this section, there is a simple code demonstrating how to use custom listeners in class and delegate forms.

Code Sample

The example of SimpleDataQueue class usage:

C#
 1using System;
 2using System.Collections.Generic;
 3using System.Linq;
 4using System.Text;
 5using FinAnalysis.Base;
 6
 7namespace SimpleDataQueueSample
 8{
 9    class SimpleDataQueueSample
10    {
11        const int SimpleDataQueueCapacity = 12;
12        const int Steps = 0xFF;
13
14        static void Main(string[] args)
15        {
16            Random r = new Random();
17
18
19            // AutoStatic Mode
20            SimpleDataQueue simpleDataQueue = new SimpleDataQueue(SimpleDataQueueCapacity, true);
21
22            for (int i = 0; i < Steps; ++i)
23            {
24                simpleDataQueue.Put(r.NextDouble(), DateTime.Now);
25            }
26
27            Console.WriteLine("Arithmetic Mean = {0:0.00}", simpleDataQueue.ArithmeticMean);
28            Console.WriteLine("Coefficient Of Variation = {0:0.00}", simpleDataQueue.CoefficientOfVariation);
29            Console.WriteLine("Expected Value = {0:0.00}", simpleDataQueue.ExpectedValue);
30            Console.WriteLine("First Raw Moment Mean = {0:0.00}", simpleDataQueue.FirstRawMoment);
31            Console.WriteLine("Quadratic Mean = {0:0.00}", simpleDataQueue.QuadraticMean);
32            Console.WriteLine("Second Central Moment  = {0:0.00}", simpleDataQueue.SecondCentralMoment);
33            Console.WriteLine("Second Raw Moment = {0:0.00}", simpleDataQueue.SecondRawMoment);
34            Console.WriteLine("Standard Deviation = {0:0.00}", simpleDataQueue.StandardDeviation);
35            Console.WriteLine("Standard Deviation Population = {0:0.00}", simpleDataQueue.StandardDeviationPopulation);
36            Console.WriteLine("Standard Deviation Sample = {0:0.00}", simpleDataQueue.StandardDeviationSample);
37            Console.WriteLine("Sum = {0:0.00}", simpleDataQueue.Sum);
38            Console.WriteLine("Sum Of Absolute Values = {0:0.00}", simpleDataQueue.SumOfAbsoluteValues);
39            Console.WriteLine("Sum Of Squares = {0:0.00}", simpleDataQueue.SumOfSquares);
40            Console.WriteLine("Variance = {0:0.00}", simpleDataQueue.Variance);
41            Console.WriteLine("Variance Population = {0:0.00}", simpleDataQueue.VariancePopulation);
42            Console.WriteLine("Variance Sample = {0:0.00}", simpleDataQueue.VarianceSample);
43
44
45            for (int i = 0; i < SimpleDataQueueCapacity; ++i)
46            {
47                Console.WriteLine("Time Index #{0} = {1}", i, simpleDataQueue.GetTimeByIndex(i));
48            }
49
50
51
52            // Manual Mode
53            SimpleDataQueue simpleQueue = new SimpleDataQueue(true);
54
55            for (int i = 0; i < Steps; ++i)
56            {
57                simpleQueue.PutFirst(r.NextDouble(), DateTime.Now);
58                simpleQueue.PutLast(r.NextDouble(), DateTime.Now);
59            }
60
61            for (int i = 0; i < Steps / 2; ++i)
62            {
63                simpleQueue.RemoveFirst();
64                simpleQueue.RemoveLast();
65            }
66
67
68            Console.WriteLine("Arithmetic Mean = {0:0.00}", simpleQueue.ArithmeticMean);
69            Console.WriteLine("Coefficient Of Variation = {0:0.00}", simpleQueue.CoefficientOfVariation);
70            Console.WriteLine("Expected Value = {0:0.00}", simpleQueue.ExpectedValue);
71            Console.WriteLine("First Raw Moment Mean = {0:0.00}", simpleQueue.FirstRawMoment);
72            Console.WriteLine("Quadratic Mean = {0:0.00}", simpleQueue.QuadraticMean);
73            Console.WriteLine("Second Central Moment  = {0:0.00}", simpleQueue.SecondCentralMoment);
74            Console.WriteLine("Second Raw Moment = {0:0.00}", simpleQueue.SecondRawMoment);
75            Console.WriteLine("Standard Deviation = {0:0.00}", simpleQueue.StandardDeviation);
76            Console.WriteLine("Standard Deviation Population = {0:0.00}", simpleQueue.StandardDeviationPopulation);
77            Console.WriteLine("Standard Deviation Sample = {0:0.00}", simpleQueue.StandardDeviationSample);
78            Console.WriteLine("Sum = {0:0.00}", simpleQueue.Sum);
79            Console.WriteLine("Sum Of Absolute Values = {0:0.00}", simpleQueue.SumOfAbsoluteValues);
80            Console.WriteLine("Sum Of Squares = {0:0.00}", simpleQueue.SumOfSquares);
81            Console.WriteLine("Variance = {0:0.00}", simpleQueue.Variance);
82            Console.WriteLine("Variance Population = {0:0.00}", simpleQueue.VariancePopulation);
83            Console.WriteLine("Variance Sample = {0:0.00}", simpleQueue.VarianceSample);
84        }
85    }
86}