using System.Collections.Generic; namespace MarketData.ValueAtRisk { public class BinResult { public double Value { get; private set; } public T Item { get; private set; } public BinResult() { } public BinResult(double value, T item) { this.Value = value; this.Item = item; } } public class BinManager { private List> binItems; private long binCount; private long samples; public BinManager(long binCount = 100) { this.binCount = binCount; InitializeBins(); } private void InitializeBins() { this.binItems = new List>(); this.samples = 0; } public BinResult GetVaRReturn(double[] values, List items, double percentile) { double minValue = float.NaN; double maxValue = float.NaN; double binSize = double.NaN; T item = default(T); InitializeBins(); GetMinMax(ref minValue, ref maxValue, values); binSize = (maxValue - minValue) / (double)binCount; if (binSize == 0) binSize = 1e-10; // Create bins for (double value = minValue; value <= maxValue; value += binSize) { binItems.Add(new BinItem(value)); } // Add values and objects to bins for (int index = 0; index < values.Length; index++) { AddValueToBin(values[index]); AddObjectToBin(values[index], items[index]); } double samplesAtRank = samples - (samples * (percentile / 100.0)); if (samplesAtRank < 1) samplesAtRank = 1; long samplesInRank = 0; double percentVaR = 0; for (int index = 0; index < binItems.Count; index++) { BinItem binItem = binItems[index]; samplesInRank += binItem.BinCount; percentVaR = binItem.BinValue; item = binItem.BinObject; if (samplesInRank > samplesAtRank) break; } return new BinResult(percentVaR, item); } public double GetVaRReturn(double[] values, double percentile) { double minValue = float.NaN; double maxValue = float.NaN; double binSize = double.NaN; InitializeBins(); GetMinMax(ref minValue, ref maxValue, values); binSize = (maxValue - minValue) / (double)binCount; if (binSize == 0) binSize = 1e-10; // Create bins for (double value = minValue; value <= maxValue; value += binSize) { binItems.Add(new BinItem(value)); } // Add values to bins for (int index = 0; index < values.Length; index++) { AddValueToBin(values[index]); } double samplesAtRank = samples - (samples * (percentile / 100.0)); if (samplesAtRank < 1) samplesAtRank = 1; long samplesInRank = 0; double percentVaR = 0; for (int index = 0; index < binItems.Count; index++) { BinItem binItem = binItems[index]; samplesInRank += binItem.BinCount; percentVaR = binItem.BinValue; if (samplesInRank > samplesAtRank) break; } return percentVaR; } private static void GetMinMax(ref double minValue, ref double maxValue, double[] values) { for (int index = 0; index < values.Length; index++) { double value = values[index]; if (index == 0) { minValue = maxValue = value; continue; } if (value > maxValue) maxValue = value; if (value < minValue) minValue = value; } } private void AddValueToBin(double value) { bool added = false; samples++; for (int index = 0; index < binItems.Count; index++) { BinItem binItem = binItems[index]; if (value <= binItem.BinValue) { binItem.BinCount++; added = true; break; } } if (!added && binItems.Count > 0) binItems[binItems.Count - 1].BinCount++; } private void AddObjectToBin(double value, T item) { bool added = false; for (int index = 0; index < binItems.Count; index++) { BinItem binItem = binItems[index]; if (value <= binItem.BinValue) { binItem.BinObject = item; added = true; break; } } if (!added && binItems.Count > 0) binItems[binItems.Count - 1].BinObject = item; } } }