Dùng Python cập nhật giá Bitcoin

Post by dangsonbk on 23-09-2017

Note: Đây là bài viết đầu tiên của mình sử dụng Python cho một ứng dụng thực tế, mình sẽ viết một cách cơ bản nhất cho những bạn mới sử dụng Python hoặc mới bắt đầu học lập trình có thể dễ dàng tiếp cận. Một số kiến thức hoặc kỹ năng giới thiệu trong bài viết có thể khá nhàm chán nếu bạn đã quen với việc lập trình, ngoài ra việc ngắt quãng để giải thích một số thuật ngữ, kỹ thuật cơ bản có thể khiến bài viết không được liền mạch.

Source code: crawler.py


Đây là bài viết dựa trên một ứng dụng thực tế của mình và ứng dụng cũng đang hoạt động khá ổn định. Mục tiêu của ứng dụng bao gồm:

Bài viết này mình cũng sẽ tách làm 2 phần tương ứng, riêng phần phân tích và vẽ biểu đồ mình sẽ không giới thiệu ở đây, thực tế thì phần này cũng được mình tách ra một script khác và cũng chưa được hoàn thiện cho lắm ở thời điểm viết bài.


Cập nhật giá bitcoin

Mục tiêu: Lấy giá bitcoin trên sàn wex.nz

Sàn giao dịch bitcoin và các đồng tiền mã hóa khác: wex.nz (trước đó được biết đến với cái tên btc-e) cung cấp sẵn cho chúng ta các hàm API cho phép dễ dàng lấy được các thông tin về các đồng tiền và giao dịch trên sàn, bạn có thể xem chi tiết thông tin về các API này ở đường dẫn: wex public API

Một giao diện lập trình ứng dụng (tiếng Anh Application Programming Interface hay API) là một giao diện mà một hệ thống máy tính hay ứng dụng cung cấp để cho phép các yêu cầu dịch vụ có thể được tạo ra từ các chương trình máy tính khác, và/hoặc cho phép dữ liệu có thể được trao đổi qua lại giữa chúng. (...) Nhiều loại hệ thống và ứng dụng hiện thực API, như các hệ thống đồ họa, cơ sở dữ liệu, mạng, dịch vụ web, và ngay cả một số trò chơi máy tính.

Để cho dễ hiểu hơn về API của wex, bạn có thể truy cập vào đường dẫn sau: https://wex.nz/api/3/ticker/btc_usd-btc_rur. Kết quả trả về toàn bộ là dữ liệu thô, không kèm theo các nội dung về giao diện web hay những thành phần không liên quan.

Ngoài public API, wex cũng cung cấp các APIs liên quan đến giao dịch trên sàn: Trade API, Push API

Bây giờ chúng ta bắt đầu bằng việc lấy giá cập nhật của đồng Bitcoin, mã sàn là btc, địa chỉ là https://wex.nz/api/3/ticker/btc_usd

Thư viện sử dụng để truy cập API trên là urllib, đây là thư viện mặc định của Python nên bạn có thể import và sử dụng luôn.

Trên Python Sell (Python 3):

>>> import urllib.request
>>> x = urllib.request.urlopen('https://wex.nz/api/3/ticker/btc_usd')
>>> print(x.read())
b'{"btc_usd":{"high":4234.181,"low":4091.45,"avg":4162.8155,"vol":2059874.59043,"vol_cur":493.2161,"last":4166.271,"buy":4185,"sell":4166.271,"updated":1506223812}}'

Bạn có thể thấy dữ liệu trả về có định dạng trùng khớp với dữ liệu trả về khi truy cập vào đường dẫn API trên bằng trình duyệt.

Trong bài viết có thể bạn sẽ thấy các keywords như Shell, Terminal, Console, Command Prompt, Cửa sổ lệnh ..., lý do là tùy vào hệ điều hành, phần mềm và đôi khi là thói quen mà tên gọi của chúng có thể thay đổi, vì vậy để cho đơn giản, bài viết này mình sẽ ngầm hiểu chung ý nghĩa của chúng là "Cửa sổ gõ lệnh" không phân biệt vào hệ điều hành cũng như phần mềm.

