← Back to Blog

Charting USDT/VND Price History on Binance With a Prometheus Exporter and Grafana

I've recently gotten interested in USDT on Binance, so I wanted to track its price history converted to VND. After going around a few sites, I couldn't find a single one that gave me the info the way I wanted.

Context

  • Track the sell rate at a transaction size of 10,000,000 VND
  • Strip out the ad price sitting at the top (which skews quite far from the market rate)
Binance P2P USDT/VND price listing

Given that, I figured I'd just build it myself. In this post I'll walk through the "quick and dirty" process of building a system to store and track the USDT/VND exchange rate history.

To build a system that can chart price history, I used 3 components:

  • Scraper (Python): to scrape data directly from the Binance page
  • Prometheus: to store the exchange rate history long-term.
  • Grafana: the familiar companion tool to Prometheus, to visualize it as a chart.

Scraper (Python)

Starting with scraping data from Binance's P2P page to get the exchange rate. I chose to grab the 3 closest prices and average them to reduce the gap with the real rate. The following code runs in 1 thread and scrapes a new price every 10 minutes. I picked 10 minutes because scraping more frequently risks getting blocked.

The data gets returned as JSON at path /:

from flask import Flask, jsonify
import requests
import json
import threading
import time

app = Flask(__name__)

# Initialize variables
usdtvnd = 0

def update_usdt_vnd():
    global usdtvnd  # Access the global variable

    headers = {
        'Accept': '*/*',
        'Accept-Language': 'vi,vi-VN;q=0.9,en;q=0.8',
        'C2CType': 'c2c_web',
        'ClientType': 'web',
        'Content-Type': 'application/json',
        'Lang': 'vi',
        'Origin': 'https://p2p.binance.com',
        'Referer': 'https://p2p.binance.com/trade/sell/USDT?fiat=VND&payment=all-payments',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
        'X-Passthrough-Token': ''  # Replace with your actual token if needed
    }

    data = {
        "fiat": "VND",
        "page": 1,
        "rows": 3,
        "tradeType": "SELL",
        "asset": "USDT",
        "countries": ["VN"],
        "proMerchantAds": False,
        "shieldMerchantAds": False,
        "filterType": "all",
        "additionalKycVerifyFilter": 0,
        "publisherType": None,
        "payTypes": [],
        "classifies": ["mass", "profession"],
        "transAmount": 10000000
    }

    url = 'https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search'

    try:
        response = requests.post(url, headers=headers, json=data)
        response.raise_for_status()

        # Parse JSON data
        data = json.loads(response.text)

        # Extract prices and calculate average
        prices = []
        for item in data["data"]:
            price = float(item["adv"]["price"])
            prices.append(price)

        usdtvnd = round(sum(prices) / len(prices), 0)

    except requests.exceptions.RequestException as e:
        print(f"Error fetching data: {e}")

    # Schedule next update after 10 minutes
    threading.Timer(600, update_usdt_vnd).start()

# Start background thread to update price every 10 minutes
update_usdt_vnd()

@app.route('/')
def get_average_price():
    return jsonify({'USDT/VND': int(usdtvnd)})

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

When you access port 5000, you'll get a value like {'USDT/VND': 25100}. So we've now got the USDT rate at the current moment.

Next, our job is to expose this data in a format Prometheus can read and store. In this post I use a Python Exporter with the prometheus_client library. Here's the code after adding the metrics-exposing logic for Prometheus:

from flask import Flask, jsonify
import requests
import json
import threading
import time
from prometheus_client import start_http_server, Gauge, generate_latest

app = Flask(__name__)

# Initialize variables
usdtvnd = 0

# Prometheus metrics
usdt_vnd_gauge = Gauge('usdt_vnd', 'USDT to VND exchange rate') # Declare metric

