chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,364 @@
|
||||
/*
|
||||
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
|
||||
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*
|
||||
*/
|
||||
|
||||
using NUnit.Framework;
|
||||
using Python.Runtime;
|
||||
using QuantConnect.Data;
|
||||
using QuantConnect.Python;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuantConnect.Tests.Python
|
||||
{
|
||||
[TestFixture]
|
||||
public partial class PandasConverterTests
|
||||
{
|
||||
// Over complicating it on purpose to test the expanding of nested classes
|
||||
private class BarOpen
|
||||
{
|
||||
public decimal Open { get; set; }
|
||||
public BarOpen(decimal open)
|
||||
{
|
||||
Open = open;
|
||||
}
|
||||
}
|
||||
|
||||
private class BarHigh
|
||||
{
|
||||
public decimal High { get; set; }
|
||||
public BarHigh(decimal high)
|
||||
{
|
||||
High = high;
|
||||
}
|
||||
}
|
||||
|
||||
private class BarLow
|
||||
{
|
||||
public decimal Low { get; set; }
|
||||
public BarLow(decimal low)
|
||||
{
|
||||
Low = low;
|
||||
}
|
||||
}
|
||||
|
||||
private class BarClose
|
||||
{
|
||||
public decimal Close { get; set; }
|
||||
public BarClose(decimal close)
|
||||
{
|
||||
Close = close;
|
||||
}
|
||||
}
|
||||
|
||||
private class CustomBar
|
||||
{
|
||||
public BarOpen Open { get; set; }
|
||||
public BarHigh High { get; set; }
|
||||
public BarLow Low { get; set; }
|
||||
public BarClose Close { get; set; }
|
||||
}
|
||||
|
||||
private class CustomTradeBar : BaseData
|
||||
{
|
||||
public CustomBar Prices { get; set; }
|
||||
|
||||
public decimal Volume { get; set; }
|
||||
|
||||
public CustomTradeBar(Symbol symbol, decimal open, decimal high, decimal low, decimal close, decimal volume)
|
||||
{
|
||||
Symbol = symbol;
|
||||
Prices = new CustomBar
|
||||
{
|
||||
Open = new BarOpen(open),
|
||||
High = new BarHigh(high),
|
||||
Low = new BarLow(low),
|
||||
Close = new BarClose(close),
|
||||
};
|
||||
Volume = volume;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpandsNestedClassesIntoDataFrameColumns()
|
||||
{
|
||||
var converter = new PandasConverter();
|
||||
var data = new List<CustomTradeBar>
|
||||
{
|
||||
new CustomTradeBar(Symbols.IBM, 101m, 102m, 100m, 101m, 10m),
|
||||
new CustomTradeBar(Symbols.IBM, 102m, 103m, 101m, 101m, 9m),
|
||||
new CustomTradeBar(Symbols.IBM, 99m, 100m, 98m, 99m, 10m),
|
||||
};
|
||||
|
||||
dynamic dataFrame = converter.GetDataFrame(data);
|
||||
|
||||
var expectedColumnNames = new List<string>() { "open", "high", "low", "close", "volume" };
|
||||
|
||||
AssertDataFrameColumns(dataFrame, data.Count, expectedColumnNames);
|
||||
}
|
||||
|
||||
private class CustomQuoteBar : BaseData
|
||||
{
|
||||
public CustomBar Bid { get; set; }
|
||||
|
||||
public decimal BidSize { get; set; }
|
||||
|
||||
public CustomBar Ask { get; set; }
|
||||
|
||||
public decimal AskSize { get; set; }
|
||||
|
||||
public CustomQuoteBar(Symbol symbol, decimal bidOpen, decimal bidHigh, decimal bidLow, decimal bidClose, decimal bidSize,
|
||||
decimal askOpen, decimal askHigh, decimal askLow, decimal askClose, decimal askSize)
|
||||
{
|
||||
Symbol = symbol;
|
||||
Bid = new CustomBar
|
||||
{
|
||||
Open = new BarOpen(bidOpen),
|
||||
High = new BarHigh(bidHigh),
|
||||
Low = new BarLow(bidLow),
|
||||
Close = new BarClose(bidClose),
|
||||
};
|
||||
BidSize = bidSize;
|
||||
Ask = new CustomBar
|
||||
{
|
||||
Open = new BarOpen(askOpen),
|
||||
High = new BarHigh(askHigh),
|
||||
Low = new BarLow(askLow),
|
||||
Close = new BarClose(askClose),
|
||||
};
|
||||
AskSize = askSize;
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void ExpandsNestedClassesIntoDataFrameColumnsWithDuplicateNames()
|
||||
{
|
||||
var converter = new PandasConverter();
|
||||
var data = new List<CustomQuoteBar>
|
||||
{
|
||||
new CustomQuoteBar(Symbols.IBM, 101m, 102m, 100m, 101m, 10m, 101m, 102m, 100m, 101m, 10m),
|
||||
new CustomQuoteBar(Symbols.IBM, 102m, 103m, 101m, 101m, 9m, 102m, 103m, 101m, 101m, 9m),
|
||||
new CustomQuoteBar(Symbols.IBM, 99m, 100m, 98m, 99m, 10m, 99m, 100m, 98m, 99m, 10m),
|
||||
};
|
||||
|
||||
dynamic dataFrame = converter.GetDataFrame(data);
|
||||
|
||||
var expectedColumnNames = new List<string>() {
|
||||
"bidopen", "bidhigh", "bidlow", "bidclose", "bidsize",
|
||||
"askopen", "askhigh", "asklow", "askclose", "asksize"
|
||||
};
|
||||
|
||||
AssertDataFrameColumns(dataFrame, data.Count, expectedColumnNames);
|
||||
}
|
||||
|
||||
public class TestInnerData1
|
||||
{
|
||||
public decimal DecimalValue1 { get; set; }
|
||||
public string StringValue1 { get; set; }
|
||||
|
||||
}
|
||||
|
||||
[PandasNonExpandable]
|
||||
public class TestInnerData2
|
||||
{
|
||||
public decimal DecimalValue2 { get; set; }
|
||||
public string StringValue2 { get; set; }
|
||||
}
|
||||
|
||||
public class TestData1 : BaseData
|
||||
{
|
||||
public TestInnerData1 TestInnerData1 { get; set; }
|
||||
public TestInnerData2 TestInnerData2 { get; set; }
|
||||
|
||||
[PandasNonExpandable]
|
||||
public TestInnerData1 TestInnerData3 { get; set; }
|
||||
|
||||
public TestData1(Symbol symbol, decimal decimalValue1, string stringValue1, decimal decimalValue2, string stringValue2,
|
||||
decimal decimalValue3, string stringValue3)
|
||||
{
|
||||
Symbol = symbol;
|
||||
TestInnerData1 = new TestInnerData1
|
||||
{
|
||||
DecimalValue1 = decimalValue1,
|
||||
StringValue1 = stringValue1,
|
||||
};
|
||||
TestInnerData2 = new TestInnerData2
|
||||
{
|
||||
DecimalValue2 = decimalValue2,
|
||||
StringValue2 = stringValue2,
|
||||
};
|
||||
TestInnerData3 = new TestInnerData1
|
||||
{
|
||||
DecimalValue1 = decimalValue3,
|
||||
StringValue1 = stringValue3,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DoesNotExpandMarkedPropertiesAndClasses()
|
||||
{
|
||||
var converter = new PandasConverter();
|
||||
var data = new List<TestData1>
|
||||
{
|
||||
new TestData1(Symbols.IBM, 1m, "Test 1.1", 2m, "Test 1.2", 3m, "Test 1.3"),
|
||||
new TestData1(Symbols.IBM, 10m, "Test 2.1", 20m, "Test 2.2", 30m, "Test 2.3"),
|
||||
new TestData1(Symbols.IBM, 100m, "Test 3.1", 200m, "Test 3.2", 300m, "Test 3.3"),
|
||||
};
|
||||
|
||||
dynamic dataFrame = converter.GetDataFrame(data);
|
||||
|
||||
var expectedColumnNames = new List<string>() { "decimalvalue1", "stringvalue1", "testinnerdata2", "testinnerdata3" };
|
||||
|
||||
AssertDataFrameColumns(dataFrame, data.Count, expectedColumnNames);
|
||||
|
||||
using var _ = Py.GIL();
|
||||
|
||||
foreach (var value in dataFrame["testinnerdata2"])
|
||||
{
|
||||
Assert.DoesNotThrow(() => value.As<TestInnerData2>());
|
||||
}
|
||||
|
||||
foreach (var value in dataFrame["testinnerdata3"])
|
||||
{
|
||||
Assert.DoesNotThrow(() => value.As<TestInnerData1>());
|
||||
}
|
||||
}
|
||||
|
||||
private static void AssertDataFrameColumns(dynamic dataFrame, int dataCount, List<string> expectedColumnNames)
|
||||
{
|
||||
using var _ = Py.GIL();
|
||||
|
||||
Assert.AreEqual(dataCount, dataFrame.shape[0].As<int>());
|
||||
Assert.AreEqual(expectedColumnNames.Count, dataFrame.shape[1].As<int>());
|
||||
|
||||
var columnNames = new List<string>();
|
||||
foreach (var pandasColumn in dataFrame.columns.to_list())
|
||||
{
|
||||
columnNames.Add(pandasColumn.__str__().As<string>());
|
||||
}
|
||||
CollectionAssert.AreEquivalent(expectedColumnNames, columnNames);
|
||||
}
|
||||
|
||||
private class TestInnerData3
|
||||
{
|
||||
public decimal TestValue1 { get; set; }
|
||||
|
||||
public decimal TestValue2 { get; set; }
|
||||
|
||||
[PandasIgnore]
|
||||
public decimal IgnoredValue { get; set; }
|
||||
}
|
||||
|
||||
private class TestData2 : BaseData
|
||||
{
|
||||
public TestInnerData3 InnerData { get; set; }
|
||||
|
||||
public string MainValue { get; set; }
|
||||
|
||||
public TestData2(Symbol symbol, string mainValue, decimal testValue1, decimal testValue2, decimal ignoredValue)
|
||||
{
|
||||
Symbol = symbol;
|
||||
MainValue = mainValue;
|
||||
InnerData = new TestInnerData3()
|
||||
{
|
||||
TestValue1 = testValue1,
|
||||
TestValue2 = testValue2,
|
||||
IgnoredValue = ignoredValue
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OmitsPropertiesMarkedToBeIgnored()
|
||||
{
|
||||
var converter = new PandasConverter();
|
||||
var data = new List<TestData2>
|
||||
{
|
||||
new TestData2(Symbols.IBM, "Main value 1", 10m, 200m, 5m),
|
||||
new TestData2(Symbols.IBM, "Main value 2", 20m, 300m, 10m),
|
||||
new TestData2(Symbols.IBM, "Main value 3", 30m, 400m, 15m),
|
||||
};
|
||||
|
||||
dynamic dataFrame = converter.GetDataFrame(data);
|
||||
|
||||
var expectedColumnNames = new List<string>() { "mainvalue", "testvalue1", "testvalue2" };
|
||||
|
||||
AssertDataFrameColumns(dataFrame, data.Count, expectedColumnNames);
|
||||
}
|
||||
|
||||
[PandasIgnoreMembers]
|
||||
private class TestIgnoreMembersBaseClass : BaseData
|
||||
{
|
||||
public decimal Decimal1 { get; set; }
|
||||
public decimal Decimal2 { get; set; }
|
||||
}
|
||||
|
||||
private class TestIgnoreMembersDerivedClass : TestIgnoreMembersBaseClass
|
||||
{
|
||||
public decimal Decimal3 { get; set; }
|
||||
public decimal Decimal4 { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void OmitsBaseClassMembers()
|
||||
{
|
||||
var converter = new PandasConverter();
|
||||
var data = new List<TestIgnoreMembersDerivedClass>
|
||||
{
|
||||
new TestIgnoreMembersDerivedClass { Decimal1 = 1m, Decimal2 = 2m, Decimal3 = 3m, Decimal4 = 4m },
|
||||
new TestIgnoreMembersDerivedClass { Decimal1 = 10m, Decimal2 = 20m, Decimal3 = 30m, Decimal4 = 40m },
|
||||
new TestIgnoreMembersDerivedClass { Decimal1 = 100m, Decimal2 = 200m, Decimal3 = 300m, Decimal4 = 400m },
|
||||
};
|
||||
|
||||
dynamic dataFrame = converter.GetDataFrame(data);
|
||||
|
||||
// BaseData and TestIgnoreMembersBaseClass members are ignored
|
||||
var expectedColumnNames = new List<string>() { "decimal3", "decimal4" };
|
||||
|
||||
AssertDataFrameColumns(dataFrame, data.Count, expectedColumnNames);
|
||||
}
|
||||
|
||||
private class TestRenameMembersClass : ISymbolProvider
|
||||
{
|
||||
[PandasColumn("TheSymbol")]
|
||||
public Symbol Symbol { get; set; }
|
||||
|
||||
[PandasColumn("Decimal")]
|
||||
public decimal DecimalValue { get; set; }
|
||||
|
||||
[PandasColumn("String")]
|
||||
public string StringValue { get; set; }
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void RenamesMemberNames()
|
||||
{
|
||||
var converter = new PandasConverter();
|
||||
var data = new List<TestRenameMembersClass>
|
||||
{
|
||||
new TestRenameMembersClass { Symbol = Symbols.IBM, DecimalValue = 1m, StringValue = "Test 1" },
|
||||
new TestRenameMembersClass { Symbol = Symbols.AAPL, DecimalValue = 10m, StringValue = "Test 2" },
|
||||
new TestRenameMembersClass { Symbol = Symbols.SPY, DecimalValue = 100m, StringValue = "Test 3" },
|
||||
};
|
||||
|
||||
dynamic dataFrame = converter.GetDataFrame(data);
|
||||
|
||||
var expectedColumnNames = new List<string>() { "thesymbol", "decimal", "string" };
|
||||
|
||||
AssertDataFrameColumns(dataFrame, data.Count, expectedColumnNames);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user