Đối với các trình thông dịch như Python, (code sẽ được đọc và chạy trực tiếp chứ không biên dịch toàn bộ ra mã máy rồi chạy như Biên dịch) thì code để thực thi sẽ thường được gọi là script - một đoạn mã lệnh, script có thể được gõ và chạy trực tiếp trên Python Shell hoặc cũng có thể được lưu và chạy trên file.

Dĩ nhiên là không ai chạy script trên Shell mãi được, Python cho phép chạy code từ file text có đuôi .py để lập trình viên có thể dễ dàng làm việc hơn.

Trên Windows, khi bạn tạo một file text có tên main.py, có thể hệ điều hành sẽ tạo cho bạn một file main.py.txt. Để tránh việc này xảy ra và cũng giúp bạn thuận tiện hơn trong việc viết code sau này, mình khuyên các bạn sử dụng các trình soạn thảo code hoặc IDE chuyên dùng như Sublime Text, Visual Studio Code, Notepad++, Pycharm... Cá nhân mình đang sử dụng cả Sublime Text lẫn Visual Studio Code và thấy rất hài lòng, mỗi phần mềm đều có những ưu điểm riêng.

Bạn cần tạo một file crawler.py có nội dung như sau:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
import urllib.request
import json

def btceGetTicker(pairs):
""" Get current coin values, return (buy_price, sell_price, avg_price)"""
    try:
        ticker_raw = urllib.request.urlopen("https://wex.nz/api/3/ticker/" + pairs)
        ticker_encoding = ticker_raw.info().get_content_charset('utf-8')
        ticker_json = json.loads(ticker_raw.read().decode(ticker_encoding))
        return ticker_json
    except Exception as e:
        print("Get ticker failed: ", e)
    return None

def main():
    ticker = btceGetTicker("btc-usd")
    print(ticker)

if __name__ == '__main__':
    main()

Hướng dẫn chạy thử chương trình từ windows console: từ thư mục chứa file, bạn gõ cmd trên thanh địa chỉ. Một cửa sổ Windows Commander sẽ hiện lên. Gõ python ten_file.py để chạy script. Ở ví dụ này là: python crawler.py. Bạn cũng có thể gọi Python Shell và cd tới thư mục chứa script

Hướng dẫn chạy thử chương trình từ IDLE (Python GUI): load file cần chạy, có thể sửa trực tiếp file code trên này, nhấn F5 để chạy.

Để chạy chương trình Python vừa viết, trên Windows Command Prompt (cmd), bạn có thể gõ tên của script ra (ở hình minh họa là main.py, nhiều trường hợp bạn sẽ phải chạy python main.py tùy vào môi trường của từng máy). Bạn gõ python trên Windows Command Prompt để gọi Python Shell (trình command của Python), rất hữu ích để chạy thử lệnh. Trong bài viết này, cách tốt nhất là bạn mở 2 cửa sổ Windows Command Prompt một lúc, một để chạy script và một để chạy thử lệnh Python, trường hợp lỗi bạn tham khảo phần tiếp theo.

Run Python gif

Lỗi "'python' is not recognized as an internal or external command, operable program or batch file". Đây là lỗi do bạn chưa thêm đường dẫn Python vào Windows Environment và Windows không biết bạn gọi python thì python ở đâu. Hướng dẫn giải quyết: VN: daynhauhoc, EN: Tuts, EN: Official

Cùng đi dọc từ phần đầu chương trình xuống dưới:

import urllib.request
import json

Phần này khá dễ hiểu, đầu tiên chương trình sẽ import thư viện urllib.request và thư viện json

Thư viện lập trình cung cấp cho người dùng (ở đây là lập trình viên sử dụng thư viện) các hàm, tiện ích ... cho phép người dùng không cần phải viết lại mọi thứ và chỉ cần tập trung vào vấn đề chính cần giải quyết của phần mềm. Python cung cấp sẵn rất nhiều thư viện đi kèm với gói cài đặt, ngoài ra là một số lượng rất lớn các thư viện khác được người dùng chia sẻ.

Lại một kiến thức mới về json đối với các bạn mới học lập trình. Bạn có thể hiểu một cách đơn giản là khi bạn lưu trữ hoặc truyền dữ liệu, thay vì sử dụng các con số 0, 1 chỉ máy tính mới hiểu thì bạn có thể dùng một file văn bản. Dĩ nhiên để cho cả máy tính và người đều hiểu thì văn bản này phải được thiết kế theo một format nhất định, một văn bản json thường trông như thế này:

