def test_negative_weight_depth_finder_c(self): """Tests NegativeWeightDepthFinder as it is used in arbitrag""" symbols = [ 'BTC/USD', 'ETH/USD', 'ETH/BTC', 'LTC/BTC', 'LTC/USD', 'ETH/LTC', 'DRC/BTC', 'DRC/ETH' ] markets = { symbol: { 'volume_increment': 10**-8, 'price_increment': 10**-8, 'min_market_funds': 10**-16, 'taker_fee': 0.001, 'maker_fee': 0, } for symbol in symbols } graph = nx.DiGraph() [wss_add_market(graph, k, v) for k, v in markets.items()] wss_update_graph(graph, 'BTC/USD', 'asks', 5000, 0.5) wss_update_graph(graph, 'ETH/USD', 'bids', 500, 6) wss_update_graph(graph, 'ETH/BTC', 'asks', 0.14, 8) nwdf = NegativeWeightDepthFinder(graph) paths = nwdf.bellman_ford('BTC') for p in paths: print(p)
def test_returned_volume(self): """ Tests the volume returned by NegativeWeightDepthFinder.bellman_ford """ graph = build_graph_from_edge_list(self.edges_b, 0) finder = NegativeWeightDepthFinder(graph) opportunities = finder.bellman_ford('G', True) opportunities_found = 0 for path, volume in opportunities: opportunities_found += 1 self.assertGreater(len(path), 2, 'assert that there are at least 2 currencies in the path') self.assertGreater(volume, 0, 'assert that the maximum usable volume is at least 0') starting_edge_index = -1 for i, edge in enumerate(self.edges_b): if edge[4].lower() != 'sell': raise RuntimeError('This test expects only sell orders. There is an edge which is not a sell ' 'order: {}'.format(edge)) if edge[0] == path[0]: starting_edge_index = i break # If this fails, something went very wrong or self.edges_b was changed. self.assertNotEqual(starting_edge_index, -1, 'assert that the first currency in the path is a part of an edge in self.edges_b') # used because of floating point precision diff_precision = 10 ** -8 # how many orders use the maximum amount of possible volume orders_at_volume_capacity = 0 for i in range(len(path) - 1): edge = self.edges_b[(starting_edge_index + i) % len(self.edges_b)] # difference between usable and used volume maximum_volume_diff = edge[3] - volume try: self.assertGreater(maximum_volume_diff, -diff_precision, 'assert that the volume used is less than the volume allowed by the market') except AssertionError as e: raise e # if the volume used was within 10**-8 of the usable volume if abs(maximum_volume_diff) < diff_precision: orders_at_volume_capacity += 1 volume *= edge[2] self.assertGreater(orders_at_volume_capacity, 0, 'assert that all of the volume of at least one market is used') # assert that only one opportunity was found self.assertEqual(opportunities_found, 1)
def test_negative_weight_depth_finder(self): """ Tests NegativeWeightDepthFinder """ final_edge_weight = 0.25 edges = [ # tail node, head node, no_fee_rate, depth (in terms of profited currency), trade_type ['A', 'B', 2, 3, 'SELL'], ['B', 'C', 3, 4, 'SELL'], ['C', 'D', 1 / 7, 14, 'BUY'], ['D', 'E', 0.2, 3 / 2, 'BUY'], ['E', 'F', 4, 3, 'SELL'], ['F', 'G', 6, 0.8, 'BUY'], ['G', 'H', 0.75, 6, 'BUY'], ['H', 'A', final_edge_weight, 20, 'BUY'], ] fee = 0.01 # ratio for the rates from A -> H def get_edge_ratio(): constant_ratio = 1 for edge in edges: constant_ratio *= edge[2] * (1 - fee) return constant_ratio for i in range(10): edges[-1][2] = final_edge_weight * (i + 1) graph = build_graph_from_edge_list(edges, fee) finder = NegativeWeightDepthFinder(graph) paths = finder.bellman_ford('A') edge_ratio = get_edge_ratio() if edge_ratio <= 1: with self.assertRaises(StopIteration): paths.__next__() for path in paths: # assert that if a path is found, only one is found. with self.assertRaises(StopIteration): paths.__next__() ratio = calculate_profit_ratio_for_path( graph, path['loop'], depth=True, starting_amount=math.exp(-path['minimum'])) self.assertAlmostEqual(ratio, edge_ratio)
def test_negative_weight_depth_finder_b(self): """ Another test for NegativeWeightDepthFinder """ node_count = 30 complete_graph = nx.complete_graph(node_count) graph = nx.DiGraph() for edge in complete_graph.edges(): # Only use 1 / 3 of the edges, but use all edges connected to 0 to ensure all nodes reachable if random.random() < 2 / 3 and not (edge[0] == 0 or edge[1] == 0): continue random_weight = random.uniform(-10, 6) random_depth = random.uniform(0, 15) random_depth_b = random.uniform(-15, 0) if random_weight < 0: random_depth *= -1 random_depth_b *= -1 graph.add_edge(edge[0], edge[1], weight=random_weight, depth=random_depth) graph.add_edge(edge[1], edge[0], weight=-random_weight, depth=-random_depth_b) finder = NegativeWeightDepthFinder(graph) # does not matter which source is used, can be any number from 0 to 49. we use 0. paths = finder.bellman_ford(0) def calculate_ratio(found_path): total = 0 for i in range(len(found_path) - 1): start = found_path[i] end = found_path[i + 1] total += graph[start][end]['weight'] return total for path in paths: ratio = calculate_ratio(path['loop']) self.assertLess(ratio, 0.0)
def test_true_depth(self): """ Tests NegativeWeightDepthFinder """ # Tests that a negative loop starting at A cannot exist because the minimum weight of a cycle from and to A # is approximately 0.154, which is the negative log of 6/7. for i in range(1, 4): # todo: must we reinitialize G? G = nx.DiGraph() G.add_edge('A', 'B', weight=-math.log(2), depth=0) G.add_edge('B', 'C', weight=-math.log(3), depth=-math.log(2)) G.add_edge('C', 'A', weight=-math.log(2 / 7), depth=-math.log(i)) paths = NegativeWeightDepthFinder(G).bellman_ford('A') total = 0 for path in paths: total += 1 # asserts that there were no paths with negative weight given depths between -math.log(1) and -math.log(3) # for the edge C->A self.assertEqual(total, 0) total = 0 for i in range(4, 7): G = nx.DiGraph() G.add_edge('A', 'B', weight=-math.log(2), depth=0) G.add_edge('B', 'C', weight=-math.log(3), depth=-math.log(2)) G.add_edge('C', 'A', weight=-math.log(2 / 7), depth=-math.log(i)) paths = NegativeWeightDepthFinder(G).bellman_ford('A') for path in paths: # asserts that each of the 3 paths has a profit ratio of 8/7, 10/7, and 12/7, respectively. # Because of Python float precision, the last digit of either value is sometimes not equal to the other. self.assertAlmostEqual( calculate_profit_ratio_for_path(G, path, depth=True), 8 / 7 + (i - 4) * (2 / 7)) total += 1 # asserts that there is a negatively-weighted path when the depth for the edge C->A < -math.log(4) self.assertEqual(total, 3)