Unable to authenticate with Advanced Trade API via Flask App on Google Cloud API

I have the following python scripts running on v3.12 deployed on Google Cloud Functions (GCF) to accomplish trades as soon as TradingView Alerts are triggered.

In my tests, everything passes except creating orders. I have the following error: “ERROR:main:Error processing trading data: Error in API response… finished with status code: 500”.

I’ve tried over 500 builds, and still I’m in the loop of API response with status code 500. Here’s my entire code, and I really appreciate correcting where I’m failing to implement correctly. Please help.

I’ve 3 files: 1. main.py, 2. requirements.txt and 3. auth.py

  1. main.py:
import flask
from flask import Flask, request, jsonify, render_template, Response
import jwt
from cryptography.hazmat.primitives import serialization
import time
import http.client
import json
import logging
import hmac
import hashlib
import requests
import os
import uuid
from urllib.error import HTTPError
import traceback
from auth import api_key, api_secret, PASS_PHRASE

# Flask App
app = Flask(__name__)

# Logging Configuration
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# APIs
def load_api_credentials():
    api_key = 'api_key'
    api_secret = 'api_secret'
    return [api_key, api_secret]

creds = load_api_credentials()

# Receive TradingView signals/alerts
@app.route('/', methods=['POST'])
def webhook(request): # def webhook() without 'request' throws Internal Server 500
    try:
        data = request.get_json()

        if not data:
            raise ValueError("Invalid JSON data")

        ticker = data.get('ticker')
        quantity = data.get('quantity')
        action = data.get('action')
        passphrase = data.get('security_passphrase')

        # Verify the security passphrase
        if passphrase != PASS_PHRASE:
            raise ValueError("Invalid security passphrase")

        if not all([ticker, quantity, action]):
            raise ValueError("Invalid TradingView alert data")

        # Determine side based on action ("buy" or "sell")
        side = 'BUY' if action.lower() == 'buy' else 'SELL'

        # Coinbase Advanced Trade API Configuration
        response = cb_create_market_order(ticker, quantity, side)

        if not response or not hasattr(response, 'status_code') or response.status_code != 200:
            raise Exception("Error in API response")

        return flask.jsonify({'success': True}), 200

    except Exception as e:
        logger.error("Error processing trading data: %s", e, exc_info=True)

        # Print the traceback for more details
        traceback.print_exc()

        return flask.jsonify({'error': str(e)}), 500

# Create a market buy/sell order
def cb_create_market_order(product_id, quote_size, side):
    client_order_id = str(uuid.uuid4())  # Convert UUID to string
    payload = {
        "client_order_id": client_order_id,
        "product_id": product_id,
        "side": side,
        "order_configuration": {
            "market_market_ioc": {
                "quote_size": str(quote_size),
                "base_size": str(quote_size)  # Assumes base_size is same as quote_size
            }
        }
    }
    headers = {
        'Content-Type': 'application/json'
    }

    # Initialize HTTP connection
    conn = http.client.HTTPSConnection("api.coinbase.com")

    try:
        conn.request("POST", "/api/v3/brokerage/orders", json.dumps(payload), headers)  # Send request

        # Get response
        res = conn.getresponse()
        response_status = res.status
        response_data = res.read().decode('utf-8')
        
        if response_status == 200:
            print("Order placed successfully!")
        else:
            print(f"Error placing order. Status code: {response_status}. Response data: {response_data}")

    except Exception as e:
        # Include traceback information for better debugging
        raise Exception(f"Error creating market order: {str(e)}\n{traceback.format_exc()}")

    finally:
        conn.close()  # Close the connection after getting the response

# API Authentication
key_name       = creds[0]  # Load API key
key_secret     = creds[1]  # Load API secret
request_method = "GET"
request_host   = "https://api.coinbase.com"
request_path   = "/api/v3/brokerage/accounts"
service_name   = "retail_rest_api_proxy"

# Build JWT token
def build_jwt(service, uri):
    private_key_bytes = key_secret.encode('utf-8')
    private_key = serialization.load_pem_private_key(private_key_bytes, password=None)
    jwt_payload = {
        'sub': key_name,
        'iss': "coinbase-cloud",
        'nbf': int(time.time()),
        'exp': int(time.time()) + 120,
        'aud': [service],
        'uri': uri,
    }
    jwt_token = jwt.encode(
        jwt_payload,
        private_key,
        algorithm='ES256',
        headers={'kid': key_name, 'nonce': secrets.token_hex()},
    )
    return jwt_token

def main():
    uri = f"{request_method} {request_host}{request_path}"
    jwt_token = build_jwt(service_name, uri)
    print(f"export JWT={jwt_token}")

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))
  1. requirements.txt:
Flask<3.0,>=1.0
Werkzeug==2.0.1
PyJWT>=2.0.0
cryptography==36.0.0
numpy==1.26.3
requests
tradingview_ta
  1. auth.py:
api_key='*************************' # Actual API key is replaced
api_secret="""
-----BEGIN PRIVATE KEY-----
Line 1
Line 2
Line 3
-----END PRIVATE KEY-----
"""
PASS_PHRASE='*************' # For TradingView Alerts Security

You are not adding Authorization header, are you?

Also you should generate JWT for /api/v3/brokerage/orders not /api/v3/brokerage/accounts.

I updated as follows:

# Build JWT token for /api/v3/brokerage/orders
def build_jwt(service, uri):
    private_key_bytes = key_secret.encode('utf-8')
    private_key = serialization.load_pem_private_key(private_key_bytes, password=None)
    jwt_payload = {
        'sub': key_name,
        'iss': "coinbase-cloud",
        'nbf': int(time.time()),
        'exp': int(time.time()) + 120,
        'aud': [service],
        'uri': uri,
    }
    jwt_token = jwt.encode(
        jwt_payload,
        private_key,
        algorithm='ES256',
        headers={'kid': key_name, 'nonce': secrets.token_hex()},
    )
    return jwt_token

# Main function
def main():
    # Construct URI for the orders endpoint
    orders_uri = f"{request_method} {request_host}/api/v3/brokerage/orders"
    
    # Generate JWT token for the orders endpoint
    jwt_token = build_jwt(service_name, orders_uri)
    print(f"export JWT={jwt_token}")

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=int(os.environ.get('PORT', 8080)))

but the response is still:

Error placing order. Status code: 401. Response data: Unauthorized
ERROR:main:Error processing trading data: Error in API response
Function execution took 135 ms, finished with status code: 500

From code I see you are not setting authorization header.

Could you please give me an authorization header that works? Thank you!

headers = {
     'Content-Type': 'application/json',
     'Authorization': 'Bearer YOUR_JWT_TOKEN'
}
1 Like