{
    "name":"Product",
    "properties":
    {
        "id":
        {
            "type":"number",
            "description":"Product identifier",
            "required":true
        },
        "name":
        {
            "description":"Name of the product",
            "type":"string",
            "required":true
        },
        "price":
        {
            "type":"number",
            "minimum":0,
            "required":true
        }
    }
}

Chuyên sâu hơn thì bạn có thể xem tại: json.org. Các kiểu dữ liệu của Python như Dictionary, List ... rất tương đồng với cấu trúc của json, hiểu được cấu trúc cơ bản của json chắc chắn sẽ giúp ích rất nhiều cho bạn sau này.

Bạn sẽ thấy mối liên hệ tương đồng giữa mẫu văn bản trên và thông tin trả về từ API của wex.nz:

{
    "btc_usd":
    {
        "high": 4234.181,
        "low": 4080,
        "avg": 4157.0905,
        "vol": 1936210.71756,
        "vol_cur": 464.60394,
        "last": 4150,
        "buy": 4150,
        "sell": 4131,
        "updated": 1506247634
    }
}

Tiếp theo chúng ta có hàm lấy thông tin tỷ giá bitcoin:

def btceGetTicker(pairs):
""" Get current coin values, return (buy_price, sell_price, avg_price)"""
    try:
        ticker_raw = urllib.request.urlopen("https://wex.nz/api/3/ticker/" + pairs)
        ticker_encoding = ticker_raw.info().get_content_charset('utf-8')
        ticker_json = json.loads(ticker_raw.read().decode(ticker_encoding))
        return ticker_json
    except Exception as e:
        print("Get ticker failed: ", e)
    return None

Mình có gặp một thắc mắc có lẽ cũng thường gặp của một bạn đang muốn học Python để trợ giúp công việc, nguyên văn như thế này: "(...) bác nào pro cho e hỏi là cùng một đoạn lệnh mà muốn đổi biến nó cũng chạy đoạn lệnh đó thì làm ntn? Chứ copy lại cũng nhanh cơ mà nhìn dài dài kiểu gì ấy! ". Vâng, câu trả lời đó chính là sử dụng hàm.

Ở đây btceGetTicker là tên hàm, pairs là đối số truyền vào, một hàm có thể có 1 hoặc nhiều hoặc không có đối số truyền vào.

Tiếp sau đó là thân hàm, thân hàm được lùi vào 1 tab (hoặc 2 hoặc 4 khoảng trắng) theo đúng cú pháp của Python. Lệnh return ở cuối hàm trả về giá trị sau khi thực hiện hàm.

Lấy một ví dụ thực tế:

- Sỹ, xem hộ tao 10 giờ chưa?
- Kém 5 phút

Sỹ: thư viện nhờ vả. xem hộ tao (X) giờ chưa? là tên hàm để thực hiện so xem mấy phút nữa đến X giờ. 10: đối số truyền vào. Kém 5 phút: giá trị trả về (return)

Quay lại vấn đề chính, trong thân hàm bạn sẽ thấy một một số câu lệnh khá rắc rối:

ticker_raw = urllib.request.urlopen("https://wex.nz/api/3/ticker/" + pairs)
ticker_encoding = ticker_raw.info().get_content_charset('utf-8')
ticker_json = json.loads(ticker_raw.read().decode(ticker_encoding))

Dòng thứ nhất đó là lời gọi một hàm từ thư viện urllib.request, đối số truyền vào là đường dẫn API. Ở đây bạn sẽ thấy mình truyền vào chuỗi: "https://wex.nz/api/3/ticker/" + pairs. pairs ở đây chính là cặp (các cặp) tỷ giá muốn lấy thông tin, ví dụ: btc_usd

urllib.request.urlopen("https://wex.nz/api/3/ticker/" + pairs)
# urllib.request.urlopen("https://wex.nz/api/3/ticker/btc_usd")

Để giải thích chi tiết phần này sẽ hơi khó cho các bạn mới học lập trình hoặc mới làm quen với Python cũng như thư viện urllib. Về cơ bản, có thể hiểu flow của đoạn code trên như sau: Lấy data từ web -> lấy thông tin encoding -> decode theo thông tin encoding -> convert sang dữ liệu kiểu json

