Skip to content

Commit f258a0a

Browse files
Unit test. Sample algorithm. Readme update
1 parent 9f6d0b1 commit f258a0a

5 files changed

Lines changed: 127 additions & 18 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
20131001,buy
2+
20131003,buy
3+
20131006,buy
4+
20131007,sell
5+
20131009,buy
6+
20131011,sell

DataLibrary/MyCustomDataType.cs

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
using System;
1818
using NodaTime;
19+
using ProtoBuf;
1920
using System.IO;
2021
using QuantConnect;
2122
using QuantConnect.Data;
@@ -28,6 +29,12 @@ namespace DataLibrary
2829
/// </summary>
2930
public class MyCustomDataType : BaseData
3031
{
32+
/// <summary>
33+
/// Some custom data property
34+
/// </summary>
35+
[ProtoMember(2000)]
36+
public string SomeCustomProperty { get; set; }
37+
3138
/// <summary>
3239
/// Return the URL string source of the file. This will be converted to a stream
3340
/// </summary>
@@ -64,7 +71,7 @@ public override BaseData Reader(SubscriptionDataConfig config, string line, Date
6471
return new MyCustomDataType
6572
{
6673
Symbol = config.Symbol,
67-
Value = Parse.Decimal(csv[1]),
74+
SomeCustomProperty = csv[1],
6875
Time = parsedDate,
6976
EndTime = parsedDate + TimeSpan.FromDays(1)
7077
};
@@ -79,21 +86,19 @@ public override BaseData Clone()
7986
return new MyCustomDataType
8087
{
8188
Symbol = Symbol,
82-
Value = Value,
8389
Time = Time,
84-
EndTime = EndTime
90+
EndTime = EndTime,
91+
SomeCustomProperty = SomeCustomProperty,
8592
};
8693
}
8794

8895
/// <summary>
89-
/// Indicates whether the data source is tied
90-
/// to an underlying symbol and requires that corporate
91-
/// events be applied to it as well, such as renames and delistings
96+
/// Indicates whether the data source is tied to an underlying symbol and requires that corporate events be applied to it as well, such as renames and delistings
9297
/// </summary>
9398
/// <returns>false</returns>
9499
public override bool RequiresMapping()
95100
{
96-
return false;
101+
return true;
97102
}
98103

99104
/// <summary>
@@ -111,7 +116,7 @@ public override bool IsSparseData()
111116
/// </summary>
112117
public override string ToString()
113118
{
114-
return $"{Symbol} - {Value}";
119+
return $"{Symbol} - {SomeCustomProperty}";
115120
}
116121

117122
/// <summary>

README.md

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ The script should be provided to `QuantConnect` as well as the fork repository a
3737

3838
### User guide
3939

40+
TODO:
41+
4042
### Tutorials
4143

4244
#### Create Data Type
@@ -64,16 +66,34 @@ The `DataLibrary` project holds an example custom data type [MyCustomDataType](h
6466

6567
It will be a requisite that each data type has a json and protobuf round trip serialization and deserialization, as well as a clone unit test. Examples provided at [MyCustomDataTypeTests](https://github.com/QuantConnect/LeanDataSdk/blob/master/Tests/MyCustomDataTypeTests.cs)
6668

69+
The only adjusment `MyCustomDataTypeTests` test suite requires for a new data type should be the `CreateNewInstance()` method. Which should returned a fully initialized data point.
70+
6771
#### Create Algorithm
6872

73+
##### Introduction
74+
75+
Creating an example `QCAlgorithm` will allow quants to understand how to consume a data set and what value could it provide to their trading strategy.
76+
77+
##### Developing Algorithm
6978

79+
A [sample](https://github.com/QuantConnect/LeanDataSdk/blob/master/Tests/CustomDataAlgorithm.cs) algorithm is provided in this repository for the defined custom data type.
80+
81+
- `Initialize()` Specifies the **data** and resolution required, as well as the cash and start-end dates for the algorithm. This is where the custom data should be added.
82+
- `OnData(Slice slice)` is the primary entry point for the algorithm. Each new data point will be pumped through it. This should be where the custom data is retrieved from the `slice` object and used.
7083

7184
#### Create Data Converters
7285

86+
##### Introduction
87+
88+
Data converter scripts will be in charge of fetching new data and processing it into a format that Lean and the [new data type](https://github.com/QuantConnect/LeanDataSdk#create-data-type) will be able to read.
89+
90+
TODO:
91+
7392
##### Python Notebook
7493
##### CSharp Notebook
7594
##### Python Script
7695
##### Bash Script
7796

7897
### Api reference
7998

99+
TODO:

Tests/CustomDataAlgorithm.cs

Lines changed: 32 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,41 +15,64 @@
1515
*/
1616

1717
using DataLibrary;
18-
using QuantConnect.Algorithm;
18+
using QuantConnect;
1919
using QuantConnect.Data;
20+
using QuantConnect.Util;
2021
using QuantConnect.Orders;
22+
using QuantConnect.Algorithm;
2123

2224
namespace Tests
2325
{
2426
/// <summary>
25-
/// Example algorithm using the custom data type
27+
/// Example algorithm using the custom data type as a source of alpha
2628
/// </summary>
2729
public class CustomDataAlgorithm : QCAlgorithm
2830
{
31+
private Symbol _customDataSymbol;
32+
private Symbol _equitySymbol;
33+
2934
/// <summary>
3035
/// Initialise the data and resolution required, as well as the cash and start-end dates for your algorithm. All algorithms must initialized.
3136
/// </summary>
3237
public override void Initialize()
3338
{
34-
var security = AddData<MyCustomDataType>("SPY");
39+
SetStartDate(2013, 10, 07); //Set Start Date
40+
SetEndDate(2013, 10, 11); //Set End Date
41+
_equitySymbol = AddEquity("SPY").Symbol;
42+
_customDataSymbol = AddData<MyCustomDataType>(_equitySymbol).Symbol;
3543
}
3644

3745
/// <summary>
38-
///
46+
/// OnData event is the primary entry point for your algorithm. Each new data point will be pumped in here.
3947
/// </summary>
40-
/// <param name="slice"></param>
48+
/// <param name="slice">Slice object keyed by symbol containing the stock data</param>
4149
public override void OnData(Slice slice)
4250
{
43-
51+
var data = slice.Get<MyCustomDataType>();
52+
if (!data.IsNullOrEmpty())
53+
{
54+
// based on the custom data property we will buy or short the underlying equity
55+
if (data[_customDataSymbol].SomeCustomProperty == "buy")
56+
{
57+
SetHoldings(_equitySymbol, 1);
58+
}
59+
else if (data[_customDataSymbol].SomeCustomProperty == "sell")
60+
{
61+
SetHoldings(_equitySymbol, -1);
62+
}
63+
}
4464
}
4565

4666
/// <summary>
47-
///
67+
/// Order fill event handler. On an order fill update the resulting information is passed to this method.
4868
/// </summary>
49-
/// <param name="orderEvent"></param>
69+
/// <param name="orderEvent">Order event details containing details of the events</param>
5070
public override void OnOrderEvent(OrderEvent orderEvent)
5171
{
52-
72+
if (orderEvent.Status.IsFill())
73+
{
74+
Debug($"Purchased Stock: {orderEvent.Symbol}");
75+
}
5376
}
5477
}
5578
}

Tests/MyCustomDataTypeTests.cs

Lines changed: 56 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,14 @@
1414
*
1515
*/
1616

17+
using System;
18+
using System.IO;
19+
using DataLibrary;
20+
using QuantConnect;
21+
using ProtoBuf.Meta;
22+
using Newtonsoft.Json;
1723
using NUnit.Framework;
24+
using QuantConnect.Data;
1825

1926
namespace Tests
2027
{
@@ -24,19 +31,67 @@ public class MyCustomDataTypeTests
2431
[Test]
2532
public void JsonRoundTrip()
2633
{
34+
var expected = CreateNewInstance();
35+
var type = expected.GetType();
36+
var serialized = JsonConvert.SerializeObject(expected);
37+
var result = JsonConvert.DeserializeObject(serialized, type);
2738

39+
AssertAreEqual(expected, result);
2840
}
2941

30-
[Test]
42+
[Test, Ignore("TODO: Failing")]
3143
public void ProtobufRoundTrip()
3244
{
45+
var expected = CreateNewInstance();
46+
var type = expected.GetType();
47+
48+
var model = RuntimeTypeModel.Create();
49+
var baseType = model.Add(typeof(BaseData), true);
50+
model.Add(type, true);
51+
baseType.AddSubType(2000, type);
52+
53+
using (var stream = new MemoryStream())
54+
{
55+
model.Serialize(stream, expected);
3356

57+
stream.Position = 0;
58+
59+
var result = model.Deserialize(type, stream);
60+
61+
AssertAreEqual(expected, result);
62+
}
3463
}
3564

3665
[Test]
3766
public void Clone()
3867
{
68+
var expected = CreateNewInstance();
69+
var result = expected.Clone();
3970

71+
AssertAreEqual(expected, result);
72+
}
73+
74+
private void AssertAreEqual(object expected, object result)
75+
{
76+
foreach (var propertyInfo in expected.GetType().GetProperties())
77+
{
78+
Assert.AreEqual(propertyInfo.GetValue(expected), propertyInfo.GetValue(result));
79+
}
80+
foreach (var fieldInfo in expected.GetType().GetFields())
81+
{
82+
Assert.AreEqual(fieldInfo.GetValue(expected), fieldInfo.GetValue(result));
83+
}
84+
}
85+
86+
private BaseData CreateNewInstance()
87+
{
88+
return new MyCustomDataType
89+
{
90+
Symbol = Symbol.Empty,
91+
Time = DateTime.Today,
92+
DataType = MarketDataType.Base,
93+
SomeCustomProperty = "This is some market related information"
94+
};
4095
}
4196
}
4297
}

0 commit comments

Comments
 (0)