Пример #1
0
    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)
Пример #2
0
    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)
Пример #3
0
    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)
Пример #4
0
    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)
Пример #5
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)