Dòng tiếp theo sẽ lấy thông tin encoding của dữ liệu web trả về.

ticker_encoding = ticker_raw.info().get_content_charset('utf-8')

urllib.request.urlopen(url[, data][, timeout]): data should be a buffer in the standard application/x-www-form-urlencoded format. Chi tiết: urllib.request.html#urllib.request.urlopen

Giá trị trả về sẽ được đọc và decode sau đó đưa vào làm đối số của hàm tiếp theo: json.loads(). Đúng như tên gọi, hàm này sẽ đọc (loads) chuỗi json và trả về nội dung vào biến ticker_json.

ticker_json = json.loads(ticker_raw.read().decode(ticker_encoding))

Trong trường hợp có lỗi xảy ra (không truy cập được vào API, timeout, json trả về sai ... ) một exception sẽ được sinh ra và nội dung sẽ được in trên sell. Xem thêm về bắt lỗi ở đây: Python Errors and Exceptions handling

Tiếp theo chúng ta phân tích đến hàm main(), nhiệm vụ hiện tại của hàm main này chưa có gì ngoài việc gọi hàm btceGetTicker() kèm theo đối số là cặp tỷ giá muốn lấy thông tin.

Tiếp nữa bạn sẽ thấy một câu lệnh nhìn khá là lạ:

if __name__ == '__main__':
    main()

Đây là một cách viết rất phổ biến khi lập trình Python. Nhiệm vụ của đoạn code này là kiểm tra xem file .py đang được gọi từ đâu. Mình sẽ giải thích kỹ hơn phần này bằng một ví dụ như sau:

Bạn có một file code Python có tên là mymodule.py. Khi bạn gọi mymodule.py từ một module khác (bằng lệnh import mymodule) thì biến __name__ sẽ có giá trị là mymodule. Nếu bạn gọi từ console (chạy python mymodule.py) thì __name__ sẽ có giá trị là __main__

Như vậy câu lệnh trên chỉ đơn giản là kiểm tra xem có phải bạn đang chạy trực tiếp code không, nếu đúng như vậy thì sẽ chạy hàm main tương ứng.

Thực chất bạn có thể gọi trực tiếp hàm btceGetTicker() từ đây


Lấy giá bitcoin sau một khoảng thời gian nhất định

Khi chạy thử, bạn sẽ thấy chương trình chỉ lấy giá một lần sau đó thoát ra. Để thực hiện được nhiều lần, chúng ta sẽ gọi hàm btceGetTicker() để lấy giá liên tục từ một vòng lặp, tiện thể chúng ta sẽ sửa đổi một chút để không chỉ lấy mỗi tỷ giá btc_usd. Bạn update nội dung hàm main như sau:

1
2
3
4
5
6
7
def main():
    COINS = ["btc_usd", "ltc_usd", "nmc_usd", "nvc_usd", "ppc_usd", "dsh_usd", "eth_usd", "usd_rur"]
    pairs = "-".join(COINS)
    while True:
        ticker = btceGetTicker(pairs)
        if ticker:
            print(ticker)

Các bạn để ý vòng lặp while True:, do điều kiện lặp luôn luôn là True nên sẽ lặp vô tận code bên trong.

Tiếp theo là tạo danh sách các cặp tỷ giá muốn lấy thông tin, dĩ nhiên mình không bịa ra chuỗi này được, thông tin chi tiết bạn tham khảo trong đặc tả API: wex public API, về cơ bản thì chỉ cần phân tách các cặp tỉ giá bằng dấu -. Mình tạo một danh sách các cặp tỉ giá được lưu vào trong một list:

COINS = ["btc_usd", "ltc_usd", "nmc_usd", "nvc_usd", "ppc_usd", "dsh_usd", "eth_usd", "usd_rur"]

Nối chúng lại thành một chuỗi sử dụng join(), mình sẽ dùng shell để các bạn có thể hiểu rõ hơn:

>>> COINS = ["btc_usd", "ltc_usd", "nmc_usd", "nvc_usd", "ppc_usd", "dsh_usd", "eth_usd", "usd_rur"]
>>> pairs = "-".join(COINS)
>>> pairs
'btc_usd-ltc_usd-nmc_usd-nvc_usd-ppc_usd-dsh_usd-eth_usd-usd_rur'

