Crypto Trading Bot in Python: Automating your trading strategy

A few years ago, crypto market experienced a substantial increase in its portfolio values and reached daily transactions levels we had never seen before. Nowadays, FOMO has decreased but the amount of daily BTC volume (USD) still remains high, with over $16.33B traded daily in Binance for all pairs.

In this context, trading bots can be excellent choices for individuals seeking to automate their trades, allowing them to focus solely on defining their strategy. The primary question now is: ‘How will we do it?’

Metodology

Today I’m going to show you how to establish an API connection with your exchange account, enabling us to make both POST and GET requests to the service and defining signals based on a strategy. These signals trigger specific POST requests, automating interactions with the server and optimizing API functionality in Python. For this example, we have chosen Binance as our exchange due to its user-friendly API and well-documented features.

Before we start, is important to ensure your Binance account is fully set up and configured for Spot orders. If you haven’t done so already, sign up for an account and generate a personal API Key, which we’ll use shortly.

Once everything is in place, we will define our main Python class responsible for managing and constructing the final URL for our requests.

https://developers.binance.com/docs/binance-spot-api-docs: Crypto Trading Bot in Python: Automating your trading strategy

Steps

At first, is important to store your API Key and your Secret Key that you have generated into a JSON file.

Step 1:

Now, let’s define our class and its constructor. The aim in this step is to securely store our credentials in variables and establish our ‘Base URL’.

class Client:

    '''
    Define all the necessary functions to be able to generate the requests
    '''

    def __init__(self):


        self.__path = "../../api_binance.json" #path to your credentials file
        self.URL = "https://api.binance.com"

        if self.__path is not None:
            with open(self.__path, 'r') as file:
                data = json.load(file)
                self.__apiKey = data.get('apiKey', None)
                self.__secretKey = data.get('secretKey', None)
        else:
            self.__apiKey = None
            self.__secretKey = None

Step 2:

Now comes one of the most important parts where we’ll focus on generating our URL that will be used to make the request. For this, we’ll need a structure like this: “Base Url + Endpoint + Params + Signature”.

We’ll use 2 auxiliars functions to help define each part:

def signature(self,query_string):

    return hmac.new(
        self.__secretKey.encode("utf-8"), query_string.encode("utf-8"), hashlib.sha256
    ).hexdigest()


def get_timestamp(self):

    return int(time.time() * 1000)

Now, taking into consideration what has been discussed above, the function would look like this:

def base_request(self, http_method, signed: bool, endpoint:str, params=None):

    if params is None:
        load_params = {}
    else:
        load_params = params

    if signed:
        query_string = urlencode(load_params,True)
        if query_string:
            query_string = f"{query_string}&timestamp={self.get_timestamp()}"
        else:
            query_string = "timestamp={}".format(self.get_timestamp())
        
        url = (
            self.URL + endpoint + "?" + query_string + "&signature=" + Client().signature(query_string)
        )
        print(f"{http_method} {url}")
        params = {
            "url": url,
        }

        session = requests.Session()
        session.headers.update(
            {"Content-Type" : "application/json;charset=utf-8", "X-MBX-APIKEY": self.__apiKey}
        )

        return {
            "GET" : session.get,
            "DELETE" : session.delete,
            "PUT" : session.put,
            "POST" : session.post,
        }.get(http_method, "GET")(**params).json()

    else:
        url = (
            self.URL + endpoint
        )
        params = {
            "url" : url,
            "params" : params
        }

        session = requests.Session()
        return {
            "GET" : session.get,
            "DELETE" : session.delete,
            "PUT" : session.put,
            "POST" : session.post,
        }.get(http_method, "GET")(**params).json()

As seen, we define different logic depending on whether the request needs to include a signature or not, and the output of the function will be the result of our request.

Step 3:

To clarify, we are going to execute two differents examples — one involving an endpoint that requires a personal signature, and the other where it’s not needed. The first one is to get the current symbol information and the other we’ll use an especial endpoint to test spot sorders requests.

def exchange_information (self,
                          symbol:str)->json:
    
    response = Client().base_request(http_method="GET",signed=False,endpoint="/api/v3/exchangeInfo",params={"symbol":symbol})

    with open("../results/info_"+symbol+".json", 'w') as file:
        file.write(json.dumps(response, indent=4))

    print(response)



if __name__=="__main__":

    symbol = "BTCUSDT"
    exchange_information(symbol=symbol)
def spot_order(self,
               params:dict) -> json: 
    
    
    response = Client().base_request(http_method="POST",signed=True,endpoint="/api/v3/order/test",params=params)

    return response

    '''
    #Use it if you want to save the results
    with open("../results/firstsell.json", 'w') as file:
        file.write(json.dumps(response, indent=4))
    '''



if __name__=="__main__":

    params = {
        "symbol":"BTCUSDT",
        "side":"SELL",
        "type":"MARKET",
        "quantity":5
    }
    
    response = spot_order(params=params)
    print(response)

Now, you’re ready to use it and craft your strategy. Utilize signals to kick off a mix of requests that suit your style.

Ongoing Investigation

What we’ve just seen is a simple example of how to place orders on the exchange using its API without the need for third-party libraries. However, this is only the first step to make a real bot capable of operating based on a strategy.

Once you’ve defined a strategy and implemented it into your program, you’ll need to continuously work on improving performance. In this kind of projects, efficiency is crucial because many strategies are evaluated second-by-second.

I hope this has been helpful, and if you have any suggestions or want to actively participate in this project, feel free to reach out!