Skip to content

Commit c2147b8

Browse files
committed
Added main feature and unit tests
1 parent 387eae9 commit c2147b8

File tree

2 files changed

+106
-0
lines changed

2 files changed

+106
-0
lines changed

Common/Securities/SecurityCache.cs

+35
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@
1919
using QuantConnect.Data.Market;
2020
using System.Collections.Generic;
2121
using System.Runtime.CompilerServices;
22+
using QuantConnect.Python;
23+
using System.Reflection;
24+
using Python.Runtime;
2225

2326
namespace QuantConnect.Securities
2427
{
@@ -224,6 +227,38 @@ protected virtual void ProcessDataPoint(BaseData data, bool cacheByType)
224227
return;
225228
}
226229

230+
var pythonData = data as PythonData;
231+
if (pythonData != null && (_lastQuoteBarUpdate != data.EndTime || _lastOHLCUpdate != data.EndTime) && isDefaultDataType)
232+
{
233+
// Get matching pythonData and IBar properties
234+
IDictionary<string, object> storage = pythonData.GetStorageDictionary();
235+
PropertyInfo[] barProperties = typeof(IBar).GetProperties();
236+
List<string> fieldsRequired = barProperties.Select(property => property.Name.ToLowerInvariant()).ToList();
237+
var matches = storage.Where(kvp => fieldsRequired.Contains(kvp.Key) && kvp.Value != null) ;
238+
239+
IDictionary<string, decimal> validOHLC = new Dictionary<string, decimal>();
240+
241+
// Convert OHLC to decimal & update properties
242+
if (matches.Count() == fieldsRequired.Count)
243+
{
244+
foreach (KeyValuePair<string, object> match in matches)
245+
{
246+
decimal.TryParse(match.Value.ToString(), out decimal result);
247+
validOHLC.Add(match.Key, result);
248+
}
249+
_lastOHLCUpdate = data.EndTime;
250+
if (validOHLC["open"] != 0) Open = validOHLC["open"];
251+
if (validOHLC["high"] != 0) High = validOHLC["high"];
252+
if (validOHLC["low"] != 0) Low = validOHLC["low"];
253+
if (validOHLC["close"] != 0)
254+
{
255+
Price = validOHLC["close"];
256+
Close = validOHLC["close"];
257+
}
258+
return;
259+
}
260+
}
261+
227262
var bar = data as IBar;
228263
if (bar != null)
229264
{

Tests/Common/Securities/SecurityCacheTests.cs

+71
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,15 @@
1717
using System.Collections.Generic;
1818
using System.Linq;
1919
using Newtonsoft.Json.Linq;
20+
using NodaTime;
2021
using NUnit.Framework;
22+
using Python.Runtime;
2123
using QuantConnect.Algorithm.CSharp;
2224
using QuantConnect.Data;
2325
using QuantConnect.Data.Fundamental;
2426
using QuantConnect.Data.Market;
2527
using QuantConnect.Data.UniverseSelection;
28+
using QuantConnect.Python;
2629
using QuantConnect.Securities;
2730
using QuantConnect.Tests.Common.Data.Fundamental;
2831

@@ -359,6 +362,65 @@ public void AddDataEquity_OHLC_IgnoresQuoteBar()
359362
Assert.AreEqual(110, securityCache.BidSize);
360363
}
361364

365+
[Test]
366+
public void AddDataEquity_OHLC_PythonData()
367+
{
368+
using (Py.GIL())
369+
{
370+
dynamic testModule = PyModule.FromString("testModule",
371+
@"
372+
from AlgorithmImports import *
373+
374+
class CustomDataTest(PythonData):
375+
def Reader(self, config, line, date, isLiveMode):
376+
result = CustomDataTest()
377+
result.Symbol = config.Symbol
378+
result.Value = 3.7
379+
result.Open = 3.1
380+
result.High = 4.1
381+
result.low = 2.0
382+
result.close = 3.7
383+
result.Time = datetime.strptime(""2022-05-05"", ""%Y-%m-%d"")
384+
return result");
385+
386+
var data = GetDataFromModule(testModule);
387+
388+
var securityCache = new SecurityCache();
389+
securityCache.AddData(data);
390+
391+
Assert.AreEqual(4.1, securityCache.High);
392+
Assert.AreEqual(3.7, securityCache.Close);
393+
Assert.AreEqual(2.0, securityCache.Low);
394+
Assert.AreEqual(3.1, securityCache.Open);
395+
396+
testModule = PyModule.FromString("testModule",
397+
@"
398+
from AlgorithmImports import *
399+
400+
class CustomDataTest(PythonData):
401+
def Reader(self, config, line, date, isLiveMode):
402+
result = CustomDataTest()
403+
result.Symbol = config.Symbol
404+
result.Value = 3.7
405+
result.Open = 3.1
406+
result.High = 4
407+
result.low = 2.0
408+
result.Close = ""test""
409+
result.Time = datetime.strptime(""2022-05-05"", ""%Y-%m-%d"")
410+
return result");
411+
412+
data = GetDataFromModule(testModule);
413+
414+
securityCache.Reset();
415+
securityCache.AddData(data);
416+
417+
Assert.AreEqual(4, securityCache.High);
418+
Assert.AreEqual(0, securityCache.Close);
419+
Assert.AreEqual(2.0, securityCache.Low);
420+
Assert.AreEqual(3.1, securityCache.Open);
421+
}
422+
}
423+
362424
[Test]
363425
[TestCaseSource(nameof(GetSecurityCacheInitialStates))]
364426
public void AddDataWithSameEndTime_SetsQuoteBarValues(SecurityCache cache, SecuritySeedData seedType)
@@ -456,6 +518,15 @@ public void GetAllData_ReturnsListOfDataOnTargetCache()
456518
Assert.AreEqual(2m, data[1].Ask);
457519
}
458520

521+
private static BaseData GetDataFromModule(dynamic testModule)
522+
{
523+
var type = Extensions.CreateType(testModule.GetAttr("CustomDataTest"));
524+
var customDataTest = new PythonData(testModule.GetAttr("CustomDataTest")());
525+
var config = new SubscriptionDataConfig(type, Symbols.SPY, Resolution.Daily, DateTimeZone.Utc,
526+
DateTimeZone.Utc, false, false, false, isCustom: true);
527+
return customDataTest.Reader(config, "something", DateTime.UtcNow, false);
528+
}
529+
459530
private void AddDataAndAssertChanges(SecurityCache cache, SecuritySeedData seedType, SecuritySeedData dataType, BaseData data, Dictionary<string, string> cacheToBaseDataPropertyMap = null)
460531
{
461532
var before = JObject.FromObject(cache);

0 commit comments

Comments
 (0)