Hàm btceGetTicker() sau đó sẽ nối chuỗi này thành một địa chỉ API hợp lệ, bạn có thể thử lại bằng trình duyệt để kiểm tra. Bạn cũng có thể code luôn chuỗi này vào biến pairs cũng được, tuy nhiên ở đây mình tách ra để sau này nếu muốn thêm hoặc xóa một cặp tỷ giá, mình chỉ cần sửa trong danh sách COINS rất dễ dàng.

>>> "https://wex.nz/api/3/ticker/" + pairs
'https://wex.nz/api/3/ticker/btc_usd-ltc_usd-nmc_usd-nvc_usd-ppc_usd-dsh_usd-eth_usd-usd_rur'

Kiểm tra lại cho chắc chắn rồi mới in:

    if ticker:
        print(ticker)

Ở đây bạn có thể ngầm hiểu if ticker tương đương với việc kiểm tra nếu biến ticker != None.

Tại sao lại None? Mình sẽ copy lại đoạn code btceGetTicker ở đây để tiện theo dõi:

def btceGetTicker(pairs):
""" Get current coin values, return (buy_price, sell_price, avg_price)"""
    try:
        ticker_raw = urllib.request.urlopen("https://wex.nz/api/3/ticker/" + pairs)
        ticker_encoding = ticker_raw.info().get_content_charset('utf-8')
        ticker_json = json.loads(ticker_raw.read().decode(ticker_encoding))
        return ticker_json
    except Exception as e:
        print("Get ticker failed: ", e)
    return None

Như vậy trong trường hợp biến ticker được cập nhật (nghĩa là mọi chuyện xảy ra suôn sẻ) thì giá trị ticker sẽ được trả về, đồng nghĩa với việc tất cả lệnh sau đó trong hàm sẽ không được thực thi nữa. Tuy nhiên, nếu chuyện xảy ra không suôn sẻ, exception nào đó bị bắt lại, đoạn chương trình sẽ chạy qua print("Get ticker failed: ", e) tới lệnh return mặc định: return None. Đây cũng là một mẹo rất hữu ích khi lập trình, nếu một lời gọi hàm chờ đợi một giá trị trả về, hãy luôn đảm bảo hàm được gọi sẽ trả về hoặc là kết quả, hoặc là một giá trị nào đó (kể cả lỗi) trong mọi trường hợp.

Như vậy chúng ta đã có thể cập nhật được tỷ giá một cách liên tục, tuy nhiên còn một vấn đề nữa, đó là việc cập nhật liên tục này là không cần thiết. Nguyên nhân là tỷ giá trên sàn cũng không giao động quá nhiều trong một khoảng thời gian ngắn, hơn nữa chúng ta sẽ mất rất nhiều resource nếu thực hiện việc này liên tục (CPU, lưu trữ, đường truyền ...).

Để khắc phục, chúng ta sẽ thêm vào một lệnh cho phép chương trình "nghỉ ngơi" trong một khoảng thời gian. Đầu tiên, thêm module time vào đầu chương trình:

import time

Sửa đổi hàm main một chút:

1
2
3
4
5
6
7
8
def main():
    COINS = ["btc_usd", "ltc_usd", "nmc_usd", "nvc_usd", "ppc_usd", "dsh_usd", "eth_usd", "usd_rur"]
    pairs = "-".join(COINS)
    while True:
        ticker = btceGetTicker(pairs)
        if ticker:
            print(ticker)
        time.sleep(15)

Chú ý câu lệnh: time.sleep(15). 15 ở đây là số giây (thời gian) mà chương trình sẽ "tạm nghỉ" khi gặp lệnh này, bạn có thể tăng hoặc giảm tùy ý.


Lưu dữ liệu vào cơ sở dữ liệu mongodb

Giới thiệu về csdl mongodb:

MongoDB là một hệ quản trị cơ sở dữ liệu mã nguồn mở thiết kế theo kiểu hướng đối tượng, các bảng trong MongoDB được cấu trúc rất linh hoạt cho phép các dữ liệu lưu trữ trên bảng không cần tuân theo một cấu trúc nhất định. Các dữ liệu được lưu trữ kiểu JSON với khả năng truy vấn cực kỳ nhanh.

