Skip to content

Commit

Permalink
Fix tests and small refactoring to avoid duplicate code (#5)
Browse files Browse the repository at this point in the history
* Fix and cleanup tests

* Address review
  • Loading branch information
arodus authored Nov 6, 2023
1 parent 1b3874a commit 2ec4892
Show file tree
Hide file tree
Showing 6 changed files with 121 additions and 345 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -54,10 +54,10 @@ private static TestCaseData[] ValidHistory
return new[]
{
// valid
new TestCaseData(ETHUSDT, Resolution.Tick, Time.OneMinute, TickType.Trade, false),
new TestCaseData(ETHUSDT, Resolution.Minute, Time.OneHour, TickType.Trade, false),
new TestCaseData(ETHUSDT, Resolution.Hour, Time.OneDay, TickType.Trade, false),
new TestCaseData(ETHUSDT, Resolution.Daily, TimeSpan.FromDays(15), TickType.Trade, false),
new TestCaseData(ETHUSDT, Resolution.Tick, Time.OneMinute, TickType.Trade),
new TestCaseData(ETHUSDT, Resolution.Minute, Time.OneHour, TickType.Trade),
new TestCaseData(ETHUSDT, Resolution.Hour, Time.OneDay, TickType.Trade),
new TestCaseData(ETHUSDT, Resolution.Daily, TimeSpan.FromDays(15), TickType.Trade),
};
}
}
Expand Down Expand Up @@ -98,122 +98,113 @@ private static TestCaseData[] InvalidHistory

[Test]
[TestCaseSource(nameof(ValidHistory))]
public virtual void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, TickType tickType,
bool throwsException)
public virtual void GetsHistory(Symbol symbol, Resolution resolution, TimeSpan period, TickType tickType)
{
BaseHistoryTest(_brokerage, symbol, resolution, period, tickType, throwsException, false);
BaseHistoryTest(_brokerage, symbol, resolution, period, tickType, false);
}

[Test]
[TestCaseSource(nameof(NoHistory))]
[TestCaseSource(nameof(InvalidHistory))]
public virtual void GetEmptyHistory(Symbol symbol, Resolution resolution, TimeSpan period, TickType tickType)
{
BaseHistoryTest(_brokerage, symbol, resolution, period, tickType, false, true);
BaseHistoryTest(_brokerage, symbol, resolution, period, tickType, true);
}

