Crypto Token Exchange Rate Computation Based on Breadth First Search Algorithm on Poloniex API


The P-site of the crypto world is Poloniex, which was acquired by Justin Sun a few years ago. It is an Crypto exchange. I used Poloniex long time ago, and my impression of it wasn’t very good back then.

However, my opinion of it has improved now because P-site provides free API endpoints (Public Endpoints) to query token prices. Compared to others, it is relatively easy to use. For instance, the free interface provided by coinmarketcap (acquired by Binance) has many restrictions.

The following interface of official documentation) has a generous rate limit of 200 requests per second.

https://api.poloniex.com/markets/price

It can return all trading pairs, for example:

[
    {
        "symbol": "BTS_BTC",
        "price": "0.0000000186",
        "time": 1731852313035,
        "dailyChange": "-0.0462",
        "ts": 1731852313054
    },
    {
        "symbol": "DASH_BTC",
        "price": "0.000317",
        "time": 1731848096481,
        "dailyChange": "0.0063",
        "ts": 1731848096489
    },
    ... ...
]

The JSON structure returned is an array, with each element being a structure representing a token price pair. We can think of this as an edge with two vertices, forming a graph structure (a Weighted Graph where the weights of an edge represents the exchange rate from start vertex to another for that edge). Although the data represents one-way edges, it’s essentially bidirectional. For instance, the value of USD_BTC can be reversed to deduce the BTC to USD price.

We can design an algorithm to calculate the price from token A to token B using the BFS (Breadth-First Search) algorithm. For example, given pairs A_B, B_C, and C_D, we can deduce the value of A_D.

DFS (Depth-First Search) algorithm can also be used. However, this algorithm returns the first path found and cannot guarantee the shortest one. The shortest path is indeed the most accurate because the longer the chain, the more the conversion precision decreases.

Of course, there may be multiple paths. Ideally, all these paths can be computed and averaged. However, this requires brute-forcing all paths (finding all the possible paths between two vertices), which results in higher algorithmic time complexity.

Below is the BFS (Breadth First Search) algorithm code in JavaScript. It can be used for web frontends, Node.js backends, or even CloudFlare Serverless Workers or other serverless frameworks like Azure Functions, AWS Lambda, etc.:

const fetch = require('node-fetch');

async function getTicker(a, b) {
  try {
    const response = await fetch('https://api.poloniex.com/markets/price');
    const data = await response.json();

    // Create a hash table to store token pairs and their prices
    const pairMap = new Map();

    // Populate the hash table with direct pairs and their reverse pairs
    for (const { symbol, price } of data) {
      const [base, quote] = symbol.split('_').map(token => token.toLowerCase());
      if (!pairMap.has(base)) pairMap.set(base, new Map());
      if (!pairMap.has(quote)) pairMap.set(quote, new Map());
      
      pairMap.get(base).set(quote, parseFloat(price));
      pairMap.get(quote).set(base, 1 / parseFloat(price)); // Add reverse edge
    }

    // Convert tokens to lowercase
    a = a.toLowerCase();
    b = b.toLowerCase();

    // BFS to find the conversion rate from a to b
    const queue = [[a, 1]]; // Start with the initial token and conversion rate of 1
    const visited = new Set([a]);

    while (queue.length > 0) {
      const [currentToken, currentRate] = queue.shift();

      if (currentToken === b) return currentRate;

      // Check connected tokens
      for (const [nextToken, rate] of (pairMap.get(currentToken) || new Map())) {
        if (!visited.has(nextToken)) {
          visited.add(nextToken);
          queue.push([nextToken, currentRate * rate]);
        }
      }
    }

    // Return null if no path is found
    return null;
  } catch (error) {
    console.error("Error fetching or processing data:", error);
    return null;
  }
}

// Example usage:
(async () => {
  const rate = await getTicker('btc', 'trx');
  console.log('Conversion rate from BTC to TRX:', rate);
})();

Some explanations of the code:

  • API Data Extraction: Fetches data from Poloniex API and parses the response as JSON.
  • Mapping Pairs: Creates a hash map with each token as a key, where the value is another map of tokens it can directly convert to, along with the conversion rate.
  • Bidirectional Mapping: Stores both direct and reverse pairs by inverting the reverse conversion price.
  • Breadth-First Search: Uses a queue to traverse paths from a to b. For each token, its neighbors (convertible tokens) are checked. If b is found, the function returns the cumulative rate; otherwise, it continues until all options are exhausted.
  • Handling No Path: If no conversion path is found, the function returns null.

If there are multiple conversion requests, we can modify the function to accept an array. This way, we don’t need to call Poloniex API multiple times.

const fetch = require('node-fetch');

async function getToken(pairs) {
  try {
    const response = await fetch('https://api.poloniex.com/markets/price');
    const data = await response.json();

    // Create a hash table to store token pairs and their prices
    const pairMap = new Map();

    // Populate the map with direct pairs and their reverse pairs
    for (const { symbol, price } of data) {
      const [base, quote] = symbol.split('_').map(token => token.toLowerCase());
      if (!pairMap.has(base)) pairMap.set(base, new Map());
      if (!pairMap.has(quote)) pairMap.set(quote, new Map());
      
      pairMap.get(base).set(quote, parseFloat(price));
      pairMap.get(quote).set(base, 1 / parseFloat(price)); // Add a reverse edge
    }

    // Helper function to find the conversion rate for a single pair using BFS
    const findConversionRate = (a, b) => {
      a = a.toLowerCase();
      b = b.toLowerCase();
      
      if (a === b) return 1; // Direct conversion

      const queue = [[a, 1]];
      const visited = new Set([a]);

      while (queue.length > 0) {
        const [currentToken, currentRate] = queue.shift();

        if (currentToken === b) return currentRate;

        for (const [nextToken, rate] of (pairMap.get(currentToken) || new Map())) {
          if (!visited.has(nextToken)) {
            visited.add(nextToken);
            queue.push([nextToken, currentRate * rate]);
          }
        }
      }

      return null; // Path not found
    };

    // Iterate over the list and find conversion rates
    const results = pairs.map(([a, b]) => findConversionRate(a, b));
    return results;
  } catch (error) {
    console.error("Error fetching or processing data:", error);
    return pairs.map(() => null); // Return null for each pair in case of error
  }
}

// Example usage:
(async () => {
  const conversionRates = await getToken([['btc', 'trx'], ['usd', 'steem']]);
  console.log('Conversion results:', conversionRates);
})();

Simple code explanations:

  • Parameter Update: `getToken` now accepts an array of tuples, where each tuple represents a pair `[a, b]`.
  • Helper Function: `findConversionRate` handles conversion for each pair, implementing the same BFS method as before.
  • Mapping Each Pair: The function iterates over each pair in `pairs`, applies `findConversionRate` to compute the conversion rate, and stores the results in an array.
  • Error Handling: In case of API or processing error, an array of `null` values is returned, matching the input array’s length.

This modified function can now handle multiple pairs (given in an array) and return their conversion rates based on one single Poloniex API call.

Crypto Exchanges / Crypto Cards

–EOF (The Ultimate Computing & Technology Blog) —

1472 words
Last Post: Understanding Availability Percentages: Calculating Downtime for Your Systems
Next Post: Ionomy Exchange Has Absconded with Investors' Coins

The Permanent URL is: Crypto Token Exchange Rate Computation Based on Breadth First Search Algorithm on Poloniex API (AMP Version)

Leave a Reply