Ở bài viết này mình sẽ không đi sâu vào việc cài đặt mongodb trên máy của bạn, việc cài đặt không quá khó và cũng có rất nhiều bài viết chi tiết hướng dẫn rồi.

Nếu bạn sử dụng máy cá nhân thì chỉ cần cài đặt phiên bản community là đủ. Download tại địa chỉ: MongoDB download center

Cài đặt MongoDB package cho Python

Gói cài đặt sẵn của Python không hỗ trợ package để làm việc với MongoDB đồng nghĩa với việc bạn bắt buộc phải cài đặt thêm vào nếu muốn sử dụng. Ở đây mình xin giới thiệu package được khuyến nghị có tên là PyMongo

Việc cài đặt rất đơn giản sử dụng pip, từ cửa sổ command của windows, bạn gõ lệnh sau:

python -m pip install pymongo

Trên windows 10 do giới hạn quyền, việc cài đặt có thể bị lỗi, bạn nên mở windows command sử dụng quyền Administrator

Để sử dụng pymongo, bước đầu tiên là import module:

from pymongo import MongoClient

Setup connection:

Giả sử bạn đang có mongodb server hoạt động ở localhost, cổng 27017, đã tạo sẵn collection: wex và không yêu cầu đăng nhập.

Thêm đoạn code sau vào hàm main:

# setup database connection
from pymongo import MongoClient
dbClientConnection = MongoClient('localhost', 27017)
db = dbClientConnection["wex"]

Để lưu giá trị json vừa lấy được từ API của wex.nz về, bạn chỉ việc insert trực tiếp vào document của mongodb. Hàm main() hoàn chỉnh như sau (chú ý import thêm module datetime):

def main():
    COINS = ["btc_usd", "ltc_usd", "nmc_usd", "nvc_usd", "ppc_usd", "dsh_usd", "eth_usd", "usd_rur"]
    pairs = "-".join(COINS)

    # setup database connection
    dbClientConnection = MongoClient('localhost', 27017)
    db = dbClientConnection["wex"]

    while True:
        ticker = btceGetTicker(pairs)
        if ticker:
            ticker["update_time"] = datetime.datetime.utcnow()
            entry = db["ticker"]
            entry.insert_one(ticker)
        time.sleep(15)

Ở đây mình có thay đổi một chút dữ liệu trong ticker để phù hợp hơn với việc lưu trữ và truy xuất dữ liệu trong mongodb, tạm thời bạn có thể không cần quá quan tâm tới chi tiết này:

ticker["update_time"] = datetime.datetime.utcnow()

Tiếp theo ta cần tạo một document có tên ticker, việc khởi tạo này khá đơn giản: entry = db["ticker"]. Để ý db chính là object collection được tạo ở trên: db = dbClientConnection["wex"]

Để dữ liệu vào document, bạn chỉ việc lưu trực tiếp nội dung json: entry.insert_one(ticker).

Ví dụ mẫu làm việc với mongodb bằng Python: MongoDB tutorial


Code hoàn chỉnh:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#!/usr/bin/python
import os, sys
import time
import urllib.request
import json
import datetime
from pymongo import MongoClient

def btceGetTicker(pairs):
""" Get current coin values, return (buy_price, sell_price, avg_price)"""
    try:
        ticker_raw = urllib.request.urlopen("https://wex.nz/api/3/ticker/" + pairs)
        ticker_encoding = ticker_raw.info().get_content_charset('utf-8')
        ticker_json = json.loads(ticker_raw.read().decode(ticker_encoding))
        return ticker_json
    except Exception as e:
        print("Get ticker failed: ", e)
    return None

def main():
    COINS = ["btc_usd", "ltc_usd", "nmc_usd", "nvc_usd", "ppc_usd", "dsh_usd", "eth_usd", "usd_rur"]
    pairs = "-".join(COINS)
    # setup database connection
    dbClientConnection = MongoClient('localhost', 27017)
    db = dbClientConnection["wex"]

    while True:
        ticker = btceGetTicker(pairs)
        if ticker:
            try:
                ticker["update_time"] = datetime.datetime.utcnow()
                entry = db["ticker"]
                entry.insert_one(ticker)
            except Exception as e:
                print("Store ticker failed: ", e)
        time.sleep(15)

if __name__ == '__main__':
    main()

tags: Python, bitcoin