22 using System.Collections;
23 using System.Collections.Concurrent;
24 using System.Collections.Generic;
25 using System.Globalization;
27 using System.Reflection;
36 private const string Open =
"open";
37 private const string High =
"high";
38 private const string Low =
"low";
39 private const string Close =
"close";
40 private const string Volume =
"volume";
42 private const string AskOpen =
"askopen";
43 private const string AskHigh =
"askhigh";
44 private const string AskLow =
"asklow";
45 private const string AskClose =
"askclose";
46 private const string AskPrice =
"askprice";
47 private const string AskSize =
"asksize";
49 private const string BidOpen =
"bidopen";
50 private const string BidHigh =
"bidhigh";
51 private const string BidLow =
"bidlow";
52 private const string BidClose =
"bidclose";
53 private const string BidPrice =
"bidprice";
54 private const string BidSize =
"bidsize";
56 private const string LastPrice =
"lastprice";
57 private const string Quantity =
"quantity";
58 private const string Exchange =
"exchange";
59 private const string Suspicious =
"suspicious";
63 private static PyString _empty;
64 private static PyObject _pandas;
65 private static PyObject _seriesFactory;
66 private static PyObject _dataFrameFactory;
67 private static PyObject _multiIndexFactory;
69 private static PyList _defaultNames;
70 private static PyList _level2Names;
71 private static PyList _level3Names;
73 private readonly
static HashSet<string> _baseDataProperties = typeof(
BaseData).GetProperties().ToHashSet(x => x.Name.ToLowerInvariant());
74 private readonly
static ConcurrentDictionary<Type, IEnumerable<MemberInfo>> _membersByType =
new ();
75 private readonly
static IReadOnlyList<string> _standardColumns =
new string []
77 Open, High, Low, Close, LastPrice, Volume,
78 AskOpen, AskHigh, AskLow, AskClose, AskPrice, AskSize, Quantity, Suspicious,
82 private readonly
Symbol _symbol;
83 private readonly
bool _isFundamentalType;
84 private readonly Dictionary<string, Serie> _series;
86 private readonly IEnumerable<MemberInfo> _members = Enumerable.
Empty<MemberInfo>();
108 _pandas = Py.Import(
"PandasMapper");
109 _seriesFactory = _pandas.GetAttr(
"Series");
110 _dataFrameFactory = _pandas.GetAttr(
"DataFrame");
111 using var multiIndex = _pandas.GetAttr(
"MultiIndex");
112 _multiIndexFactory = multiIndex.GetAttr(
"from_tuples");
113 _empty =
new PyString(
string.Empty);
115 var time =
new PyString(
"time");
116 var symbol =
new PyString(
"symbol");
117 var expiry =
new PyString(
"expiry");
118 _defaultNames =
new PyList(
new PyObject[] { expiry,
new PyString(
"strike"),
new PyString(
"type"), symbol, time });
119 _level2Names =
new PyList(
new PyObject[] { symbol, time });
120 _level3Names =
new PyList(
new PyObject[] { expiry, symbol, time });
126 if (data is not
IBaseData && data is IEnumerable enumerable)
128 foreach (var item
in enumerable)
135 var type = data.GetType();
141 if (_symbol.SecurityType.IsOption())
Levels = 5;
143 IEnumerable<string> columns = _standardColumns;
147 var keys = (data as
DynamicData)?.GetStorageDictionary()
149 .Where(x => !x.Key.StartsWith(
"__", StringComparison.InvariantCulture)).ToHashSet(x => x.Key);
154 if (_membersByType.TryGetValue(type, out _members))
156 keys = _members.ToHashSet(x => x.Name.ToLowerInvariant());
160 var members = type.GetMembers().Where(x => x.MemberType == MemberTypes.Field || x.MemberType == MemberTypes.Property).ToList();
162 var duplicateKeys = members.GroupBy(x => x.Name.ToLowerInvariant()).Where(x => x.Count() > 1).Select(x => x.Key);
163 foreach (var duplicateKey
in duplicateKeys)
165 throw new ArgumentException($
"PandasData.ctor(): {Messages.PandasData.DuplicateKey(duplicateKey, type.FullName)}");
169 keys = members.ToHashSet(x => x.Name.ToLowerInvariant());
170 keys.ExceptWith(_baseDataProperties);
171 keys.ExceptWith(GetPropertiesNames(typeof(
QuoteBar), type));
172 keys.ExceptWith(GetPropertiesNames(typeof(
TradeBar), type));
173 keys.ExceptWith(GetPropertiesNames(typeof(
Tick), type));
176 _members = members.Where(x => keys.Contains(x.Name.ToLowerInvariant())).ToList();
177 _membersByType.TryAdd(type, _members);
181 var customColumns =
new HashSet<string>(columns);
182 customColumns.Add(
"value");
183 customColumns.UnionWith(keys);
185 columns = customColumns;
188 _series = columns.ToDictionary(k => k, v =>
new Serie());
195 public void Add(
object baseData)
197 var endTime = ((
IBaseData)baseData).EndTime;
198 foreach (var member
in _members)
201 var key = member.Name.ToLowerInvariant();
202 var propertyMember = member as PropertyInfo;
203 if (propertyMember !=
null)
205 var propertyValue = propertyMember.GetValue(baseData);
210 AddToSeries(key, endTime, propertyValue);
215 var fieldMember = member as FieldInfo;
216 if (fieldMember !=
null)
218 AddToSeries(key, endTime, fieldMember.GetValue(baseData));
223 var storage = (baseData as
DynamicData)?.GetStorageDictionary();
226 var value = ((
IBaseData) baseData).Value;
227 AddToSeries(
"value", endTime, value);
229 foreach (var kvp
in storage.Where(x => x.Key !=
"value"
231 && !x.Key.StartsWith(
"__", StringComparison.InvariantCulture)))
233 AddToSeries(kvp.Key, endTime, kvp.Value);
238 var tick = baseData as
Tick;
245 var tradeBar = baseData as
TradeBar;
246 var quoteBar = baseData as
QuoteBar;
247 Add(tradeBar, quoteBar);
259 if (tradeBar !=
null)
262 GetSerie(Open).Add(time, tradeBar.
Open);
263 GetSerie(High).Add(time, tradeBar.
High);
264 GetSerie(Low).Add(time, tradeBar.
Low);
265 GetSerie(Close).Add(time, tradeBar.
Close);
266 GetSerie(Volume).Add(time, tradeBar.
Volume);
268 if (quoteBar !=
null)
271 if (tradeBar ==
null)
273 GetSerie(Open).Add(time, quoteBar.
Open);
274 GetSerie(High).Add(time, quoteBar.
High);
275 GetSerie(Low).Add(time, quoteBar.
Low);
276 GetSerie(Close).Add(time, quoteBar.
Close);
278 if (quoteBar.
Ask !=
null)
280 GetSerie(AskOpen).Add(time, quoteBar.
Ask.
Open);
281 GetSerie(AskHigh).Add(time, quoteBar.
Ask.
High);
282 GetSerie(AskLow).Add(time, quoteBar.
Ask.
Low);
283 GetSerie(AskClose).Add(time, quoteBar.
Ask.
Close);
286 if (quoteBar.
Bid !=
null)
288 GetSerie(BidOpen).Add(time, quoteBar.
Bid.
Open);
289 GetSerie(BidHigh).Add(time, quoteBar.
Bid.
High);
290 GetSerie(BidLow).Add(time, quoteBar.
Bid.
Low);
291 GetSerie(BidClose).Add(time, quoteBar.
Bid.
Close);
310 GetSerie(AskPrice).Add(time, tick.
AskPrice);
311 GetSerie(AskSize).Add(time, tick.
AskSize);
312 GetSerie(BidPrice).Add(time, tick.
BidPrice);
313 GetSerie(BidSize).Add(time, tick.
BidSize);
318 GetSerie(AskPrice).Add(time,
null);
319 GetSerie(AskSize).Add(time,
null);
320 GetSerie(BidPrice).Add(time,
null);
321 GetSerie(BidSize).Add(time,
null);
325 GetSerie(Suspicious).Add(time, tick.
Suspicious);
326 GetSerie(Quantity).Add(time, tick.
Quantity);
331 GetSerie(LastPrice).Add(time,
null);
335 GetSerie(LastPrice).Add(time, tick.
Value);
348 var symbol = _symbol.ID.ToString().ToPython();
351 var names = _defaultNames;
355 names = _level2Names;
356 list =
new List<PyObject> { symbol, _empty };
358 else if (levels == 3)
361 names = _level3Names;
362 list =
new List<PyObject> { _symbol.ID.Date.ToPython(), symbol, _empty };
366 list =
new List<PyObject> { _empty, _empty, _empty, symbol, _empty };
369 list[0] = _symbol.ID.Date.ToPython();
371 else if (_symbol.SecurityType.IsOption())
373 list[0] = _symbol.ID.Date.ToPython();
374 list[1] = _symbol.ID.StrikePrice.ToPython();
375 list[2] = _symbol.ID.OptionRight.ToString().ToPython();
382 using var pyDict =
new PyDict();
383 foreach (var kvp
in _series)
385 if (kvp.Value.ShouldFilter)
continue;
387 if (!indexCache.TryGetValue(kvp.Value.Times, out var index))
389 using var tuples = kvp.Value.Times.Select(time => CreateTupleIndex(time, list)).ToPyListUnSafe();
390 using var namesDic = Py.kw(
"names", names);
392 indexCache[kvp.Value.Times] = index = _multiIndexFactory.Invoke(
new[] { tuples }, namesDic);
394 foreach (var pyObject
in tuples)
401 using var pyvalues =
new PyList();
402 for (var i = 0; i < kvp.Value.Values.Count; i++)
404 using var pyObject = kvp.Value.Values[i].ToPython();
405 pyvalues.Append(pyObject);
407 using var series = _seriesFactory.Invoke(pyvalues, index);
408 pyDict.SetItem(kvp.Key, series);
411 foreach (var kvp
in indexCache)
416 for (var i = 0; i < list.Count; i++)
418 DisposeIfNotEmpty(list[i]);
422 var result = _dataFrameFactory.Invoke(pyDict);
424 foreach (var item
in pyDict)
435 private static void DisposeIfNotEmpty(PyObject pyObject)
437 if (!ReferenceEquals(pyObject, _empty))
446 private static PyTuple CreateTupleIndex(DateTime index, List<PyObject> list)
448 DisposeIfNotEmpty(list[list.Count - 1]);
449 list[list.Count - 1] = index.ToPython();
450 return new PyTuple(list.ToArray());
459 private void AddToSeries(
string key, DateTime time,
object input)
461 var serie = GetSerie(key);
462 serie.Add(time, input);
465 private Serie GetSerie(
string key)
467 if (!_series.TryGetValue(key, out var value))
469 throw new ArgumentException($
"PandasData.GetSerie(): {Messages.PandasData.KeyNotFoundInSeries(key)}");
480 private static IEnumerable<string> GetPropertiesNames(Type baseType, Type type)
482 return baseType.IsAssignableFrom(type)
483 ? baseType.GetProperties().Select(x => x.Name.ToLowerInvariant())
484 : Enumerable.Empty<
string>();
489 private static readonly IFormatProvider InvariantCulture = CultureInfo.InvariantCulture;
490 public bool ShouldFilter {
get;
set; } =
true;
491 public List<DateTime> Times {
get;
set; } =
new();
492 public List<object> Values {
get;
set; } =
new();
494 public void Add(DateTime time,
object input)
496 var value = input is decimal ? Convert.ToDouble(input, InvariantCulture) : input;
502 if (!((
double)value).IsNaNOrZero())
504 ShouldFilter =
false;
507 else if (value is
string)
509 if (!
string.IsNullOrWhiteSpace((
string)value))
511 ShouldFilter =
false;
514 else if (value is
bool)
518 ShouldFilter =
false;
521 else if (value !=
null)
523 ShouldFilter =
false;
531 public void Add(DateTime time, decimal input)
533 var value = Convert.ToDouble(input, InvariantCulture);
534 if (ShouldFilter && !value.IsNaNOrZero())
536 ShouldFilter =
false;
544 private class FixedTimeProvider : ITimeProvider
546 private readonly DateTime _time;
547 public DateTime GetUtcNow() => _time;
548 public FixedTimeProvider(DateTime time)