public void TestLayersBuyBuySellBottleneckThree() //only profitable triangles require inputs - volume is calculated as well { //BUYBUYSELL BOTTLENECK = TRADE 2 (USE REGULAR TEST ORDER BOOKS FOR FIRST TWO TRADES): IOrderbook EosBtcProfitable = new HitbtcOrderbook(); //since all of the unprofitable test values are very close to equilibrium, a 2% change in price here will make all triangles profitable EosBtcProfitable.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosBtcProfitable.OfficialAsks.TryAdd(0.00027000m, 104.95m); EosBtcProfitable.OfficialAsks.TryAdd(0.00027100m, 123.82m); EosBtcProfitable.OfficialAsks.TryAdd(0.00027200m, 160.66m); EosBtcProfitable.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EosBtcProfitable.OfficialBids.TryAdd(0.00028050m, 506.75m); EosBtcProfitable.OfficialBids.TryAdd(0.00028000m, 120.44m); EosBtcProfitable.OfficialBids.TryAdd(0.00027900m, 725.15m); //BUYBUYSELL BOTTLENECK = TRADE 3 (USE OTHER PROFITABLE TEST ORDERBOOK FOR SECOND AND THIRD TRADE): IOrderbook EthBtcBuyBuySellBottleneckThree = new HitbtcOrderbook(); EthBtcBuyBuySellBottleneckThree.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EthBtcBuyBuySellBottleneckThree.OfficialBids.TryAdd(0.034139m, 4.2344m); EthBtcBuyBuySellBottleneckThree.OfficialBids.TryAdd(0.034110m, 2.9281m); EthBtcBuyBuySellBottleneckThree.OfficialBids.TryAdd(0.034070m, 6.0711m); EthBtcBuyBuySellBottleneckThree.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EthBtcBuyBuySellBottleneckThree.OfficialAsks.TryAdd(0.034172m, 36m); EthBtcBuyBuySellBottleneckThree.OfficialAsks.TryAdd(0.034200m, 32.35m); EthBtcBuyBuySellBottleneckThree.OfficialAsks.TryAdd(0.035210m, 17.31m); IOrderbook EosEthBuyBuySellBottleneckOne = new HitbtcOrderbook(); EosEthBuyBuySellBottleneckOne.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EosEthBuyBuySellBottleneckOne.OfficialBids.TryAdd(0.0080856m, 20m); EosEthBuyBuySellBottleneckOne.OfficialBids.TryAdd(0.0080810m, 543.14m); EosEthBuyBuySellBottleneckOne.OfficialBids.TryAdd(0.0080500m, 144.83m); EosEthBuyBuySellBottleneckOne.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosEthBuyBuySellBottleneckOne.OfficialAsks.TryAdd(0.0081086m, 2000000m); EosEthBuyBuySellBottleneckOne.OfficialAsks.TryAdd(0.0081500m, 3620000.18m); EosEthBuyBuySellBottleneckOne.OfficialAsks.TryAdd(0.0081575m, 1440000.86m); EthEosBtc.FirstSymbolOrderbook = EthBtcBuyBuySellBottleneckThree; EthEosBtc.SecondSymbolOrderbook = EosEthBuyBuySellBottleneckOne; EthEosBtc.ThirdSymbolOrderbook = EosBtcProfitable; EthEosBtc.CreateOrderbookSnapshots(); EthEosBtc.SetMaxVolumeAndProfitability(); Assert.AreEqual(EthEosBtcProfitableBottleneckThreeProfit, EthEosBtc.Profit); Assert.AreEqual(EthEosBtcProfitableBottleneckThreeVolume, EthEosBtc.MaxVolume); Assert.AreEqual(EthEosBtcProfitableBottleneckThreeProfitPercent, EthEosBtc.ProfitPercent); }
public void TestLayersBuySellSellBottleneckThree() { IOrderbook EthBtc = new HitbtcOrderbook(); EthBtc.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EthBtc.OfficialBids.TryAdd(0.034139m, 0.01m); EthBtc.OfficialBids.TryAdd(0.034110m, 0.05m); EthBtc.OfficialBids.TryAdd(0.034070m, 0.1m); EthBtc.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EthBtc.OfficialAsks.TryAdd(0.034172m, 3.6m); EthBtc.OfficialAsks.TryAdd(0.034200m, 0.3235m); EthBtc.OfficialAsks.TryAdd(0.035210m, 1.1731m); IOrderbook EosEth = new HitbtcOrderbook(); EosEth.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EosEth.OfficialBids.TryAdd(0.0080856m, 20m); EosEth.OfficialBids.TryAdd(0.0080810m, 543.14m); EosEth.OfficialBids.TryAdd(0.0080500m, 144.83m); EosEth.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosEth.OfficialAsks.TryAdd(0.0081086m, 20m); EosEth.OfficialAsks.TryAdd(0.0081500m, 362.18m); EosEth.OfficialAsks.TryAdd(0.0081575m, 144.86m); //BUYBUYSELL BOTTLENECK = TRADE 3 (USE REGULAR TEST ORDER BOOKS FOR FIRST TWO TRADES): IOrderbook EosBtcProfitable = new HitbtcOrderbook(); //since all of the unprofitable test values are very close to equilibrium, a 2% change in price here will make all triangles profitable EosBtcProfitable.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosBtcProfitable.OfficialAsks.TryAdd(0.00027000m, 104.95m); EosBtcProfitable.OfficialAsks.TryAdd(0.00027100m, 123.82m); EosBtcProfitable.OfficialAsks.TryAdd(0.00027200m, 160.66m); EosBtcProfitable.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EosBtcProfitable.OfficialBids.TryAdd(0.00028050m, 506.75m); EosBtcProfitable.OfficialBids.TryAdd(0.00028000m, 120.44m); EosBtcProfitable.OfficialBids.TryAdd(0.00027900m, 725.15m); EosEthBtc.FirstSymbolOrderbook = EosBtcProfitable; EosEthBtc.SecondSymbolOrderbook = EosEth; EosEthBtc.ThirdSymbolOrderbook = EthBtc; EosEthBtc.CreateOrderbookSnapshots(); EosEthBtc.SetMaxVolumeAndProfitability(); Assert.AreEqual(EosEthBtcProfitableBottleneckThreeProfit, EosEthBtc.Profit, "Incorrect Profit"); Assert.AreEqual(EosEthBtcProfitableBottleneckThreeVolume, EosEthBtc.MaxVolume, "Incorrect Volume"); Assert.AreEqual(EosEthBtcProfitableBottleneckThreeProfitPercent, EosEthBtc.ProfitPercent, "Incorrect ProfitPercent"); }
public void TestVolumeAndProfitNoLayersBuySellSell() { IOrderbook EosBtcUnprofitable = new HitbtcOrderbook(); EosBtcUnprofitable.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosBtcUnprofitable.OfficialAsks.TryAdd(0.00027619m, 104.95m); EosBtcUnprofitable.OfficialAsks.TryAdd(0.00027750m, 123.82m); EosBtcUnprofitable.OfficialAsks.TryAdd(0.00027900m, 160.66m); EosBtcUnprofitable.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EosBtcUnprofitable.OfficialBids.TryAdd(0.00027500m, 506.75m); EosBtcUnprofitable.OfficialBids.TryAdd(0.00027300m, 120.44m); EosBtcUnprofitable.OfficialBids.TryAdd(0.00027100m, 725.15m); IOrderbook EthBtc = new HitbtcOrderbook(); EthBtc.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EthBtc.OfficialBids.TryAdd(0.034139m, 4.2344m); EthBtc.OfficialBids.TryAdd(0.034110m, 2.9281m); EthBtc.OfficialBids.TryAdd(0.034070m, 6.0711m); EthBtc.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EthBtc.OfficialAsks.TryAdd(0.034172m, 3.6m); EthBtc.OfficialAsks.TryAdd(0.034200m, 0.3235m); EthBtc.OfficialAsks.TryAdd(0.035210m, 1.1731m); IOrderbook EosEth = new HitbtcOrderbook(); EosEth.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EosEth.OfficialBids.TryAdd(0.0080856m, 20m); EosEth.OfficialBids.TryAdd(0.0080810m, 543.14m); EosEth.OfficialBids.TryAdd(0.0080500m, 144.83m); EosEth.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosEth.OfficialAsks.TryAdd(0.0081086m, 20m); EosEth.OfficialAsks.TryAdd(0.0081500m, 362.18m); EosEth.OfficialAsks.TryAdd(0.0081575m, 144.86m); EosEthBtc.FirstSymbolOrderbook = EosBtcUnprofitable; EosEthBtc.SecondSymbolOrderbook = EosEth; EosEthBtc.ThirdSymbolOrderbook = EthBtc; EosEthBtc.CreateOrderbookSnapshots(); EosEthBtc.SetMaxVolumeAndProfitability(); Assert.AreEqual(EosEthBtcUnprofitableProfit, EosEthBtc.ProfitPercent); Assert.AreEqual(EosEthBtcUnprofitableVolume, EosEthBtc.MaxVolume); }
public void TestLayersSellBuySellBottlenecks() { IOrderbook BtcUsdSortedBids = new HitbtcOrderbook(); BtcUsdSortedBids.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); BtcUsdSortedBids.OfficialBids.TryAdd(10372.24m, 0.01m); BtcUsdSortedBids.OfficialBids.TryAdd(10370.04m, 1m); BtcUsdSortedBids.OfficialBids.TryAdd(10367.85m, 2m); BtcUsdSortedBids.OfficialAsks.TryAdd(1m, 1m); //dummy to avoid null exception IOrderbook EosUsdSortedAsks = new HitbtcOrderbook(); EosUsdSortedAsks.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosUsdSortedAsks.OfficialAsks.TryAdd(2.85385m, 37.09m); EosUsdSortedAsks.OfficialAsks.TryAdd(2.86429m, 600m); EosUsdSortedAsks.OfficialAsks.TryAdd(2.86940m, 363.86m); IOrderbook EosBtcProfitable = new HitbtcOrderbook(); //since all of the unprofitable test values are very close to equilibrium, a 2% change in price here will make all triangles profitable EosBtcProfitable.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosBtcProfitable.OfficialAsks.TryAdd(0.00027000m, 104.95m); EosBtcProfitable.OfficialAsks.TryAdd(0.00027100m, 123.82m); EosBtcProfitable.OfficialAsks.TryAdd(0.00027200m, 160.66m); EosBtcProfitable.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EosBtcProfitable.OfficialBids.TryAdd(0.00028050m, 506.75m); EosBtcProfitable.OfficialBids.TryAdd(0.00028000m, 120.44m); EosBtcProfitable.OfficialBids.TryAdd(0.00027300m, 725.15m); UsdEosBtc.FirstSymbolOrderbook = BtcUsdSortedBids; UsdEosBtc.SecondSymbolOrderbook = EosUsdSortedAsks; UsdEosBtc.ThirdSymbolOrderbook = EosBtcProfitable; UsdEosBtc.CreateOrderbookSnapshots(); UsdEosBtc.SetMaxVolumeAndProfitability(); Assert.AreEqual(UsdEosBtcProfitableBottlenecksProfit, UsdEosBtc.Profit, "Incorrect Profit"); Assert.AreEqual(UsdEosBtcProfitableBottlenecksVolume, UsdEosBtc.MaxVolume, "Incorrect Volume"); Assert.AreEqual(UsdEosBtcProfitableBottlenecksProfitPercent, UsdEosBtc.ProfitPercent, "Incorrect ProfitPercent"); }
public void TestProfitAndVolumeNoLayersSellBuySell() { IOrderbook BtcUsdBids = new HitbtcOrderbook(); BtcUsdBids.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); BtcUsdBids.OfficialBids.TryAdd(10372.24m, 0.75m); BtcUsdBids.OfficialBids.TryAdd(10370.04m, 0.12m); BtcUsdBids.OfficialBids.TryAdd(10367.85m, 0.24m); IOrderbook EosUsdAsks = new HitbtcOrderbook(); EosUsdAsks.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosUsdAsks.OfficialAsks.TryAdd(2.85385m, 37.09m); EosUsdAsks.OfficialAsks.TryAdd(2.86429m, 600m); EosUsdAsks.OfficialAsks.TryAdd(2.86940m, 363.86m); IOrderbook EosBtcUnprofitable = new HitbtcOrderbook(); EosBtcUnprofitable.OfficialAsks = new ConcurrentDictionary <decimal, decimal>(); EosBtcUnprofitable.OfficialAsks.TryAdd(0.00027619m, 104.95m); EosBtcUnprofitable.OfficialAsks.TryAdd(0.00027750m, 123.82m); EosBtcUnprofitable.OfficialAsks.TryAdd(0.00027900m, 160.66m); EosBtcUnprofitable.OfficialBids = new ConcurrentDictionary <decimal, decimal>(); EosBtcUnprofitable.OfficialBids.TryAdd(0.00027500m, 506.75m); EosBtcUnprofitable.OfficialBids.TryAdd(0.00027300m, 120.44m); EosBtcUnprofitable.OfficialBids.TryAdd(0.00027100m, 725.15m); UsdEosBtc.FirstSymbolOrderbook = BtcUsdBids; UsdEosBtc.SecondSymbolOrderbook = EosUsdAsks; UsdEosBtc.ThirdSymbolOrderbook = EosBtcUnprofitable; UsdEosBtc.CreateOrderbookSnapshots(); UsdEosBtc.SetMaxVolumeAndProfitability(); Assert.AreEqual(UsdEosBtcUnprofitableProfit, UsdEosBtc.ProfitPercent); Assert.AreEqual(UsdEosBtcUnprofitableVolume, UsdEosBtc.MaxVolume); }
public void NEWTestSymbolTriangleMapping() //test that all of the triangle eligible symbols are matched properly to all of their respective triangles { var testExchange = (IExchange)Activator.CreateInstance(typeof(HitbtcExchange), typeof(HitbtcExchange).ToString()); //Arrange: list all of the possible triangles and map them to their symbols HashSet <IOrderbook> expectedTriangleEligiblePairs = new HashSet <IOrderbook>(); //"ETHBTC", "EOSETH", "EOSBTC", "EOSUSD", "BTCUSD" var ethbtc = new HitbtcOrderbook(); ethbtc.Symbol = "ETHBTC"; ethbtc.QuoteCurrency = "BTC"; ethbtc.BaseCurrency = "ETH"; expectedTriangleEligiblePairs.Add(ethbtc); var eoseth = new HitbtcOrderbook(); eoseth.Symbol = "EOSETH"; eoseth.QuoteCurrency = "ETH"; eoseth.BaseCurrency = "EOS"; expectedTriangleEligiblePairs.Add(eoseth); var eosbtc = new HitbtcOrderbook(); eosbtc.Symbol = "EOSBTC"; eosbtc.QuoteCurrency = "BTC"; eosbtc.BaseCurrency = "EOS"; expectedTriangleEligiblePairs.Add(eosbtc); var eosusd = new HitbtcOrderbook(); eosusd.Symbol = "EOSUSD"; eosusd.QuoteCurrency = "USD"; eosusd.BaseCurrency = "EOS"; expectedTriangleEligiblePairs.Add(eosusd); var btcusd = new HitbtcOrderbook(); btcusd.Symbol = "BTCUSD"; btcusd.QuoteCurrency = "USD"; btcusd.BaseCurrency = "BTC"; expectedTriangleEligiblePairs.Add(btcusd); List <Triangle> triangles = new List <Triangle>() { new Triangle(ethbtc, eoseth, eosbtc, Triangle.Directions.BuyBuySell, testExchange), new Triangle(eosbtc, eoseth, ethbtc, Triangle.Directions.BuySellSell, testExchange), new Triangle(btcusd, eosusd, eosbtc, Triangle.Directions.SellBuySell, testExchange) }; var expectedSymbolTriangleMapping = new ConcurrentDictionary <string, List <Triangle> >(); expectedSymbolTriangleMapping.GetOrAdd("ETHBTC", new List <Triangle>() { triangles[0], triangles[1] }); expectedSymbolTriangleMapping.GetOrAdd("EOSETH", new List <Triangle>() { triangles[0], triangles[1] }); expectedSymbolTriangleMapping.GetOrAdd("EOSBTC", new List <Triangle>() { triangles[0], triangles[1], triangles[2] }); expectedSymbolTriangleMapping.GetOrAdd("BTCUSD", new List <Triangle>() { triangles[2] }); expectedSymbolTriangleMapping.GetOrAdd("EOSUSD", new List <Triangle>() { triangles[2] }); //Act: Run the sample API response through the function testExchange.TriarbEligibleMarkets.Clear(); testExchange.TriarbMarketMapping.Clear(); testExchange.TradedMarkets = expectedTriangleEligiblePairs; MarketMapper.MapOpportunities(testExchange); //Assert: confirm that the result of the test matches the expected outcome //First, test that the correct number of symbols are being mapped. Assert.IsTrue(expectedSymbolTriangleMapping.Count == testExchange.TriarbMarketMapping.Count, $"the wrong number of symbols were mapped. Expected: {expectedSymbolTriangleMapping.Count}. Actual: {testExchange.TriarbMarketMapping.Count}"); //Next, for each symbol mapped, test that the correct number of triangles is matched to the symbol. foreach (var testItem in expectedSymbolTriangleMapping) { if (testExchange.TriarbMarketMapping.TryGetValue(testItem.Key, out List <Triangle> value)) { Assert.IsTrue(testItem.Value.Count == value.Count, $"the wrong number of triangles were mapped to a symbol. For {testItem.Key}, expected was {testItem.Value.Count}, but the actual count was {value.Count}"); //Now, for each symbol mapped, test that the correct triangles are being mapped to the correct symbols. This is really manual but I couldn't think of a better way if (testItem.Key == "EOSUSD" || testItem.Key == "BTCUSD") { Assert.IsTrue(value[0].FirstSymbol == triangles[2].FirstSymbol, $"first symbol is incorrect. should be {triangles[2].FirstSymbol}, is {value[0].FirstSymbol}"); Assert.IsTrue(value[0].SecondSymbol == triangles[2].SecondSymbol, $"second symbol is incorrect. should be {triangles[2].SecondSymbol}, is {value[0].SecondSymbol}"); Assert.IsTrue(value[0].ThirdSymbol == triangles[2].ThirdSymbol, $"third symbol is incorrect. should be {triangles[2].ThirdSymbol}, is {value[0].ThirdSymbol}"); Assert.IsTrue(value[0].Direction == triangles[2].Direction, $"direction is incorrect. should be {triangles[2].Direction}, is {value[0].Direction}"); } else if (testItem.Key == "ETHBTC" || testItem.Key == "EOSETH") { Assert.IsTrue(value.Count == 2); Assert.IsTrue(value[1].FirstSymbol == triangles[1].FirstSymbol, $"first symbol is incorrect. should be {triangles[1].FirstSymbol}, is {value[1].FirstSymbol}"); Assert.IsTrue(value[1].SecondSymbol == triangles[1].SecondSymbol, $"second symbol is incorrect. should be {triangles[1].SecondSymbol}, is {value[1].SecondSymbol}"); Assert.IsTrue(value[1].ThirdSymbol == triangles[1].ThirdSymbol, $"third symbol is incorrect. should be {triangles[1].ThirdSymbol}, is {value[1].ThirdSymbol}"); Assert.IsTrue(value[1].Direction == triangles[1].Direction, $"direction is incorrect. should be {triangles[1].Direction}, is {value[1].Direction}"); Assert.IsTrue(value[0].FirstSymbol == triangles[0].FirstSymbol, $"first symbol is incorrect. should be {triangles[0].FirstSymbol}, is {value[0].FirstSymbol}"); Assert.IsTrue(value[0].SecondSymbol == triangles[0].SecondSymbol, $"second symbol is incorrect. should be {triangles[0].SecondSymbol}, is {value[0].SecondSymbol}"); Assert.IsTrue(value[0].ThirdSymbol == triangles[0].ThirdSymbol, $"third symbol is incorrect. should be {triangles[0].ThirdSymbol}, is {value[0].ThirdSymbol}"); Assert.IsTrue(value[0].Direction == triangles[0].Direction, $"direction is incorrect. should be {triangles[0].Direction}, is {value[0].Direction}"); } else if (testItem.Key == "EOSBTC") { Assert.IsTrue(value.Count == 3); Assert.IsTrue(value[1].FirstSymbol == triangles[1].FirstSymbol, $"first symbol is incorrect. should be {triangles[1].FirstSymbol}, is {value[1].FirstSymbol}"); Assert.IsTrue(value[1].SecondSymbol == triangles[1].SecondSymbol, $"second symbol is incorrect. should be {triangles[1].SecondSymbol}, is {value[1].SecondSymbol}"); Assert.IsTrue(value[1].ThirdSymbol == triangles[1].ThirdSymbol, $"third symbol is incorrect. should be {triangles[1].ThirdSymbol}, is {value[1].ThirdSymbol}"); Assert.IsTrue(value[1].Direction == triangles[1].Direction, $"direction is incorrect. should be {triangles[1].Direction}, is {value[1].Direction}"); Assert.IsTrue(value[0].FirstSymbol == triangles[0].FirstSymbol, $"first symbol is incorrect. should be {triangles[0].FirstSymbol}, is {value[0].FirstSymbol}"); Assert.IsTrue(value[0].SecondSymbol == triangles[0].SecondSymbol, $"second symbol is incorrect. should be {triangles[0].SecondSymbol}, is {value[0].SecondSymbol}"); Assert.IsTrue(value[0].ThirdSymbol == triangles[0].ThirdSymbol, $"third symbol is incorrect. should be {triangles[0].ThirdSymbol}, is {value[0].ThirdSymbol}"); Assert.IsTrue(value[0].Direction == triangles[0].Direction, $"direction is incorrect. should be {triangles[0].Direction}, is {value[0].Direction}"); Assert.IsTrue(value[2].FirstSymbol == triangles[2].FirstSymbol, $"first symbol is incorrect. should be {triangles[2].FirstSymbol}, is {value[2].FirstSymbol}"); Assert.IsTrue(value[2].SecondSymbol == triangles[2].SecondSymbol, $"second symbol is incorrect. should be {triangles[2].SecondSymbol}, is {value[2].SecondSymbol}"); Assert.IsTrue(value[2].ThirdSymbol == triangles[2].ThirdSymbol, $"third symbol is incorrect. should be {triangles[2].ThirdSymbol}, is {value[2].ThirdSymbol}"); Assert.IsTrue(value[2].Direction == triangles[2].Direction, $"direction is incorrect. should be {triangles[2].Direction}, is {value[2].Direction}"); } else { Assert.Fail($"Unexpected case {testItem.Key}"); } } else { Assert.Fail($"Expected symbol {testItem.Key} but did not find it in actual mapping."); } } }
public void NEWTestTriangleEligiblePairs() { //Arrange: Expected outcomes are declared. HashSet <string> expectedTriangleEligiblePairs = new HashSet <string>() { "ETHBTC", "EOSETH", "EOSBTC", "EOSUSD", "BTCUSD" }; //"ETHBTC", "EOSETH", "EOSBTC", "EOSUSD", "BTCUSD" HashSet <IOrderbook> testTradedMarkets = new HashSet <IOrderbook>(); var ethbtc = new HitbtcOrderbook(); ethbtc.Symbol = "ETHBTC"; ethbtc.QuoteCurrency = "BTC"; ethbtc.BaseCurrency = "ETH"; testTradedMarkets.Add(ethbtc); var eoseth = new HitbtcOrderbook(); eoseth.Symbol = "EOSETH"; eoseth.QuoteCurrency = "ETH"; eoseth.BaseCurrency = "EOS"; testTradedMarkets.Add(eoseth); var eosbtc = new HitbtcOrderbook(); eosbtc.Symbol = "EOSBTC"; eosbtc.QuoteCurrency = "BTC"; eosbtc.BaseCurrency = "EOS"; testTradedMarkets.Add(eosbtc); var btcusd = new HitbtcOrderbook(); btcusd.Symbol = "BTCUSD"; btcusd.QuoteCurrency = "USD"; btcusd.BaseCurrency = "BTC"; testTradedMarkets.Add(btcusd); var eosusd = new HitbtcOrderbook(); eosusd.Symbol = "EOSUSD"; eosusd.QuoteCurrency = "USD"; eosusd.BaseCurrency = "EOS"; testTradedMarkets.Add(eosusd); //Act: run the sample API response through the function var testExchange = (IExchange)Activator.CreateInstance(typeof(HitbtcExchange), typeof(HitbtcExchange).ToString()); testExchange.TriarbEligibleMarkets.Clear(); testExchange.TradedMarkets = testTradedMarkets; MarketMapper.MapOpportunities(testExchange); //Assert: confirm that the results of the test symbols match the expected outcome Assert.IsTrue(testExchange.TriarbEligibleMarkets.Count == expectedTriangleEligiblePairs.Count, $"wrong count: expected is {expectedTriangleEligiblePairs.Count}, actual is {testExchange.TriarbEligibleMarkets.Count}"); bool setsMatch = true; foreach (var item in expectedTriangleEligiblePairs) { if (!testExchange.TriarbEligibleMarkets.Contains(item)) { Assert.Fail($"{item} missing from actual hashset"); setsMatch = false; } } Assert.IsTrue(setsMatch); }