def update_usdt_vnd():
    global usdtvnd  # Access the global variable

    headers = {
        'Accept': '*/*',
        'Accept-Language': 'vi,vi-VN;q=0.9,en;q=0.8',
        'C2CType': 'c2c_web',
        'ClientType': 'web',
        'Content-Type': 'application/json',
        'Lang': 'vi',
        'Origin': 'https://p2p.binance.com',
        'Referer': 'https://p2p.binance.com/trade/sell/USDT?fiat=VND&payment=all-payments',
        'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36',
        'X-Passthrough-Token': ''  # Replace with your actual token if needed
    }

    data = {
        "fiat": "VND",
        "page": 1,
        "rows": 3,
        "tradeType": "SELL",
        "asset": "USDT",
        "countries": ["VN"],
        "proMerchantAds": False,
        "shieldMerchantAds": False,
        "filterType": "all",
        "additionalKycVerifyFilter": 0,
        "publisherType": None,
        "payTypes": [],
        "classifies": ["mass", "profession"],
        "transAmount": 10000000
    }

    url = 'https://p2p.binance.com/bapi/c2c/v2/friendly/c2c/adv/search'

    try:
        response = requests.post(url, headers=headers, json=data)
        response.raise_for_status()

        # Parse JSON data
        data = json.loads(response.text)

        # Extract prices and calculate average
        prices = []
        for item in data["data"]:
            price = float(item["adv"]["price"])
            prices.append(price)

        usdtvnd = round(sum(prices) / len(prices), 0)

        # Update Prometheus metrics
        usdt_vnd_gauge.set(usdtvnd) # Update value for usdt_vnd metric

    except requests.exceptions.RequestException as e:
        print(f"Error fetching data: {e}")

    # Schedule next update after 10 minutes
    threading.Timer(600, update_usdt_vnd).start()

# Start background thread to update price every 10 minutes
update_usdt_vnd()

@app.route('/')
def get_average_price():
    return jsonify({'USDT/VND': int(usdtvnd)})

@app.route('/metrics')
def metrics():
    # Return the metrics for Prometheus
    return generate_latest(), 200, {'Content-Type': 'text/plain; charset=utf-8'}

if __name__ == '__main__':
    app.run(host='0.0.0.0', debug=True)

Run the code above again and visit http://localhost:5000/metrics — you'll see the metrics data for Prometheus displayed as shown. At the bottom is the metric named usdt_vnd that we defined, along with its current value.

Prometheus metrics endpoint output

So we've finished the first and also the most tedious part. Next we'll set up Prometheus and Grafana to store the data.

Prometheus

For Prometheus, I chose to just run it as a container for convenience. First, let's create a config file for Prometheus to pass into the container. Create a file prometheus.yml with the following content:

global:
  scrape_interval: 300s  # How frequently Prometheus will scrape targets
  evaluation_interval: 300s  # How frequently rules will be evaluated

scrape_configs:
  - job_name: 'usdt-metrics'  # Name of the job
    static_configs:
      - targets: ['172.16.1.16:5000']  # Change this to match the address of your running Python service

Then run the Prometheus container with this command:

docker run -d --name prometheus -p 9090:9090 -v ./prometheus.yml:/etc/prometheus/prometheus.yml prom/prometheus

Visit Prometheus at localhost:9090, go to Status => Targets, and if you see the Endpoint with status UP like in the image below, you're good.

Prometheus targets showing UP status

To double check, you can go to Graph to try querying the metric. Note that I set the interval to 5 minutes, so you might have to wait a bit for Prometheus to scrape the first batch of data.

Prometheus graph showing the usdt_vnd metric

Grafana

Finally, you can install Grafana to visualize the data collected in Prometheus. Run the Grafana container with this config:

docker run -d --name grafana -p 3000:3000 grafana/grafana

Go into Grafana and configure a Data Source to display it, via Connection => Data Sources => Add new datasource

Grafana add data source screen

After you Save & Test the Prometheus data source successfully, you can start charting this metric.

Grafana dashboard chart of the usdt_vnd metric

Go to the Dashboard section and try creating a dashboard with the metric named usdt_vnd.

Wrapping up

By using a Prometheus Exporter, we can "track literally any data we want." This post walked through the process of building a system to track the USDT/VND exchange rate. I hope this post gives you another solution to reach for at work.

If you found this post useful, don't hesitate to give me an Upvote and Follow me to catch more posts like this! Have a nice day!

If you're running into technical challenges or need help with systems, DevOps tools, I'm confident I can help. Get in touch at hoangviet.io.vn

Have thoughts on this?

I'd love to hear your perspective. Send me a message.

Get in touch