def test_depth(self): # currently does not work as bellman_ford's depth feature is broken/ in development. for i in range(1, 4): G = nx.DiGraph() # for the depth of A->B, 0 == -math.log(1). also note weight of A->B == depth B->C. G.add_edge('A', 'B', weight=-math.log(2), depth=0) G.add_edge('B', 'C', weight=-math.log(3), depth=-math.log(2)) # there should be weight of 6 after going through A->B->C. G.add_edge('C', 'A', weight=-math.log(1 / 8), depth=-math.log(i)) finder = NegativeWeightFinder(G, depth=True) paths = finder.bellman_ford('A', loop_from_source=False, unique_paths=True) total = 0 for path in paths: self.assertLessEqual( 1, calculate_profit_ratio_for_path(G, path, depth=True, starting_amount=1)) for i in range(6, 8): 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(1 / 4), depth=-math.log(i)) paths = bellman_ford(G, 'A', unique_paths=True, depth=True)
def test_ratio(self): G = nx.DiGraph() G.add_edge('A', 'B', weight=-0.69) G.add_edge('B', 'C', weight=-1.1) G.add_edge('C', 'A', weight=1.39) paths = bellman_ford(G, 'A', unique_paths=True, loop_from_source=False) for path in paths: self.assertEquals(calculate_profit_ratio_for_path(G, path), 1.5)
def test_positive_ratio(self): graph = multi_digraph_from_json('test_multigraph.json') for node in graph: new_graph, paths = bellman_ford_multi(graph, node) for path in paths: if path: # assert that the path is a negative weight cycle ratio = calculate_profit_ratio_for_path(new_graph, path) # python float precision may round some numbers to 1.0. self.assertGreaterEqual(ratio, 1.0)
def test_positive_ratio(self): __location__ = os.path.realpath( os.path.join(os.getcwd(), os.path.dirname(__file__))) graph = multi_digraph_from_json(os.path.join(__location__, 'test_multigraph.json')) for node in graph: new_graph, paths = bellman_ford_multi(graph, node) for path in paths: if path: # assert that the path is a negative weight cycle ratio = calculate_profit_ratio_for_path(new_graph, path) # python float precision may round some numbers to 1.0. self.assertGreaterEqual(ratio, 1.0)
def test_ratio(self): G = nx.DiGraph() G.add_edge('A', 'B', weight=-math.log(2)) G.add_edge('B', 'C', weight=-math.log(3)) G.add_edge('C', 'A', weight=-math.log(1 / 4)) paths = bellman_ford(G, 'A', unique_paths=True) path_count = 0 for path in paths: path_count += 1 self.assertAlmostEqual(calculate_profit_ratio_for_path(G, path), 1.5) # assert that unique_paths allows for only one path self.assertEqual(path_count, 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 notify(self): # print(self.__class__.__name__, 'notify:', self.exchanges) graph = self.build_graph() graph, paths = bellman_ford_multi(graph, 'btc', loop_from_source=False, unique_paths=True, ensure_profit=False) for path in paths: total = calculate_profit_ratio_for_path(graph, path) total_p = (total - 1) * 100.0 if total_p > 0: # print(total, path) print('{:5.3}%'.format(total_p), ' -> '.join([p for p in path])) print_profit_opportunity_for_path_multi(graph, path)
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)
def test_calculate_profit_ratio_for_path(self): graph = nx.DiGraph() edges = [ # tail node, head node, no_fee_rate, depth (in terms of currency traded), 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', 3, 20, 'BUY'], ] fee = 0.01 for edge in edges: sell = edge[4] == 'SELL' graph.add_edge(edge[0], edge[1], weight=-math.log(edge[2] * (1 - fee)), depth=-math.log(edge[3]), trade_type=edge[4], fee=fee, no_fee_rate=edge[2] if sell else 1 / edge[2], market_name='{}/{}'.format(edge[0], edge[1]) if sell else '{}/{}'.format(edge[1], edge[0])) path = ['A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'A'] starting_amount = 3 ratio, path_data = calculate_profit_ratio_for_path( graph, path, depth=True, starting_amount=starting_amount, gather_path_data=True) self.assertEqual(path_data[0]['rate'], 2) self.assertEqual(path_data[0]['volume'], 3) self.assertEqual(path_data[0]['order'], 'SELL') self.assertEqual(path_data[1]['rate'], 3) self.assertEqual(path_data[1]['volume'], 4) self.assertEqual(path_data[1]['order'], 'SELL') self.assertEqual(path_data[2]['rate'], 7) # AlmostEqual, because of math.log, path_data[2]['volume'] == 1.697142857142857. 11.88 / 7 == 1.6971428571428573 self.assertAlmostEqual(path_data[2]['volume'], 11.88 / 7) self.assertEqual(path_data[2]['order'], 'BUY') self.assertEqual(path_data[3]['rate'], 5) self.assertEqual(path_data[3]['volume'], 0.3) self.assertEqual(path_data[3]['order'], 'BUY') self.assertEqual(path_data[4]['rate'], 4) self.assertEqual(path_data[4]['volume'], 0.297) self.assertEqual(path_data[4]['order'], 'SELL') self.assertEqual(path_data[5]['rate'], 1 / 6) # If Equal instead of AlmostEqual, will raise 4.800000000000001 != 4.8 self.assertAlmostEqual(path_data[5]['volume'], 4.8) self.assertEqual(path_data[5]['order'], 'BUY') self.assertEqual(path_data[6]['rate'], 4 / 3) self.assertAlmostEqual(path_data[6]['volume'], 4.8 * 0.99 * 0.75) self.assertEqual(path_data[6]['order'], 'BUY') self.assertEqual(path_data[7]['rate'], 1 / 3) self.assertAlmostEqual(path_data[7]['volume'], 3.564 * 0.99 * 3) self.assertEqual(path_data[7]['order'], 'BUY') self.assertAlmostEqual(ratio, 3.564 * 0.99 * 3 * 0.99 / starting_amount)