protected static void BaseHistoryTest(Brokerage brokerage, Symbol symbol, Resolution resolution,
TimeSpan period, TickType tickType, bool throwsException, bool emptyData)
TimeSpan period, TickType tickType, bool emptyData)
{
TestDelegate test = () =>
{
var historyProvider = new BrokerageHistoryProvider();
historyProvider.SetBrokerage(brokerage);
historyProvider.Initialize(new HistoryProviderInitializeParameters(null, null, null,
null, null, null, null,
false, new DataPermissionManager(), null));
var historyProvider = new BrokerageHistoryProvider();
historyProvider.SetBrokerage(brokerage);
historyProvider.Initialize(new HistoryProviderInitializeParameters(null, null, null,
null, null, null, null,
false, new DataPermissionManager(), null));

var marketHoursDatabase = MarketHoursDatabase.FromDataFolder();
var marketHoursDatabase = MarketHoursDatabase.FromDataFolder();

var now = DateTime.UtcNow.AddDays(-1);
var requests = new[]
{
new HistoryRequest(now.Add(-period),
now,
resolution == Resolution.Tick ? typeof(Tick) : typeof(TradeBar),
symbol,
resolution,
marketHoursDatabase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType),
marketHoursDatabase.GetDataTimeZone(symbol.ID.Market, symbol, symbol.SecurityType),
resolution,
false,
false,
DataNormalizationMode.Adjusted,
tickType)
};
var now = DateTime.UtcNow.AddDays(-1);
var requests = new[]
{
new HistoryRequest(now.Add(-period),
now,
resolution == Resolution.Tick ? typeof(Tick) : typeof(TradeBar),
symbol,
resolution,
marketHoursDatabase.GetExchangeHours(symbol.ID.Market, symbol, symbol.SecurityType),
marketHoursDatabase.GetDataTimeZone(symbol.ID.Market, symbol, symbol.SecurityType),
resolution,
false,
false,
DataNormalizationMode.Adjusted,
tickType)
};

var historyArray = historyProvider.GetHistory(requests, TimeZones.Utc).ToArray();
foreach (var slice in historyArray)
var historyArray = historyProvider.GetHistory(requests, TimeZones.Utc).ToArray();
foreach (var slice in historyArray)
{
if (resolution == Resolution.Tick)
{
if (resolution == Resolution.Tick)
{
foreach (var tick in slice.Ticks[symbol])
{
Log.Trace("{0}: {1} - {2} / {3}", tick.Time.ToStringInvariant("yyyy-MM-dd HH:mm:ss.fff"),
tick.Symbol, tick.BidPrice, tick.AskPrice);
}
}
else if (slice.QuoteBars.TryGetValue(symbol, out var quoteBar))
foreach (var tick in slice.Ticks[symbol])
{
Log.Trace($"QuoteBar: {quoteBar}");
}
else if (slice.Bars.TryGetValue(symbol, out var bar))
{
Log.Trace("{0}: {1} - O={2}, H={3}, L={4}, C={5}", bar.Time, bar.Symbol, bar.Open, bar.High,
bar.Low, bar.Close);
Log.Trace("{0}: {1} - {2} / {3}", tick.Time.ToStringInvariant("yyyy-MM-dd HH:mm:ss.fff"),
tick.Symbol, tick.BidPrice, tick.AskPrice);
}
}

if (emptyData)
else if (slice.QuoteBars.TryGetValue(symbol, out var quoteBar))
{
Assert.Zero(historyProvider.DataPointCount);
Log.Trace($"QuoteBar: {quoteBar}");
}
else
else if (slice.Bars.TryGetValue(symbol, out var bar))
{
Assert.Greater(historyProvider.DataPointCount, 0);
Log.Trace("{0}: {1} - O={2}, H={3}, L={4}, C={5}", bar.Time, bar.Symbol, bar.Open, bar.High,
bar.Low, bar.Close);
}
}

if (historyProvider.DataPointCount > 0)
{
// Ordered by time
Assert.That(historyArray, Is.Ordered.By("Time"));
if (emptyData)
{
Assert.Zero(historyProvider.DataPointCount);
}
else
{
Assert.Greater(historyProvider.DataPointCount, 0);
}

// No repeating bars
var timesArray = historyArray.Select(x => x.Time).ToArray();
Assert.AreEqual(timesArray.Length, timesArray.Distinct().Count());
if (historyProvider.DataPointCount > 0)
{
// Ordered by time
Assert.That(historyArray, Is.Ordered.By("Time"));

foreach (var slice in historyArray)
// No repeating bars
var timesArray = historyArray.Select(x => x.Time).ToArray();
Assert.AreEqual(timesArray.Length, timesArray.Distinct().Count());

foreach (var slice in historyArray)
{
var data = slice.AllData[0];
Assert.AreEqual(symbol, data.Symbol);

if (data.DataType != MarketDataType.Tick)
{
var data = slice.AllData[0];
Assert.AreEqual(symbol, data.Symbol);
Assert.AreEqual(resolution.ToTimeSpan(), data.EndTime - data.Time);
}
}

// No missing bars
if (resolution != Resolution.Tick && historyProvider.DataPointCount >= 2)
// No missing bars
if (resolution != Resolution.Tick && historyProvider.DataPointCount >= 2)
{
var diff = resolution.ToTimeSpan();
for (var i = 1; i < timesArray.Length; i++)
{
var diff = resolution.ToTimeSpan();
for (var i = 1; i < timesArray.Length; i++)
{
Assert.AreEqual(diff, timesArray[i] - timesArray[i - 1]);
}
Assert.AreEqual(diff, timesArray[i] - timesArray[i - 1]);
}
}

Log.Trace("Data points retrieved: " + historyProvider.DataPointCount);
};

if (throwsException)
{
Assert.Throws<ArgumentException>(test);
}
else
{
Assert.DoesNotThrow(test);
}

Log.Trace("Data points retrieved: " + historyProvider.DataPointCount);
}

protected virtual Brokerage CreateBrokerage()
private Brokerage CreateBrokerage()
{
var apiKey = Config.Get("bybit-api-key");
var apiSecret = Config.Get("bybit-api-secret");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ private static TestCaseData[] TestParameters
}

[Test, TestCaseSource(nameof(TestParameters))]
public void StreamsData(Symbol symbol, Resolution resolution, bool throwsException)
public virtual void StreamsData(Symbol symbol, Resolution resolution, bool throwsException)
{
StreamsData(symbol, resolution, throwsException, Brokerage);
}
Expand Down
13 changes: 10 additions & 3 deletions QuantConnect.BybitBrokerage.Tests/BybitBrokerageTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,13 @@ namespace QuantConnect.BybitBrokerage.Tests
[TestFixture, Explicit("Requires valid credentials to be setup and run outside USA")]
public partial class BybitBrokerageTests : BrokerageTests
{
protected static Symbol BTCUSDT = Symbol.Create("BTCUSDT", SecurityType.Crypto, "bybit");
private static Symbol BTCUSDT = Symbol.Create("BTCUSDT", SecurityType.Crypto, "bybit");
private BybitApi _client;
protected override Symbol Symbol { get; } = BTCUSDT;
protected override SecurityType SecurityType => SecurityType.Crypto;

protected virtual BybitProductCategory Category => BybitProductCategory.Spot;

protected override decimal GetDefaultQuantity() => 0.0005m;

protected override bool IsAsync() => false;
Expand All @@ -49,7 +51,10 @@ public partial class BybitBrokerageTests : BrokerageTests

protected override IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISecurityProvider securityProvider)
{
var model = new BybitBrokerageModel(AccountType.Margin);

var algorithm = new Mock<IAlgorithm>();
algorithm.SetupGet(x => x.BrokerageModel).Returns(model);

var apiKey = Config.Get("bybit-api-key");
var apiSecret = Config.Get("bybit-api-secret");
Expand All @@ -61,6 +66,8 @@ protected override IBrokerage CreateBrokerage(IOrderProvider orderProvider, ISec
securityProvider, new AggregationManager(), null);
}

protected virtual decimal TakerFee => BybitFeeModel.TakerNonVIPFee;

protected virtual BybitApi CreateRestApiClient(string apiKey, string apiSecret, string apiUrl)
{
return new BybitApi(SymbolMapper, null, apiKey, apiSecret, apiUrl);
Expand All @@ -69,7 +76,7 @@ protected virtual BybitApi CreateRestApiClient(string apiKey, string apiSecret,
protected override decimal GetAskPrice(Symbol symbol)
{
var brokerageSymbol = SymbolMapper.GetBrokerageSymbol(symbol);
return _client.Market.GetTicker(BybitProductCategory.Spot, brokerageSymbol).Ask1Price!.Value;
return _client.Market.GetTicker(Category, brokerageSymbol).Ask1Price!.Value;
}

/// <summary>
Expand Down Expand Up @@ -190,7 +197,7 @@ public override void GetAccountHoldings()
var beforeQuantity = beforeHoldings == null ? 0 : beforeHoldings.Amount;
var afterQuantity = afterHoldings == null ? 0 : afterHoldings.Amount;

var fee = order.Quantity * BybitFeeModel.TakerNonVIPFee;
var fee = order.Quantity * TakerFee;

Assert.AreEqual(GetDefaultQuantity(), afterQuantity - beforeQuantity + fee);
}
Expand Down
Loading

0 comments on commit 2ec4892

Please sign in to comment.