Source/NFX/Financial/Market/SamplingUtils.cs
/// <summary>
/// Synthesizes a stream of candle samples from Quote and Trade samples coming from the market (i.e SecDB file)
/// </summary>
/// <param name="source">Source of market data</param>
/// <param name="secSamplingPeriod">The output sampling period</param>
/// <param name="funcQuote">Aggregation func for Quote, if null default is used which aggregates best bid</param>
/// <param name="funcTrade">Aggregation func for Quote, if null default is used which aggregates buy and sell volumes</param>
/// <returns>Synthesized candle stream</returns>
Synthesizes a stream of candle samples from Quote and Trade samples coming from the market
Synthesizes a stream of candle samples from Quote and Trade samples coming from the market
public static IEnumerable<CandleSample> SynthesizeCandles(this IEnumerable<ITimeSeriesSample> source,
                                                      uint secSamplingPeriod,
                                                      Action<CandleSample, SecDBFileReader.QuoteSample, int> funcQuote = null,
                                                      Action<CandleSample, SecDBFileReader.TradeSample, int> funcTrade = null)
    {
      if (source==null) yield break;
      if (funcQuote==null)
      {
        funcQuote = (cs, qs, i) =>
        {
          var bestBid = qs.Bids.LastOrDefault();
          if (Math.Abs(bestBid.Price) < float.Epsilon) return;
          if (i==0) cs.OpenPrice = bestBid.Price;
          cs.HighPrice  = Math.Max(cs.HighPrice, bestBid.Price);
          cs.LowPrice   = Math.Abs(cs.LowPrice) > float.Epsilon ? Math.Min( cs.LowPrice, bestBid.Price) : bestBid.Price;
          cs.ClosePrice = bestBid.Price;
        };
      }
      if (funcTrade==null)
      {
        funcTrade = (cs, ts, i) =>
        {
          if (!ts.IsQty) return;
          if (ts.Side==SecDBFileReader.TradeSample.SideType.Buy)
            cs.BuyVolume += ts.Qty;
          else
            cs.SellVolume += ts.Qty;
        };
      }
      CandleSample emit = null;
      var filteredSamples = source.Where( s => s is SecDBFileReader.QuoteSample ||
                                               s is SecDBFileReader.TradeSample);
      var aggregateCount = 0;
      foreach(var sample in filteredSamples)
      {
        if (emit!=null && (sample.TimeStamp - emit.TimeStamp).TotalSeconds > secSamplingPeriod)
        {
          emit.TimeSpanMs = (long)(sample.TimeStamp - emit.TimeStamp).TotalMilliseconds;
          yield return emit;
          emit = null;
        }
        if (emit==null)
        {
          emit = new CandleSample(sample.TimeStamp);
          aggregateCount = 0;
        }
        var qts = sample as SecDBFileReader.QuoteSample;
        if (qts!=null)
        {
          funcQuote(emit, qts, aggregateCount);
        }
        var tds = sample as SecDBFileReader.TradeSample;
        if (tds!=null)
        {
          funcTrade(emit, tds, aggregateCount);
        }
        aggregateCount++;
      }
      if (emit!=null)
        yield return emit;
    }
public static IEnumerable < CandleSample > SynthesizeCandles ( this IEnumerable < ITimeSeriesSample > source , uint secSamplingPeriod , Action < CandleSample , SecDBFileReader . QuoteSample , int > funcQuote = null , Action < CandleSample , SecDBFileReader . TradeSample , int > funcTrade = null ) { if ( source == null ) yield break ; if ( funcQuote == null ) { funcQuote = ( cs , qs , i ) => { var bestBid = qs . Bids . LastOrDefault ( ) ; if ( Math . Abs ( bestBid . Price ) < float . Epsilon ) return ; if ( i == 0 ) cs . OpenPrice = bestBid . Price ; cs . HighPrice = Math . Max ( cs . HighPrice , bestBid . Price ) ; cs . LowPrice = Math . Abs ( cs . LowPrice ) > float . Epsilon ? Math . Min ( cs . LowPrice , bestBid . Price ) : bestBid . Price ; cs . ClosePrice = bestBid . Price ; } ; } if ( funcTrade == null ) { funcTrade = ( cs , ts , i ) => { if ( ! ts . IsQty ) return ; if ( ts . Side == SecDBFileReader . TradeSample . SideType . Buy ) cs . BuyVolume += ts . Qty ; else cs . SellVolume += ts . Qty ; } ; } CandleSample emit = null ; var filteredSamples = source . Where ( s => s is SecDBFileReader . QuoteSample || s is SecDBFileReader . TradeSample ) ; var aggregateCount = 0 ; foreach ( var sample in filteredSamples ) { if ( emit != null && ( sample . TimeStamp - emit . TimeStamp ) . TotalSeconds > secSamplingPeriod ) { emit . TimeSpanMs = ( long ) ( sample . TimeStamp - emit . TimeStamp ) . TotalMilliseconds ; yield return emit ; emit = null ; } if ( emit == null ) { emit = new CandleSample ( sample . TimeStamp ) ; aggregateCount = 0 ; } var qts = sample as SecDBFileReader . QuoteSample ; if ( qts != null ) { funcQuote ( emit , qts , aggregateCount ) ; } var tds = sample as SecDBFileReader . TradeSample ; if ( tds != null ) { funcTrade ( emit , tds , aggregateCount ) ; } aggregateCount ++ ; } if ( emit != null ) yield return emit ; }
Synthesizes a stream of candle samples from Quote and Trade samples coming from the market (i.e SecDB file)
Synthesizes a stream of candle samples from Quote and Trade samples coming from the market ( i . e SecDB file )
Synthesized candle stream
Synthesized candle stream
IEnumerable<ITimeSeriesSample>
this
secSamplingPeriod
uint
The output sampling period
The output sampling period
funcQuote
Action<CandleSample, SecDBFileReader.QuoteSample, int>
Aggregation func for Quote, if null default is used which aggregates best bid
Aggregation func for Quote if null default is used which aggregates best bid
funcTrade
Action<CandleSample, SecDBFileReader.TradeSample, int>
Aggregation func for Quote, if null default is used which aggregates buy and sell volumes
Aggregation func for Quote if null default is used which aggregates buy and sell volumes
source
Source of market data
Source of market data