Files
quantconnect--lean/Tests/Common/Util/ExtensionsTests.cs
Adalyat Nazirov a4f66628fd Lean Optimization interface in QCAlgorithm (#4923)
* initial commit

* run parametrized algorithm with command line parameters

* skeleton: top level structure

* OptimizationNodePacket scheme

* pass parameters as HashSet

* run Lean and read results

* call method on optimization completion

* refactor public interfaces

- close ParameterSet collection; allow only get operations
- explicit method to start LeanOptimizer

* synchronize RunLean method; the result could come in before the backtest id is set in the collections

* another portion of refactoring and interface changes

* comments

* comments & tests for Extremum, Minimization and Maximization classes

* unify optimization paramater values (min, max, step) & mode GridSearch tests

- swap min&max if necessary
- iterate left => right (negate step value if necessary) & provide default step value if step == 0
- no StackOverflow Exception
- parameterSet Id should be global for current generator and retain between steps
- test signle point boundary (min == max)

* BruteForceStrategy tests

* more comments

* Update Optimizer assembly information

- Update Optimizer projects assembly information to match behavior of
  the other projects

* Tweaks

- Adding comments
- Replace OnComplete for Ended event
- Replace Abort for Dispose
- ConsoleLeanOptimizer will keep track of running processes
- Each backtest will store results in a separated directory, so they
  don't fight for the log.txt file.
- Adding cmdline option for lean to close automatically
- Adding concurrent execution backtest limit
- Console optimizer will start Lean minimized
- Escape spaces in Json path

* remove parameter set generator abstraction layer

we don't need this flexibility now.

* refactor public methods; Step shouldn't be public

* constraints: wip

* define contract

* comparison operators and tests

* specify JsonProperty values

* Move SafeMultiply100 to extensions

* Throw exception on failed Optimizer.Start

* constraints: wip

* change finish & dispose process

* minor fixes

- handle force lean abort
- notify consumer if target has been reached

* target & constraints; adapt unit tests

* Minor Tweaks and fixes

- Some logging improvements
- Remove Public since not required

* Ignore empty ParameterValue

* simplify condition

* avoid reinitialization

* reduce type; force immutable

* unit tests for constraints  and target value

* parse & normalize percent values, i.e. 20% => 0.2

* fixup

* Target & Constraint & OptimizationNodePacket unit tests

* Add more json unit tests

- Adding more json conversion unit tests. Fix bug for Extremum which
  wasn't using the converter.

* LeanOptimizer tests

* Estimation results

* User thread safe counters

* LeanOptimizer unit tests; push OptimizationResult on Ended event

* more unit tests

* Minor tweaks

-Estimate ToString in a single line.
-Typos and missing header file

* Add base SendUpdate method

- Add base SendUpdate method for LeanOptimizer

* fix LeanOptimizer test; rely on internal Update rather than timer

* Add OptimizationStatus

- Add missing commments and OptimizationStatus

* EulerSearch implementation: wip

* OptimizationParameter custom converter

* change the type

* make step optional

* change folder structure

* enumerate optimization parameter using IEnumerable & IEnumerator

* unit tests: parameters & objectives

* unit tests: strategies

* remove redundant TODO

* change Euler search boundaries

* more Euler tests

* prevent race condition

* Add account/read endpoint

- Adding account/read endpoint. Adding unit test

* Add status check before running lean

* Minor self review

- Adding missing comments, minor changes

* remove array parameters

* minor changes

- tidy up config file, rename variable
- accept min less or equal than max

* move OptimizationParameter methods to strategies

* Minor improvements for BaseResultHandler derivates

* minor changes

- strict requirements for Step and MinStep values
- strategy specific settigs

* Add TotalRuntime to estimate

Co-authored-by: Martin Molinero <martin.molinero1@gmail.com>
2020-12-02 20:10:40 -03:00

1275 lines
50 KiB
C#

/*
* 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 System;
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using NodaTime;
using NUnit.Framework;
using Python.Runtime;
using QuantConnect.Algorithm;
using QuantConnect.Algorithm.Framework.Alphas;
using QuantConnect.Data;
using QuantConnect.Data.Auxiliary;
using QuantConnect.Data.Market;
using QuantConnect.Data.UniverseSelection;
using QuantConnect.Indicators;
using QuantConnect.Lean.Engine.DataFeeds;
using QuantConnect.Lean.Engine.HistoricalData;
using QuantConnect.Orders;
using QuantConnect.Orders.Fees;
using QuantConnect.Packets;
using QuantConnect.Scheduling;
using QuantConnect.Securities;
namespace QuantConnect.Tests.Common.Util
{
[TestFixture]
public class ExtensionsTests
{
[TestCase("A", "a")]
[TestCase("", "")]
[TestCase(null, null)]
[TestCase("Buy", "buy")]
[TestCase("BuyTheDip", "buyTheDip")]
public void ToCamelCase(string toConvert, string expected)
{
Assert.AreEqual(expected, toConvert.ToCamelCase());
}
[Test]
public void BatchAlphaResultPacket()
{
var btcusd = Symbol.Create("BTCUSD", SecurityType.Crypto, Market.GDAX);
var insights = new List<Insight>
{
new Insight(DateTime.UtcNow, btcusd, Time.OneMillisecond, InsightType.Price, InsightDirection.Up, 1, 2, "sourceModel1"),
new Insight(DateTime.UtcNow, btcusd, Time.OneSecond, InsightType.Price, InsightDirection.Down, 1, 2, "sourceModel1")
};
var orderEvents = new List<OrderEvent>
{
new OrderEvent(1, btcusd, DateTime.UtcNow, OrderStatus.Submitted, OrderDirection.Buy, 0, 0, OrderFee.Zero, message: "OrderEvent1"),
new OrderEvent(1, btcusd, DateTime.UtcNow, OrderStatus.Filled, OrderDirection.Buy, 1, 1000, OrderFee.Zero, message: "OrderEvent2")
};
var orders = new List<Order> { new MarketOrder(btcusd, 1000, DateTime.UtcNow, "ExpensiveOrder") { Id = 1 } };
var packet1 = new AlphaResultPacket("1", 1, insights: insights);
var packet2 = new AlphaResultPacket("1", 1, orders: orders);
var packet3 = new AlphaResultPacket("1", 1, orderEvents: orderEvents);
var result = new List<AlphaResultPacket> { packet1, packet2, packet3 }.Batch();
Assert.AreEqual(2, result.Insights.Count);
Assert.AreEqual(2, result.OrderEvents.Count);
Assert.AreEqual(1, result.Orders.Count);
Assert.IsTrue(result.Insights.SequenceEqual(insights));
Assert.IsTrue(result.OrderEvents.SequenceEqual(orderEvents));
Assert.IsTrue(result.Orders.SequenceEqual(orders));
Assert.IsNull(new List<AlphaResultPacket>().Batch());
}
[Test]
public void BatchAlphaResultPacketDuplicateOrder()
{
var btcusd = Symbol.Create("BTCUSD", SecurityType.Crypto, Market.GDAX);
var orders = new List<Order>
{
new MarketOrder(btcusd, 1000, DateTime.UtcNow, "ExpensiveOrder") { Id = 1 },
new MarketOrder(btcusd, 100, DateTime.UtcNow, "ExpensiveOrder") { Id = 2 },
new MarketOrder(btcusd, 2000, DateTime.UtcNow, "ExpensiveOrder") { Id = 1 },
new MarketOrder(btcusd, 10, DateTime.UtcNow, "ExpensiveOrder") { Id = 3 },
new MarketOrder(btcusd, 3000, DateTime.UtcNow, "ExpensiveOrder") { Id = 1 }
};
var orders2 = new List<Order>
{
new MarketOrder(btcusd, 200, DateTime.UtcNow, "ExpensiveOrder") { Id = 2 },
new MarketOrder(btcusd, 20, DateTime.UtcNow, "ExpensiveOrder") { Id = 3 }
};
var packet1 = new AlphaResultPacket("1", 1, orders: orders);
var packet2 = new AlphaResultPacket("1", 1, orders: orders2);
var result = new List<AlphaResultPacket> { packet1, packet2 }.Batch();
// we expect just 1 order instance per order id
Assert.AreEqual(3, result.Orders.Count);
Assert.IsTrue(result.Orders.Any(order => order.Id == 1 && order.Quantity == 3000));
Assert.IsTrue(result.Orders.Any(order => order.Id == 2 && order.Quantity == 200));
Assert.IsTrue(result.Orders.Any(order => order.Id == 3 && order.Quantity == 20));
var expected = new List<Order> { orders[4], orders2[0], orders2[1] };
Assert.IsTrue(result.Orders.SequenceEqual(expected));
}
[Test]
public void SeriesIsNotEmpty()
{
var series = new Series("SadSeries")
{ Values = new List<ChartPoint> { new ChartPoint(1, 1) } };
Assert.IsFalse(series.IsEmpty());
}
[Test]
public void SeriesIsEmpty()
{
Assert.IsTrue((new Series("Cat")).IsEmpty());
}
[Test]
public void ChartIsEmpty()
{
Assert.IsTrue((new Chart("HappyChart")).IsEmpty());
}
[Test]
public void ChartIsEmptyWithEmptySeries()
{
Assert.IsTrue((new Chart("HappyChart")
{ Series = new Dictionary<string, Series> { { "SadSeries", new Series("SadSeries") } }}).IsEmpty());
}
[Test]
public void ChartIsNotEmptyWithNonEmptySeries()
{
var series = new Series("SadSeries")
{ Values = new List<ChartPoint> { new ChartPoint(1, 1) } };
Assert.IsFalse((new Chart("HappyChart")
{ Series = new Dictionary<string, Series> { { "SadSeries", series } } }).IsEmpty());
}
[Test]
public void IsSubclassOfGenericWorksWorksForNonGenericType()
{
Assert.IsTrue(typeof(Derived2).IsSubclassOfGeneric(typeof(Derived1)));
}
[Test]
public void IsSubclassOfGenericWorksForGenericTypeWithParameter()
{
Assert.IsTrue(typeof(Derived1).IsSubclassOfGeneric(typeof(Super<int>)));
Assert.IsFalse(typeof(Derived1).IsSubclassOfGeneric(typeof(Super<bool>)));
}
[Test]
public void IsSubclassOfGenericWorksForGenericTypeDefinitions()
{
Assert.IsTrue(typeof(Derived1).IsSubclassOfGeneric(typeof(Super<>)));
Assert.IsTrue(typeof(Derived2).IsSubclassOfGeneric(typeof(Super<>)));
}
[Test]
public void DateTimeRoundDownFullDayDoesntRoundDownByDay()
{
var date = new DateTime(2000, 01, 01);
var rounded = date.RoundDown(TimeSpan.FromDays(1));
Assert.AreEqual(date, rounded);
}
[Test]
public void GetBetterTypeNameHandlesRecursiveGenericTypes()
{
var type = typeof (Dictionary<List<int>, Dictionary<int, string>>);
const string expected = "Dictionary<List<Int32>, Dictionary<Int32, String>>";
var actual = type.GetBetterTypeName();
Assert.AreEqual(expected, actual);
}
[Test]
public void ExchangeRoundDownSkipsWeekends()
{
var time = new DateTime(2015, 05, 02, 18, 01, 00);
var expected = new DateTime(2015, 05, 01);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.FXCM, null, SecurityType.Forex);
var exchangeRounded = time.ExchangeRoundDown(Time.OneDay, hours, false);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
public void ExchangeRoundDownHandlesMarketOpenTime()
{
var time = new DateTime(2016, 1, 25, 9, 31, 0);
var expected = time.Date;
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.USA, null, SecurityType.Equity);
var exchangeRounded = time.ExchangeRoundDown(Time.OneDay, hours, false);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
public void ConvertToSkipsDiscontinuitiesBecauseOfDaylightSavingsStart_AddingOneHour()
{
var expected = new DateTime(2014, 3, 9, 3, 0, 0);
var time = new DateTime(2014, 3, 9, 2, 0, 0).ConvertTo(TimeZones.NewYork, TimeZones.NewYork);
var time2 = new DateTime(2014, 3, 9, 2, 0, 1).ConvertTo(TimeZones.NewYork, TimeZones.NewYork);
Assert.AreEqual(expected, time);
Assert.AreEqual(expected, time2);
}
[Test]
public void ConvertToIgnoreDaylightSavingsEnd_SubtractingOneHour()
{
var time1Expected = new DateTime(2014, 11, 2, 1, 59, 59);
var time2Expected = new DateTime(2014, 11, 2, 2, 0, 0);
var time3Expected = new DateTime(2014, 11, 2, 2, 0, 1);
var time1 = time1Expected.ConvertTo(TimeZones.NewYork, TimeZones.NewYork);
var time2 = time2Expected.ConvertTo(TimeZones.NewYork, TimeZones.NewYork);
var time3 = time3Expected.ConvertTo(TimeZones.NewYork, TimeZones.NewYork);
Assert.AreEqual(time1Expected, time1);
Assert.AreEqual(time2Expected, time2);
Assert.AreEqual(time3Expected, time3);
}
[Test]
public void ExchangeRoundDownInTimeZoneSkipsWeekends()
{
// moment before EST market open in UTC (time + one day)
var time = new DateTime(2017, 10, 01, 9, 29, 59).ConvertToUtc(TimeZones.NewYork);
var expected = new DateTime(2017, 09, 29).ConvertFromUtc(TimeZones.NewYork);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.USA, null, SecurityType.Equity);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneDay, hours, TimeZones.Utc, false);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
// This unit test reproduces a fixed infinite loop situation, due to a daylight saving time change, in ExchangeRoundDownInTimeZone, GH issue 2368.
public void ExchangeRoundDownInTimeZoneCorrectValuesAroundDaylightTimeChanges_AddingOneHour_UTC()
{
var time = new DateTime(2014, 3, 9, 16, 0, 1);
var expected = new DateTime(2014, 3, 7, 16, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, null, SecurityType.Forex);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneHour, hours, TimeZones.Utc, false);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
// This unit test reproduces a fixed infinite loop situation, due to a daylight saving time change, in ExchangeRoundDownInTimeZone, GH issue 2368.
public void ExchangeRoundDownInTimeZoneCorrectValuesAroundDaylightTimeChanges_SubtractingOneHour_UTC()
{
var time = new DateTime(2014, 11, 2, 2, 0, 1);
var expected = new DateTime(2014, 10, 31, 16, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, null, SecurityType.Forex);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneHour, hours, TimeZones.Utc, false);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
public void ExchangeRoundDownInTimeZoneCorrectValuesAroundDaylightTimeChanges_AddingOneHour_ExtendedHours_UTC()
{
var time = new DateTime(2014, 3, 9, 2, 0, 1);
var expected = new DateTime(2014, 3, 9, 2, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.GDAX, null, SecurityType.Crypto);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneHour, hours, TimeZones.Utc, true);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
public void ExchangeRoundDownInTimeZoneCorrectValuesAroundDaylightTimeChanges_SubtractingOneHour_ExtendedHours_UTC()
{
var time = new DateTime(2014, 11, 2, 2, 0, 1);
var expected = new DateTime(2014, 11, 2, 2, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.GDAX, null, SecurityType.Crypto);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneHour, hours, TimeZones.Utc, true);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
// this unit test reproduces a fixed infinite loop situation, due to a daylight saving time change, GH issue 3707.
public void RoundDownInTimeZoneAroundDaylightTimeChanges()
{
// sydney time advanced Sunday, 6 October 2019, 02:00:00 clocks were turned forward 1 hour to
// Sunday, 6 October 2019, 03:00:00 local daylight time instead.
var timeAt = new DateTime(2019, 10, 6, 10, 0, 0);
var expected = new DateTime(2019, 10, 5, 10, 0, 0);
var exchangeRoundedAt = timeAt.RoundDownInTimeZone(Time.OneDay, TimeZones.Sydney, TimeZones.Utc);
// even though there is an entire 'roundingInterval' unit (1 day) between 'timeAt' and 'expected' round down
// is affected by daylight savings and rounds down the timeAt
Assert.AreEqual(expected, exchangeRoundedAt);
timeAt = new DateTime(2019, 10, 7, 10, 0, 0);
expected = new DateTime(2019, 10, 6, 11, 0, 0);
exchangeRoundedAt = timeAt.RoundDownInTimeZone(Time.OneDay, TimeZones.Sydney, TimeZones.Utc);
Assert.AreEqual(expected, exchangeRoundedAt);
}
[Test]
public void RoundDownInTimeZoneReturnsCorrectValuesAroundDaylightTimeChanges_AddingOneHour_UTC()
{
var timeAt = new DateTime(2014, 3, 9, 2, 0, 0);
var timeAfter = new DateTime(2014, 3, 9, 2, 0, 1);
var timeBefore = new DateTime(2014, 3, 9, 1, 59, 59);
var timeAfterDaylightTimeChanges = new DateTime(2014, 3, 9, 3, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, null, SecurityType.Forex);
var exchangeRoundedAt = timeAt.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.Utc);
var exchangeRoundedAfter = timeAfter.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.Utc);
var exchangeRoundedBefore = timeBefore.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.Utc);
var exchangeRoundedAfterDaylightTimeChanges = timeAfterDaylightTimeChanges.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.Utc);
var expected = new DateTime(2014, 3, 9, 3, 0, 0);
Assert.AreEqual(expected, exchangeRoundedAt);
Assert.AreEqual(expected, exchangeRoundedAfter);
Assert.AreEqual(timeBefore, exchangeRoundedBefore);
Assert.AreEqual(expected, exchangeRoundedAfterDaylightTimeChanges);
}
[Test]
public void RoundDownInTimeZoneReturnsCorrectValuesAroundDaylightTimeChanges_SubtractingOneHour_UTC()
{
var timeAt = new DateTime(2014, 11, 2, 2, 0, 0);
var timeAfter = new DateTime(2014, 11, 2, 2, 0, 1);
var timeBefore = new DateTime(2014, 11, 2, 1, 59, 59);
var timeAfterDaylightTimeChanges = new DateTime(2014, 11, 2, 3, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, null, SecurityType.Forex);
var exchangeRoundedAt = timeAt.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.Utc);
var exchangeRoundedAfter = timeAfter.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.Utc);
var exchangeRoundedBefore = timeBefore.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.Utc);
var exchangeRoundedAfterDaylightTimeChanges = timeAfterDaylightTimeChanges.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.Utc);
Assert.AreEqual(timeAt, exchangeRoundedAt);
Assert.AreEqual(timeAfter, exchangeRoundedAfter);
Assert.AreEqual(timeBefore, exchangeRoundedBefore);
Assert.AreEqual(timeAfterDaylightTimeChanges, exchangeRoundedAfterDaylightTimeChanges);
}
[Test]
public void ExchangeRoundDownInTimeZoneCorrectValuesAroundDaylightTimeChanges_AddingOneHour_NewYork()
{
var time = new DateTime(2014, 3, 9, 16, 0, 1);
var expected = new DateTime(2014, 3, 7, 16, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, null, SecurityType.Forex);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneHour, hours, TimeZones.NewYork, false);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
public void ExchangeRoundDownInTimeZoneCorrectValuesAroundDaylightTimeChanges_SubtractingOneHour_NewYork()
{
var time = new DateTime(2014, 11, 2, 2, 0, 1);
var expected = new DateTime(2014, 10, 31, 16, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, null, SecurityType.Forex);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneHour, hours, TimeZones.NewYork, false);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
public void ExchangeRoundDownInTimeZoneCorrectValuesAroundDaylightTimeChanges_AddingOneHour_ExtendedHours_NewYork()
{
var time = new DateTime(2014, 3, 9, 2, 0, 1);
var expected = new DateTime(2014, 3, 9, 2, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.GDAX, null, SecurityType.Crypto);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneHour, hours, TimeZones.NewYork, true);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
public void ExchangeRoundDownInTimeZoneCorrectValuesAroundDaylightTimeChanges_SubtractingOneHour_ExtendedHours_NewYork()
{
var time = new DateTime(2014, 11, 2, 2, 0, 1);
var expected = new DateTime(2014, 11, 2, 2, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.GDAX, null, SecurityType.Crypto);
var exchangeRounded = time.ExchangeRoundDownInTimeZone(Time.OneHour, hours, TimeZones.NewYork, true);
Assert.AreEqual(expected, exchangeRounded);
}
[Test]
public void RoundDownInTimeZoneReturnsCorrectValuesAroundDaylightTimeChanges_AddingOneHour_NewYork()
{
var timeAt = new DateTime(2014, 3, 9, 2, 0, 0);
var timeAfter = new DateTime(2014, 3, 9, 2, 0, 1);
var timeBefore = new DateTime(2014, 3, 9, 1, 59, 59);
var timeAfterDaylightTimeChanges = new DateTime(2014, 3, 9, 3, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, null, SecurityType.Forex);
var exchangeRoundedAt = timeAt.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.NewYork);
var exchangeRoundedAfter = timeAfter.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.NewYork);
var exchangeRoundedBefore = timeBefore.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.NewYork);
var exchangeRoundedAfterDaylightTimeChanges = timeAfterDaylightTimeChanges.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.NewYork);
var expected = new DateTime(2014, 3, 9, 3, 0, 0);
Assert.AreEqual(expected, exchangeRoundedAt);
Assert.AreEqual(expected, exchangeRoundedAfter);
Assert.AreEqual(timeBefore, exchangeRoundedBefore);
Assert.AreEqual(expected, exchangeRoundedAfterDaylightTimeChanges);
}
[Test]
public void RoundDownInTimeZoneReturnsCorrectValuesAroundDaylightTimeChanges_SubtractingOneHour_NewYork()
{
var timeAt = new DateTime(2014, 11, 2, 2, 0, 0);
var timeAfter = new DateTime(2014, 11, 2, 2, 0, 1);
var timeBefore = new DateTime(2014, 11, 2, 1, 59, 59);
var timeAfterDaylightTimeChanges = new DateTime(2014, 11, 2, 3, 0, 0);
var hours = MarketHoursDatabase.FromDataFolder().GetExchangeHours(Market.Oanda, null, SecurityType.Forex);
var exchangeRoundedAt = timeAt.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.NewYork);
var exchangeRoundedAfter = timeAfter.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.NewYork);
var exchangeRoundedBefore = timeBefore.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.NewYork);
var exchangeRoundedAfterDaylightTimeChanges = timeAfterDaylightTimeChanges.RoundDownInTimeZone(Time.OneSecond, hours.TimeZone, TimeZones.NewYork);
Assert.AreEqual(timeAt, exchangeRoundedAt);
Assert.AreEqual(timeAfter, exchangeRoundedAfter);
Assert.AreEqual(timeBefore, exchangeRoundedBefore);
Assert.AreEqual(timeAfterDaylightTimeChanges, exchangeRoundedAfterDaylightTimeChanges);
}
[Test]
public void ConvertsInt32FromString()
{
const string input = "12345678";
var value = input.ToInt32();
Assert.AreEqual(12345678, value);
}
[Test]
public void ConvertsInt32FromStringWithDecimalTruncation()
{
const string input = "12345678.9";
var value = input.ToInt32();
Assert.AreEqual(12345678, value);
}
[Test]
public void ConvertsInt64FromString()
{
const string input = "12345678900";
var value = input.ToInt64();
Assert.AreEqual(12345678900, value);
}
[Test]
public void ConvertsInt64FromStringWithDecimalTruncation()
{
const string input = "12345678900.12";
var value = input.ToInt64();
Assert.AreEqual(12345678900, value);
}
[Test]
public void ToCsvDataParsesCorrectly()
{
var csv = "\"hello\",\"world\"".ToCsvData();
Assert.AreEqual(2, csv.Count);
Assert.AreEqual("\"hello\"", csv[0]);
Assert.AreEqual("\"world\"", csv[1]);
var csv2 = "1,2,3,4".ToCsvData();
Assert.AreEqual(4, csv2.Count);
Assert.AreEqual("1", csv2[0]);
Assert.AreEqual("2", csv2[1]);
Assert.AreEqual("3", csv2[2]);
Assert.AreEqual("4", csv2[3]);
}
[Test]
public void ToCsvDataParsesEmptyFinalValue()
{
var line = "\"hello\",world,";
var csv = line.ToCsvData();
Assert.AreEqual(3, csv.Count);
Assert.AreEqual("\"hello\"", csv[0]);
Assert.AreEqual("hello", csv[0].Trim('"'));
Assert.AreEqual("world", csv[1]);
Assert.AreEqual(string.Empty, csv[2]);
}
[Test]
public void ToCsvDataParsesEmptyValue()
{
Assert.AreEqual(string.Empty, string.Empty.ToCsvData()[0]);
}
[Test]
public void ConvertsDecimalFromString()
{
const string input = "123.45678";
var value = input.ToDecimal();
Assert.AreEqual(123.45678m, value);
}
[Test]
public void ConvertsDecimalFromStringWithExtraWhiteSpace()
{
const string input = " 123.45678 ";
var value = input.ToDecimal();
Assert.AreEqual(123.45678m, value);
}
[Test]
public void ConvertsDecimalFromIntStringWithExtraWhiteSpace()
{
const string input = " 12345678 ";
var value = input.ToDecimal();
Assert.AreEqual(12345678m, value);
}
[Test]
public void ConvertsZeroDecimalFromString()
{
const string input = "0.45678";
var value = input.ToDecimal();
Assert.AreEqual(0.45678m, value);
}
[Test]
public void ConvertsOneNumberDecimalFromString()
{
const string input = "1.45678";
var value = input.ToDecimal();
Assert.AreEqual(1.45678m, value);
}
[Test]
public void ConvertsZeroDecimalValueFromString()
{
const string input = "0";
var value = input.ToDecimal();
Assert.AreEqual(0m, value);
}
[Test]
public void ConvertsEmptyDecimalValueFromString()
{
const string input = "";
var value = input.ToDecimal();
Assert.AreEqual(0m, value);
}
[Test]
public void ConvertsNegativeDecimalFromString()
{
const string input = "-123.45678";
var value = input.ToDecimal();
Assert.AreEqual(-123.45678m, value);
}
[Test]
public void ConvertsNegativeDecimalFromStringWithExtraWhiteSpace()
{
const string input = " -123.45678 ";
var value = input.ToDecimal();
Assert.AreEqual(-123.45678m, value);
}
[Test]
public void ConvertsNegativeDecimalFromIntStringWithExtraWhiteSpace()
{
const string input = " -12345678 ";
var value = input.ToDecimal();
Assert.AreEqual(-12345678m, value);
}
[Test]
public void ConvertsNegativeZeroDecimalFromString()
{
const string input = "-0.45678";
var value = input.ToDecimal();
Assert.AreEqual(-0.45678m, value);
}
[Test]
public void ConvertsNegavtiveOneNumberDecimalFromString()
{
const string input = "-1.45678";
var value = input.ToDecimal();
Assert.AreEqual(-1.45678m, value);
}
[Test]
public void ConvertsNegativeZeroDecimalValueFromString()
{
const string input = "-0";
var value = input.ToDecimal();
Assert.AreEqual(-0m, value);
}
[TestCase("1.23%", 0.0123d)]
[TestCase("-1.23%", -0.0123d)]
[TestCase("31.2300%", 0.3123d)]
[TestCase("20%", 0.2d)]
[TestCase("-20%", -0.2d)]
[TestCase("220%", 2.2d)]
public void ConvertsPercent(string input, double expected)
{
Assert.AreEqual(new decimal(expected), input.ToNormalizedDecimal());
}
[Test]
public void ConvertsTimeSpanFromString()
{
const string input = "16:00";
var timespan = input.ConvertTo<TimeSpan>();
Assert.AreEqual(TimeSpan.FromHours(16), timespan);
}
[Test]
public void ConvertsDictionaryFromString()
{
var expected = new Dictionary<string, int> {{"a", 1}, {"b", 2}};
var input = JsonConvert.SerializeObject(expected);
var actual = input.ConvertTo<Dictionary<string, int>>();
CollectionAssert.AreEqual(expected, actual);
}
[Test]
public void DictionaryAddsItemToExistsList()
{
const int key = 0;
var list = new List<int> {1, 2};
var dictionary = new Dictionary<int, List<int>> {{key, list}};
Extensions.Add(dictionary, key, 3);
Assert.AreEqual(3, list.Count);
Assert.AreEqual(3, list[2]);
}
[Test]
public void DictionaryAddCreatesNewList()
{
const int key = 0;
var dictionary = new Dictionary<int, List<int>>();
Extensions.Add(dictionary, key, 1);
Assert.IsTrue(dictionary.ContainsKey(key));
var list = dictionary[key];
Assert.AreEqual(1, list.Count);
Assert.AreEqual(1, list[0]);
}
[Test]
public void SafeDecimalCasts()
{
var input = 2d;
var output = input.SafeDecimalCast();
Assert.AreEqual(2m, output);
}
[Test]
public void SafeDecimalCastRespectsUpperBound()
{
var input = (double) decimal.MaxValue;
var output = input.SafeDecimalCast();
Assert.AreEqual(decimal.MaxValue, output);
}
[Test]
public void SafeDecimalCastRespectsLowerBound()
{
var input = (double) decimal.MinValue;
var output = input.SafeDecimalCast();
Assert.AreEqual(decimal.MinValue, output);
}
[TestCase(Language.CSharp, double.NaN)]
[TestCase(Language.Python, double.NaN)]
[TestCase(Language.CSharp, double.NegativeInfinity)]
[TestCase(Language.Python, double.NegativeInfinity)]
[TestCase(Language.CSharp, double.PositiveInfinity)]
[TestCase(Language.Python, double.PositiveInfinity)]
public void SafeDecimalCastThrowsArgumentException(Language language, double number)
{
if (language == Language.CSharp)
{
Assert.Throws<ArgumentException>(() => number.SafeDecimalCast());
return;
}
using (Py.GIL())
{
var pyNumber = number.ToPython();
var csNumber = pyNumber.As<double>();
Assert.Throws<ArgumentException>(() => csNumber.SafeDecimalCast());
}
}
[Test]
[TestCase(1.200, "1.2")]
[TestCase(1200, "1200")]
[TestCase(123.456, "123.456")]
public void NormalizeDecimalReturnsNoTrailingZeros(decimal input, string expectedOutput)
{
var output = input.Normalize();
Assert.AreEqual(expectedOutput, output.ToStringInvariant());
}
[Test]
[TestCase(0.072842, 3, "0.0728")]
[TestCase(0.0019999, 2, "0.002")]
[TestCase(0.01234568423, 6, "0.0123457")]
public void RoundToSignificantDigits(double input, int digits, string expectedOutput)
{
var output = input.RoundToSignificantDigits(digits).ToStringInvariant();
Assert.AreEqual(expectedOutput, output);
}
[Test]
public void RoundsDownInTimeZone()
{
var dataTimeZone = TimeZones.Utc;
var exchangeTimeZone = TimeZones.EasternStandard;
var time = new DateTime(2000, 01, 01).ConvertTo(dataTimeZone, exchangeTimeZone);
var roundedTime = time.RoundDownInTimeZone(Time.OneDay, exchangeTimeZone, dataTimeZone);
Assert.AreEqual(time, roundedTime);
}
[Test]
public void GetStringBetweenCharsTests()
{
const string expected = "python3.6";
// Different characters cases
var input = "[ python3.6 ]";
var actual = input.GetStringBetweenChars('[', ']');
Assert.AreEqual(expected, actual);
input = "[ python3.6 ] [ python2.7 ]";
actual = input.GetStringBetweenChars('[', ']');
Assert.AreEqual(expected, actual);
input = "[ python2.7 [ python3.6 ] ]";
actual = input.GetStringBetweenChars('[', ']');
Assert.AreEqual(expected, actual);
// Same character cases
input = "\'python3.6\'";
actual = input.GetStringBetweenChars('\'', '\'');
Assert.AreEqual(expected, actual);
input = "\' python3.6 \' \' python2.7 \'";
actual = input.GetStringBetweenChars('\'', '\'');
Assert.AreEqual(expected, actual);
// In this case, it is not equal
input = "\' python2.7 \' python3.6 \' \'";
actual = input.GetStringBetweenChars('\'', '\'');
Assert.AreNotEqual(expected, actual);
}
[Test]
public void PyObjectTryConvertQuoteBar()
{
// Wrap a QuoteBar around a PyObject and convert it back
var value = ConvertToPyObject(new QuoteBar());
QuoteBar quoteBar;
var canConvert = value.TryConvert(out quoteBar);
Assert.IsTrue(canConvert);
Assert.IsNotNull(quoteBar);
Assert.IsAssignableFrom<QuoteBar>(quoteBar);
}
[Test]
public void PyObjectTryConvertSMA()
{
// Wrap a SimpleMovingAverage around a PyObject and convert it back
var value = ConvertToPyObject(new SimpleMovingAverage(14));
IndicatorBase<IndicatorDataPoint> indicatorBaseDataPoint;
var canConvert = value.TryConvert(out indicatorBaseDataPoint);
Assert.IsTrue(canConvert);
Assert.IsNotNull(indicatorBaseDataPoint);
Assert.IsAssignableFrom<SimpleMovingAverage>(indicatorBaseDataPoint);
}
[Test]
public void PyObjectTryConvertATR()
{
// Wrap a AverageTrueRange around a PyObject and convert it back
var value = ConvertToPyObject(new AverageTrueRange(14, MovingAverageType.Simple));
IndicatorBase<IBaseDataBar> indicatorBaseDataBar;
var canConvert = value.TryConvert(out indicatorBaseDataBar);
Assert.IsTrue(canConvert);
Assert.IsNotNull(indicatorBaseDataBar);
Assert.IsAssignableFrom<AverageTrueRange>(indicatorBaseDataBar);
}
[Test]
public void PyObjectTryConvertAD()
{
// Wrap a AccumulationDistribution around a PyObject and convert it back
var value = ConvertToPyObject(new AccumulationDistribution("AD"));
IndicatorBase<TradeBar> indicatorBaseTradeBar;
var canConvert = value.TryConvert(out indicatorBaseTradeBar);
Assert.IsTrue(canConvert);
Assert.IsNotNull(indicatorBaseTradeBar);
Assert.IsAssignableFrom<AccumulationDistribution>(indicatorBaseTradeBar);
}
[Test]
public void PyObjectTryConvertSymbolArray()
{
PyObject value;
using (Py.GIL())
{
// Wrap a Symbol Array around a PyObject and convert it back
value = new PyList(new[] { Symbols.SPY.ToPython(), Symbols.AAPL.ToPython() });
}
Symbol[] symbols;
var canConvert = value.TryConvert(out symbols);
Assert.IsTrue(canConvert);
Assert.IsNotNull(symbols);
Assert.IsAssignableFrom<Symbol[]>(symbols);
}
[Test]
public void PyObjectTryConvertFailCSharp()
{
// Try to convert a AccumulationDistribution as a QuoteBar
var value = ConvertToPyObject(new AccumulationDistribution("AD"));
QuoteBar quoteBar;
bool canConvert = value.TryConvert(out quoteBar);
Assert.IsFalse(canConvert);
Assert.IsNull(quoteBar);
}
[Test]
public void PyObjectTryConvertFailPython()
{
using (Py.GIL())
{
// Try to convert a python object as a IndicatorBase<TradeBar>
var locals = new PyDict();
PythonEngine.Exec("class A:\n pass", null, locals.Handle);
var value = locals.GetItem("A").Invoke();
IndicatorBase<TradeBar> indicatorBaseTradeBar;
bool canConvert = value.TryConvert(out indicatorBaseTradeBar);
Assert.IsFalse(canConvert);
Assert.IsNull(indicatorBaseTradeBar);
}
}
[Test]
[TestCase("coarseSelector = lambda coarse: [ x.Symbol for x in coarse if x.Price % 2 == 0 ]")]
[TestCase("def coarseSelector(coarse): return [ x.Symbol for x in coarse if x.Price % 2 == 0 ]")]
public void PyObjectTryConvertToFunc(string code)
{
Func<IEnumerable<CoarseFundamental>, Symbol[]> coarseSelector;
using (Py.GIL())
{
var locals = new PyDict();
PythonEngine.Exec(code, null, locals.Handle);
var pyObject = locals.GetItem("coarseSelector");
pyObject.TryConvertToDelegate(out coarseSelector);
}
var coarse = Enumerable
.Range(0, 9)
.Select(x => new CoarseFundamental { Symbol = Symbol.Create(x.ToStringInvariant(), SecurityType.Equity, Market.USA), Value = x });
var symbols = coarseSelector(coarse);
Assert.AreEqual(5, symbols.Length);
foreach (var symbol in symbols)
{
var price = symbol.Value.ConvertInvariant<int>();
Assert.AreEqual(0, price % 2);
}
}
[Test]
public void PyObjectTryConvertToAction1()
{
Action<int> action;
using (Py.GIL())
{
var locals = new PyDict();
PythonEngine.Exec("def raise_number(a): raise ValueError(a)", null, locals.Handle);
var pyObject = locals.GetItem("raise_number");
pyObject.TryConvertToDelegate(out action);
}
try
{
action(2);
Assert.Fail();
}
catch (PythonException e)
{
Assert.AreEqual($"ValueError : {2}", e.Message);
}
}
[Test]
public void PyObjectTryConvertToAction2()
{
Action<int, decimal> action;
using (Py.GIL())
{
var locals = new PyDict();
PythonEngine.Exec("def raise_number(a, b): raise ValueError(a * b)", null, locals.Handle);
var pyObject = locals.GetItem("raise_number");
pyObject.TryConvertToDelegate(out action);
}
try
{
action(2, 3m);
Assert.Fail();
}
catch (PythonException e)
{
Assert.AreEqual("ValueError : 6.0", e.Message);
}
}
[Test]
public void PyObjectTryConvertToNonDelegateFail()
{
int action;
using (Py.GIL())
{
var locals = new PyDict();
PythonEngine.Exec("def raise_number(a, b): raise ValueError(a * b)", null, locals.Handle);
var pyObject = locals.GetItem("raise_number");
Assert.Throws<ArgumentException>(() => pyObject.TryConvertToDelegate(out action));
}
}
[Test]
public void PyObjectStringConvertToSymbolEnumerable()
{
SymbolCache.Clear();
SymbolCache.Set("SPY", Symbols.SPY);
IEnumerable<Symbol> symbols;
using (Py.GIL())
{
symbols = new PyString("SPY").ConvertToSymbolEnumerable();
}
Assert.AreEqual(Symbols.SPY, symbols.Single());
}
[Test]
public void PyObjectStringListConvertToSymbolEnumerable()
{
SymbolCache.Clear();
SymbolCache.Set("SPY", Symbols.SPY);
IEnumerable<Symbol> symbols;
using (Py.GIL())
{
symbols = new PyList(new[] { "SPY".ToPython() }).ConvertToSymbolEnumerable();
}
Assert.AreEqual(Symbols.SPY, symbols.Single());
}
[Test]
public void PyObjectSymbolConvertToSymbolEnumerable()
{
IEnumerable<Symbol> symbols;
using (Py.GIL())
{
symbols = Symbols.SPY.ToPython().ConvertToSymbolEnumerable();
}
Assert.AreEqual(Symbols.SPY, symbols.Single());
}
[Test]
public void PyObjectSymbolListConvertToSymbolEnumerable()
{
IEnumerable<Symbol> symbols;
using (Py.GIL())
{
symbols = new PyList(new[] {Symbols.SPY.ToPython()}).ConvertToSymbolEnumerable();
}
Assert.AreEqual(Symbols.SPY, symbols.Single());
}
[Test]
public void PyObjectNonSymbolObjectConvertToSymbolEnumerable()
{
using (Py.GIL())
{
Assert.Throws<ArgumentException>(() => new PyInt(1).ConvertToSymbolEnumerable().ToList());
}
}
[Test]
public void PyObjectDictionaryConvertToDictionary_Success()
{
using (Py.GIL())
{
var actualDictionary = PythonEngine.ModuleFromString(
"PyObjectDictionaryConvertToDictionary_Success",
@"
from datetime import datetime as dt
actualDictionary = dict()
actualDictionary.update({'SPY': dt(2019,10,3)})
actualDictionary.update({'QQQ': dt(2019,10,4)})
actualDictionary.update({'IBM': dt(2019,10,5)})
"
).GetAttr("actualDictionary").ConvertToDictionary<string, DateTime>();
Assert.AreEqual(3, actualDictionary.Count);
var expectedDictionary = new Dictionary<string, DateTime>
{
{"SPY", new DateTime(2019,10,3) },
{"QQQ", new DateTime(2019,10,4) },
{"IBM", new DateTime(2019,10,5) },
};
foreach (var kvp in expectedDictionary)
{
Assert.IsTrue(actualDictionary.ContainsKey(kvp.Key));
var actual = actualDictionary[kvp.Key];
Assert.AreEqual(kvp.Value, actual);
}
}
}
[Test]
public void PyObjectDictionaryConvertToDictionary_FailNotDictionary()
{
using (Py.GIL())
{
var pyObject = PythonEngine.ModuleFromString(
"PyObjectDictionaryConvertToDictionary_FailNotDictionary",
"actualDictionary = list()"
).GetAttr("actualDictionary");
Assert.Throws<ArgumentException>(() => pyObject.ConvertToDictionary<string, DateTime>());
}
}
[Test]
public void PyObjectDictionaryConvertToDictionary_FailWrongItemType()
{
using (Py.GIL())
{
var pyObject = PythonEngine.ModuleFromString(
"PyObjectDictionaryConvertToDictionary_FailWrongItemType",
@"
actualDictionary = dict()
actualDictionary.update({'SPY': 3})
actualDictionary.update({'QQQ': 4})
actualDictionary.update({'IBM': 5})
"
).GetAttr("actualDictionary");
Assert.Throws<ArgumentException>(() => pyObject.ConvertToDictionary<string, DateTime>());
}
}
[Test]
public void BatchByDoesNotDropItems()
{
var list = new List<int> {1, 2, 3, 4, 5};
var by2 = list.BatchBy(2).ToList();
Assert.AreEqual(3, by2.Count);
Assert.AreEqual(2, by2[0].Count);
Assert.AreEqual(2, by2[1].Count);
Assert.AreEqual(1, by2[2].Count);
CollectionAssert.AreEqual(list, by2.SelectMany(x => x));
}
[Test]
public void ToOrderTicketCreatesCorrectTicket()
{
var orderRequest = new SubmitOrderRequest(OrderType.Limit, SecurityType.Equity, Symbols.USDJPY, 1000, 0, 1.11m, DateTime.Now, "Pepe");
var order = Order.CreateOrder(orderRequest);
order.Status = OrderStatus.Submitted;
order.Id = 11;
var orderTicket = order.ToOrderTicket(null);
Assert.AreEqual(order.Id, orderTicket.OrderId);
Assert.AreEqual(order.Quantity, orderTicket.Quantity);
Assert.AreEqual(order.Status, orderTicket.Status);
Assert.AreEqual(order.Type, orderTicket.OrderType);
Assert.AreEqual(order.Symbol, orderTicket.Symbol);
Assert.AreEqual(order.Tag, orderTicket.Tag);
Assert.AreEqual(order.Time, orderTicket.Time);
Assert.AreEqual(order.SecurityType, orderTicket.SecurityType);
}
[Test]
public void DecimalTruncateTo3DecimalPlaces()
{
var value = 10.999999m;
Assert.AreEqual(10.999m, value.TruncateTo3DecimalPlaces());
}
[Test]
public void DecimalTruncateTo3DecimalPlacesDoesNotThrowException()
{
var value = decimal.MaxValue;
Assert.DoesNotThrow(() => value.TruncateTo3DecimalPlaces());
value = decimal.MinValue;
Assert.DoesNotThrow(() => value.TruncateTo3DecimalPlaces());
value = decimal.MaxValue - 1;
Assert.DoesNotThrow(() => value.TruncateTo3DecimalPlaces());
value = decimal.MinValue + 1;
Assert.DoesNotThrow(() => value.TruncateTo3DecimalPlaces());
}
[Test]
public void DecimalAllowExponentTests()
{
const string strWithExponent = "5e-5";
Assert.AreEqual(strWithExponent.ToDecimalAllowExponent(), 0.00005);
Assert.AreNotEqual(strWithExponent.ToDecimal(), 0.00005);
Assert.AreEqual(strWithExponent.ToDecimal(), 10275);
}
[Test]
public void DateRulesToFunc()
{
var dateRules = new DateRules(new SecurityManager(
new TimeKeeper(new DateTime(2015, 1, 1), DateTimeZone.Utc)), DateTimeZone.Utc);
var first = new DateTime(2015, 1, 10);
var second = new DateTime(2015, 1, 30);
var dateRule = dateRules.On(first, second);
var func = dateRule.ToFunc();
Assert.AreEqual(first, func(new DateTime(2015, 1, 1)));
Assert.AreEqual(first, func(new DateTime(2015, 1, 5)));
Assert.AreEqual(second, func(first));
Assert.AreEqual(Time.EndOfTime, func(second));
Assert.AreEqual(Time.EndOfTime, func(second));
}
[Test]
[TestCase(OptionRight.Call, true, OrderDirection.Sell)]
[TestCase(OptionRight.Call, false, OrderDirection.Buy)]
[TestCase(OptionRight.Put, true, OrderDirection.Buy)]
[TestCase(OptionRight.Put, false, OrderDirection.Sell)]
public void GetsExerciseDirection(OptionRight right, bool isShort, OrderDirection expected)
{
var actual = right.GetExerciseDirection(isShort);
Assert.AreEqual(expected, actual);
}
[Test]
public void AppliesScalingToEquityTickQuotes()
{
// This test ensures that all Ticks with TickType == TickType.Quote have adjusted BidPrice and AskPrice.
// Relevant issue: https://github.com/QuantConnect/Lean/issues/4788
var algo = new QCAlgorithm();
var dataFeed = new NullDataFeed();
algo.SubscriptionManager = new SubscriptionManager();
algo.SubscriptionManager.SetDataManager(new DataManager(
dataFeed,
new UniverseSelection(
algo,
new SecurityService(
new CashBook(),
MarketHoursDatabase.FromDataFolder(),
SymbolPropertiesDatabase.FromDataFolder(),
algo,
null,
null
),
new DataPermissionManager(),
new DefaultDataProvider()
),
algo,
new TimeKeeper(DateTime.UtcNow),
MarketHoursDatabase.FromDataFolder(),
false,
null,
new DataPermissionManager()
));
using (var zipDataCacheProvider = new ZipDataCacheProvider(new DefaultDataProvider()))
{
algo.HistoryProvider = new SubscriptionDataReaderHistoryProvider();
algo.HistoryProvider.Initialize(
new HistoryProviderInitializeParameters(
null,
null,
null,
zipDataCacheProvider,
new LocalDiskMapFileProvider(),
new LocalDiskFactorFileProvider(),
(_) => {},
false,
new DataPermissionManager()));
algo.SetStartDate(DateTime.UtcNow.AddDays(-1));
var history = algo.History(new[] { Symbols.IBM }, new DateTime(2013, 10, 7), new DateTime(2013, 10, 8), Resolution.Tick).ToList();
Assert.AreEqual(57401, history.Count);
foreach (var slice in history)
{
if (!slice.Ticks.ContainsKey(Symbols.IBM))
{
continue;
}
foreach (var tick in slice.Ticks[Symbols.IBM])
{
if (tick.BidPrice != 0)
{
Assert.LessOrEqual(Math.Abs(tick.Value - tick.BidPrice), 0.05);
}
if (tick.AskPrice != 0)
{
Assert.LessOrEqual(Math.Abs(tick.Value - tick.AskPrice), 0.05);
}
}
}
}
}
private PyObject ConvertToPyObject(object value)
{
using (Py.GIL())
{
return value.ToPython();
}
}
private class Super<T>
{
}
private class Derived1 : Super<int>
{
}
private class Derived2 : Derived1
{
}
}
}