404 Error_Buy/Sell issue

Title:
Getting 400 Bad Request error when trying to execute buy and sell orders using Coinbase API

Description:
I am trying to develop a trading bot using the Coinbase API. My script is generating buy and sell orders, but I keep getting a 400 Bad Request error when trying to execute these orders. I have included the relevant parts of the code below, which involve the send_request function and the code for creating buy and sell orders.

Response Status: 400
Response Reason: Bad Request
Error: Request-sent
Error: Request-sent
Error: Request-sent
Error: Request-sent
Traceback (most recent call last):
  File "/Users/path/tradebot2/test_trading_indicators.py", line 70, in <module>
    test_api_requests()
  File "/Users/path/tradebot2/test_trading_indicators.py", line 34, in test_api_requests
    buy_order = send_request(
  File "/Users/path/tradebot2/test_trading_indicators.py", line 26, in send_request
    raise Exception(f"Failed to send request after {max_retries} attempts.")
Exception: Failed to send request after 5 attempts.

here is the script that I am running.

import json
import http.client
import time
from auth import Auth

def send_request(method, path, payload, params='', max_retries=5, retry_delay=5):
    auth = Auth()
    headers = auth(method, path, payload)
    conn = http.client.HTTPSConnection('api.coinbase.com')

    for _ in range(max_retries):
        try:
            conn.request(method, path + params, payload, headers)
            response = conn.getresponse()
            if response.status != 200:
                print(f"Response Status: {response.status}")
                print(f"Response Reason: {response.reason}")
                time.sleep(retry_delay)
                continue
            data = json.loads(response.read())
            return data
        except Exception as e:
            print(f"Error: {e}")
            time.sleep(retry_delay)
    raise Exception(f"Failed to send request after {max_retries} attempts.")

# Replace market and trade_amount with actual values
market = 'BTC-USD'
trade_amount = 100

# For buy orders
buy_order = send_request(
    'POST',
    '/api/v3/brokerage/orders',
    json.dumps({
        "product_id": market,
        "side": 'buy',
        "order_configuration": {
            "market_market_ioc": {
                "quote_size": str(trade_amount)
            }
        }
    }))

# For sell orders
sell_order = send_request(
    'POST',
    '/api/v3/brokerage/orders',
    json.dumps({
        "product_id": market,
        "side": 'sell',
        "order_configuration": {
            "market_market_ioc": {
                "base_size": str(trade_amount)
            }
        }
    }))

I’m not sure when exactly our paths diverged so I don’t know what specific Auth class you’re using. If it’s this:

import time, hmac, hashlib

class Auth:
    def __init__(self):
        self.API_KEY = ''
        self.SECRET = ''
    
    def __call__(self, message):
        timestamp = str(int(time.time()))
        signature = hmac.new(self.SECRET.encode('utf-8'), (timestamp + message).encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
        headers = {
            'CB-ACCESS-KEY': self.API_KEY,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-SIGN': signature,
            'Content-Type': 'application/json'
        }
        return headers, signature, self.API_KEY, timestamp

it returns 4 things and headers is [0] so replace this:

#headers = auth(method, path, payload)
headers = auth(method + path + payload)[0]

Then add client_order_id to the payload:

import numpy as np
'client_order_id': str(np.random.randint(2**30))

And change buy to BUY and sell to SELL. Working code:

import json
import http.client
import time
from auth import Auth
import numpy as np

def send_request(method, path, payload, params='', max_retries=1, retry_delay=1):
    auth = Auth()
    #headers = auth(method, path, payload)
    headers = auth(method + path + payload)[0]
    conn = http.client.HTTPSConnection('api.coinbase.com')

    for _ in range(max_retries):
        try:
            conn.request(method, path + params, payload, headers)
            response = conn.getresponse()
            if response.status != 200:
                print(f"Response Status: {response.status}")
                print(f"Response Reason: {response.reason}")
                time.sleep(retry_delay)
                continue
            data = json.loads(response.read())
            return data
        except Exception as e:
            print(f"Error: {e}")
            time.sleep(retry_delay)
    raise Exception(f"Failed to send request after {max_retries} attempts.")

# Replace market and trade_amount with actual values
market = 'BTC-USDT'
#trade_amount = 2.8
trade_amount = 0.0001

# For buy orders
buy_order = send_request(
    'POST',
    '/api/v3/brokerage/orders',
    json.dumps({
        'client_order_id': str(np.random.randint(2**30)),
        "product_id": market,
        "side": 'BUY',
        "order_configuration": {
            "market_market_ioc": {
                "quote_size": str(trade_amount)
            }
        }
    }))

# For sell orders
sell_order = send_request(
    'POST',
    '/api/v3/brokerage/orders',
    json.dumps({
        'client_order_id': str(np.random.randint(2**30)),
        "product_id": market,
        "side": 'SELL',
        "order_configuration": {
            "market_market_ioc": {
                "base_size": str(trade_amount)
            }
        }
    }))

I will try it, but for now, here is what my auth.py script looks like.

this is what my auth.py code looks like.

import time, hmac, hashlib

API_KEY = ''
SECRET = ''

def get_api_credentials():
    API_KEY = ''
    SECRET = ''

    return API_KEY, SECRET


timestamp = str(int(time.time()))

# for websocket
def sign_message(message):
    message = hmac.new(SECRET.encode('utf-8'), message.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
    return message

# for API
class Auth:
    def __init__(self):
        self.API_KEY = API_KEY
        self.SECRET = SECRET
    
    def __call__(self, method, path, payload):
        message = timestamp + method + path + payload
        signature = hmac.new(self.SECRET.encode('utf-8'), message.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()

        headers = {
            'CB-ACCESS-KEY': self.API_KEY,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-SIGN': signature,
            'Content-Type': 'application/json'
        }
        return headers

Here is a brief description and the sections of my scripts that are returning errors:

Error Message:

Response Status: 401
Response Reason: Unauthorized
Error: Request-sent
Error: Request-sent
Error: Request-sent
Error: Request-sent
Traceback (most recent call last):
  File "/Users/path/tradebot2/crypto_trading_bot.py", line 147, in <module>
    trade_bot()
  File "/Users/path/tradebot2/crypto_trading_bot.py", line 72, in trade_bot
    _, balance = list_accounts(currency=currency)
  File "/Users/cpath/tradebot2/crypto_trading_bot.py", line 43, in list_accounts
    data = send_request(method, path, payload, params='?limit=250')
  File "/Users/path/tradebot2/crypto_trading_bot.py", line 37, in send_request
    raise Exception(f"Failed to send request after {max_retries} attempts.")
Exception: Failed to send request after 5 attempts.

Problem:
The error is a 401 Unauthorized status which indicates that the request requires user authentication. The issue lies with the authentication process, specifically with generating the correct headers for the API request.

Relevant Code Sections:

  1. auth.py
class Auth:
    def __init__(self):
        self.API_KEY = API_KEY
        self.SECRET = SECRET
    
    def __call__(self, method, path, payload=''):
        timestamp = str(int(time.time()))
        message = timestamp + method + path + (payload or '')
        signature = hmac.new(self.SECRET.encode('utf-8'), message.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()

        headers = {
            'CB-ACCESS-KEY': self.API_KEY,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-SIGN': signature,
            'Content-Type': 'application/json'
        }
        return headers
  1. crypto_trading_bot.py
def send_request(method, path, payload=None, params='', max_retries=5, retry_delay=5):
    auth = Auth()
    headers = auth(method, path, str(payload or ''))  # Convert payload to string
    conn = http.client.HTTPSConnection('api.coinbase.com')

    for _ in range(max_retries):
        try:
            conn.request(method, path + params, payload, headers)
            response = conn.getresponse()
            if response.status != 200:
                print(f"Response Status: {response.status}")
                print(f"Response Reason: {response.reason}")
                time.sleep(retry_delay)
                continue
            data = json.loads(response.read())
            return data
        except Exception as e:
            print(f"Error: {e}")
            time.sleep(retry_delay)
    raise Exception(f"Failed to send request after {max_retries} attempts.")

Possible Solution:
Ensure the API_KEY and SECRET are correctly set, and that the payload is being handled properly in the Auth class. Also, ensure that the timestamp is generated at each request, not just when the Auth class is instantiated.

Well those two things were definitely set wrong, client_order_id and buy/BUY, so try that out and see if it works. As for the timestamp issue, that will be a problem, you could do something like having the retry logic outside of the send_request function, so that each retry is a new send_request call. That’s pretty much how mine works.

Your buy/sell code works, but I am still running into an error.

#error message

Failed request. Status: 401, Headers: [('Date', 'Tue, 09 May 2023 17:00:14 GMT'), ('Content-Type', 'text/plain; charset=utf-8'), ('Content-Length', '13'), ('Connection', 'keep-alive'), ('Trace-Id', '7503526623752704635'), ('X-Content-Type-Options', 'nosniff'), ('CF-Cache-Status', 'DYNAMIC'), ('Set-Cookie', '__cf_bm=qzAY8mHnipVM75CqTpFEpy1kgAn.krTUls4ZVZ5CEXo-1683651614-0-AeYlBvUH08byoinIGi0kBB/q4YwFVgHEHzOkH43e3WhaqPWjCnjU1+kTFvFHlHS8PHrOpKDIqFSlDRVb9xmbN8Q=; path=/; expires=Tue, 09-May-23 17:30:14 GMT; domain=.coinbase.com; HttpOnly; Secure'), ('Strict-Transport-Security', 'max-age=31536000; includeSubDomains; preload'), ('Server', 'cloudflare'), ('CF-RAY', '7c4b7f5b2ceb0928-LAX')], Body: b'Unauthorized\n'
Traceback (most recent call last):
  File "/Users/tradebot2/crypto_trading_bot.py", line 145, in <module>
    trade_bot()
  File "/Users/tradebot2/crypto_trading_bot.py", line 83, in trade_bot
    available_products = get_available_products(markets)  # Pass 'markets' parameter here
  File "/Users/tradebot2/trading_indicators.py", line 44, in get_available_products
    data = send_request(method, path, payload)
  File "/Users/tradebot2/trading_indicators.py", line 37, in send_request
    raise Exception(f"Failed to send request after {max_retries} attempts.")
Exception: Failed to send request after 3 attempts.

#auth.py

import time
import hmac
import hashlib
import base64

API_KEY = "<API_KEY>"
SECRET = "<SECRET>"

class Auth():
    def __init__(self):
        self.api_key = API_KEY
        self.secret = SECRET

    def __call__(self, method, request_path, body):
        timestamp = str(time.time())
        message = timestamp + method + request_path + body
        hmac_key = base64.b64decode(self.secret)
        signature = hmac.new(hmac_key, message.encode('utf-8'), hashlib.sha256)
        signature_b64 = base64.b64encode(signature.digest()).decode('utf-8')

        return {
            'Content-Type': 'Application/JSON',
            'CB-ACCESS-KEY': self.api_key,
            'CB-ACCESS-SIGN': signature_b64,
            'CB-ACCESS-TIMESTAMP': timestamp
        }

#main script

import json
import http.client
import time
from auth import Auth

def send_request(method, path, payload, params='', max_retries=3, retry_delay=2):
    auth = Auth()
    headers = auth(method, path, payload)
    conn = http.client.HTTPSConnection('api.coinbase.com')
    retry_count = 0

    while retry_count < max_retries:
        try:
            conn.request(method, path + params, payload, headers)
            response = conn.getresponse()

            if response.status == 429:  # Rate limit exceeded
                time.sleep(int(response.getheader("Retry-After", retry_delay)))
                retry_count += 1
                continue
            elif response.status != 200:  # Other non-successful status codes
                print(f"Failed request. Status: {response.status}, Headers: {response.getheaders()}, Body: {response.read()}")
                retry_count += 1
                time.sleep(retry_delay)
                continue

            data = json.loads(response.read())
            return data
        except Exception as e:
            print(f"Request failed with error: {e}")
            retry_count += 1
            time.sleep(retry_delay)

    raise Exception(f"Failed to send request after {max_retries} attempts.")


def get_available_products(markets):
    method = 'GET'
    path = '/api/v3/brokerage/products'
    payload = ''
    data = send_request(method, path, payload)
    
    if 'errors' in data:
        print("Error:", data['errors'][0]['message'])
        return []

    filtered_products = [product for product in data['products'] if product['product_id'] in markets]
    return filtered_products

if __name__ == "__main__":
    markets = ['MEDIA-USD', 'ETC-USD', 'ACH-USD', 'FET-USD', 'SOL-USD']
    available_products = get_available_products(markets)

I tested your auth vs my auth and you are returning the wrong CB-ACCESS-SIGN, so that’s your problem, you need to figure out how you want to fix that problem. You could just copy my auth if you want, and use this to access the headers:

headers = auth(method + path + payload)[0]

and this to unpack the other return values:

channel = 'ticker'
r = auth(channel + market)
signature, API_KEY, timestamp = r[1], r[2], r[3]

Where channel + market is the required message for websocket. My auth call takes one param message instead of 3 params method, request_path, body, so you just concatenate the params on the call.

Yours:

import time
import hmac
import hashlib
import base64

API_KEY = ""
SECRET = ""

class Auth():
    def __init__(self):
        self.api_key = API_KEY
        self.secret = SECRET

    def __call__(self, method, request_path, body):
        timestamp = str(time.time())
        message = timestamp + method + request_path + body
        hmac_key = base64.b64decode(self.secret)
        signature = hmac.new(hmac_key, message.encode('utf-8'), hashlib.sha256)
        signature_b64 = base64.b64encode(signature.digest()).decode('utf-8')

        return {
            'Content-Type': 'Application/JSON',
            'CB-ACCESS-KEY': self.api_key,
            'CB-ACCESS-SIGN': signature_b64,
            'CB-ACCESS-TIMESTAMP': timestamp
        }

method = 'GET'
path = '/api/v3/brokerage/products'
payload = ''
auth = Auth()
headers = auth(method, path, payload)
print(headers)

Mine:

import time
import hmac
import hashlib

API_KEY = ""
SECRET = ""

class Auth:
    def __init__(self):
        self.CB_API_KEY = API_KEY
        self.CB_SECRET = SECRET
    
    def __call__(self, message):
        timestamp = str(int(time.time()))
        signature = hmac.new(self.CB_SECRET.encode('utf-8'), (timestamp + message).encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
        headers = {
            'CB-ACCESS-KEY': self.CB_API_KEY,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-SIGN': signature,
            'Content-Type': 'application/json'
        }
        return headers, signature, self.CB_API_KEY, timestamp

method = 'GET'
path = '/api/v3/brokerage/products'
payload = ''
auth = Auth()
headers = auth(method + path + payload)[0]
print(headers)

Also, I believe the way you’re doing it will cause your timestamp to expire after a few retries, so would this make more sense?

def send_request(method, path, payload, params='', max_retries=3, retry_delay=2):
    retry_count = 0

    while retry_count < max_retries:
        try:
            auth = Auth()
            headers = auth(method, path, payload)
            conn = http.client.HTTPSConnection('api.coinbase.com')
            conn.request(method, path + params, payload, headers)
            response = conn.getresponse()

Also, not sure if this is important or not, but your timestamp doesn’t int():

# yours:
timestamp = str(time.time())
# mine:
timestamp = str(int(time.time()))

edit - made a mistake by running them with different timestamps, to compare the two auths for testing substitute:

#timestamp = str(int(time.time()))
timestamp = str(1683680682)

I looked at this again and solved it if you want to use your auth, your hmac_key was wrong

import time
import hmac
import hashlib
import base64

API_KEY = ""
SECRET = ""

class Auth():
    def __init__(self):
        self.api_key = API_KEY
        self.secret = SECRET

    def __call__(self, method, request_path, body):
        #timestamp = str(int(time.time()))
        timestamp = str(1683680682)
        message = timestamp + method + request_path + body
        #hmac_key = base64.b64decode(self.secret)
        hmac_key = self.secret.encode('utf-8')
        signature = hmac.new(hmac_key, message.encode('utf-8'), hashlib.sha256)
        signature_b64 = signature.hexdigest()

        return {
            'Content-Type': 'Application/JSON',
            'CB-ACCESS-KEY': self.api_key,
            'CB-ACCESS-SIGN': signature_b64,
            'CB-ACCESS-TIMESTAMP': timestamp
        }

method = 'GET'
path = '/api/v3/brokerage/products'
payload = ''
auth = Auth()
headers = auth(method, path, payload)
print(headers)
import time
import hmac
import hashlib

API_KEY = ""
SECRET = ""

class Auth:
    def __init__(self):
        self.CB_API_KEY = API_KEY
        self.CB_SECRET = SECRET
    
    def __call__(self, message):
        #timestamp = str(int(time.time()))
        timestamp = str(1683680682)
        signature = hmac.new(self.CB_SECRET.encode('utf-8'), (timestamp + message).encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
        headers = {
            'CB-ACCESS-KEY': self.CB_API_KEY,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-SIGN': signature,
            'Content-Type': 'application/json'
        }
        return headers, signature, self.CB_API_KEY, timestamp

method = 'GET'
path = '/api/v3/brokerage/products'
payload = ''
auth = Auth()
headers = auth(method + path + payload)[0]
print(headers)

now they match

it seems to be working. I really appreciate your help.

Awesome, no problem.

how can I keep running into the same issue?

No markets to trade.
MEDIA balance: 0.0005830584695009
ETC balance: 0.0000000009560910
ACH balance: 0.0469744170654206
FET balance: 0.0459576695878804
SOL balance: 0.0009579198030976
USD balance: 132.7270413141750500
Sleeping for 10790 seconds until the next trading time 01:00:00.
Available products: [{'product_id': 'SOL-USD', 'price': '20.23', 'price_percentage_change_24h': '-0.83333333333333', 'volume_24h': '366269.186', 'volume_percentage_change_24h': '-47.84592958198064', 'base_increment': '0.001', 'quote_increment': '0.01', 'quote_min_size': '1', 'quote_max_size': '25000000', 'base_min_size': '0.08', 'base_max_size': '780000', 'base_name': 'Solana', 'quote_name': 'US Dollar', 'watched': False, 'is_disabled': False, 'new': False, 'status': 'online', 'cancel_only': False, 'limit_only': False, 'post_only': False, 'trading_disabled': False, 'auction_mode': False, 'product_type': 'SPOT', 'quote_currency_id': 'USD', 'base_currency_id': 'SOL', 'fcm_trading_session_details': None, 'mid_market_price': '', 'alias': '', 'alias_to': [], 'base_display_symbol': 'SOL', 'quote_display_symbol': 'USD', 'view_only': False, 'price_increment': '0.01'}, {'product_id': 'FET-USD', 'price': '0.2571', 'price_percentage_change_24h': '-3.09084055785903', 'volume_24h': '14940275.1', 'volume_percentage_change_24h': '41.38840115205009', 'base_increment': '0.1', 'quote_increment': '0.0001', 'quote_min_size': '1', 'quote_max_size': '10000000', 'base_min_size': '2', 'base_max_size': '760000', 'base_name': 'Fetch.ai', 'quote_name': 'US Dollar', 'watched': False, 'is_disabled': False, 'new': False, 'status': 'online', 'cancel_only': False, 'limit_only': False, 'post_only': False, 'trading_disabled': False, 'auction_mode': False, 'product_type': 'SPOT', 'quote_currency_id': 'USD', 'base_currency_id': 'FET', 'fcm_trading_session_details': None, 'mid_market_price': '', 'alias': '', 'alias_to': [], 'base_display_symbol': 'FET', 'quote_display_symbol': 'USD', 'view_only': False, 'price_increment': '0.0001'}, {'product_id': 'ACH-USD', 'price': '0.026161', 'price_percentage_change_24h': '-0.95032560957141', 'volume_24h': '48232352.5', 'volume_percentage_change_24h': '-37.48951926433623', 'base_increment': '0.1', 'quote_increment': '0.000001', 'quote_min_size': '1', 'quote_max_size': '10000000', 'base_min_size': '110', 'base_max_size': '20000000', 'base_name': 'Alchemy Pay', 'quote_name': 'US Dollar', 'watched': False, 'is_disabled': False, 'new': False, 'status': 'online', 'cancel_only': False, 'limit_only': False, 'post_only': False, 'trading_disabled': False, 'auction_mode': False, 'product_type': 'SPOT', 'quote_currency_id': 'USD', 'base_currency_id': 'ACH', 'fcm_trading_session_details': None, 'mid_market_price': '', 'alias': '', 'alias_to': [], 'base_display_symbol': 'ACH', 'quote_display_symbol': 'USD', 'view_only': False, 'price_increment': '0.000001'}, {'product_id': 'ETC-USD', 'price': '18.35', 'price_percentage_change_24h': '0.21845985800109', 'volume_24h': '17581.93832104', 'volume_percentage_change_24h': '-59.81754439088938', 'base_increment': '0.00000001', 'quote_increment': '0.01', 'quote_min_size': '1', 'quote_max_size': '10000000', 'base_min_size': '0.018', 'base_max_size': '44000', 'base_name': 'Ethereum Classic', 'quote_name': 'US Dollar', 'watched': False, 'is_disabled': False, 'new': False, 'status': 'online', 'cancel_only': False, 'limit_only': False, 'post_only': False, 'trading_disabled': False, 'auction_mode': False, 'product_type': 'SPOT', 'quote_currency_id': 'USD', 'base_currency_id': 'ETC', 'fcm_trading_session_details': None, 'mid_market_price': '', 'alias': '', 'alias_to': [], 'base_display_symbol': 'ETC', 'quote_display_symbol': 'USD', 'view_only': False, 'price_increment': '0.01'}, {'product_id': 'MEDIA-USD', 'price': '9.19', 'price_percentage_change_24h': '2.45261984392419', 'volume_24h': '2498.688', 'volume_percentage_change_24h': '-23.06618458231176', 'base_increment': '0.001', 'quote_increment': '0.01', 'quote_min_size': '1', 'quote_max_size': '10000000', 'base_min_size': '0.0385694847882526', 'base_max_size': '385694.8478825260614222', 'base_name': 'Media Network', 'quote_name': 'US Dollar', 'watched': False, 'is_disabled': False, 'new': False, 'status': 'online', 'cancel_only': False, 'limit_only': False, 'post_only': False, 'trading_disabled': False, 'auction_mode': False, 'product_type': 'SPOT', 'quote_currency_id': 'USD', 'base_currency_id': 'MEDIA', 'fcm_trading_session_details': None, 'mid_market_price': '', 'alias': '', 'alias_to': [], 'base_display_symbol': 'MEDIA', 'quote_display_symbol': 'USD', 'view_only': False, 'price_increment': '0.01'}]
Filtered markets: []
No markets to trade.
MEDIA balance: 0.0005830584695009
ETC balance: 0.0000000009560910
ACH balance: 0.0469744170654206
FET balance: 0.0459576695878804
SOL balance: 0.0009579198030976
USD balance: 132.7270413141750500
Sleeping for 14390 seconds until the next trading time 05:00:00.

#crypto_trading_bot.py

import http.client
import json
import time
import datetime
import logging
import pytz
from auth import Auth
from trading_indicators import trading_algo, get_available_products

# Configure logging
logging.basicConfig(filename='tradebot.log', level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')

markets = ['MEDIA-USD', 'ETC-USD', 'ACH-USD', 'FET-USD', 'SOL-USD']
crypto_currencies = [market.split("-")[0] for market in markets]
budget = 300

def send_request(method, path, payload, params='', max_retries=3, retry_delay=2):
    retry_count = 0

    while retry_count < max_retries:
        try:
            auth = Auth()
            headers = auth(method, path, payload)  # Pass the parameters separately
            conn = http.client.HTTPSConnection('api.coinbase.com')
            conn.request(method, path + params, payload, headers)
            response = conn.getresponse()

            if response.status == 200:
                return json.loads(response.read().decode('utf-8'))
            else:
                raise Exception(f"Failed request. Status: {response.status}, Headers: {response.getheaders()}, Body: {response.read()}")
        except Exception as e:
            print(e)
            retry_count += 1
            time.sleep(retry_delay)

    raise Exception(f"Failed to send request after {max_retries} attempts.")

def list_accounts(currency=None, uuid=None, balance=None):
    method = 'GET'
    path = '/api/v3/brokerage/accounts'
    payload = ''
    data = send_request(method, path, payload, params='?limit=250')

    if currency is not None and "accounts" in data:
        for i in data["accounts"]:
            if i["currency"] == currency:
                uuid = i["uuid"]
                balance = i["available_balance"]["value"]
                if currency != "USD":
                    print(f"{currency} balance: {balance}")
                return uuid, balance
    elif "accounts" not in data:
        print("Error: Data not found in the response")
        return None, None
    else:
        return data


def trade_bot():
    global markets
    trading_times = ["01:00:00", "05:00:00", "08:00:00", "10:00:00", "13:00:00", "15:00:00", "17:00:00", "20:00:00", "22:00:00"]

    while True:
        now = datetime.datetime.now(pytz.timezone('Asia/Shanghai'))
        current_time = now.time()
        trading_times_dt = [datetime.datetime.strptime(t, "%H:%M:%S").time() for t in trading_times]
        closest_time, sleep_seconds = min(((t, (datetime.datetime.combine(datetime.date.min, t) - datetime.datetime.combine(datetime.date.min, current_time)).seconds % 86400) for t in trading_times_dt), key=lambda x: x[1])

        # Print account balances before sleeping
        for currency in crypto_currencies:
            _, balance = list_accounts(currency=currency)

        # Retrieve USD balance
        _, usd_balance = list_accounts(currency="USD")
        print(f"USD balance: {usd_balance}")

        # Calculate trade_amount based on the current USD balance and the number of markets
        usd_balance = float(usd_balance)

        print(f"Sleeping for {sleep_seconds} seconds until the next trading time {closest_time.strftime('%H:%M:%S')}.") 
        time.sleep(sleep_seconds)

        available_products = get_available_products(markets)  # Pass 'markets' parameter here
        print("Available products:", available_products)

        # Filter the markets based on available products
        filtered_markets = list(filter(lambda x: x in available_products, markets))
        print("Filtered markets:", filtered_markets)

        if filtered_markets:  # This checks if the list is not empty
            trade_amount = round(usd_balance / len(filtered_markets), 2)
        else:
            print("No markets to trade.")
            trade_amount = 0

        for market in filtered_markets:
            currency = market.split("-")[0]
            trade_decision = trading_algo(market)
            print(f"Trade decision for {market}: {trade_decision}")

            _, balance = list_accounts(currency=currency)

            if trade_decision['buy']:
                print(f"Buying {market}")

                try:
                    # For buy orders
                    buy_order = send_request(
                        'POST',
                        '/api/v3/brokerage/orders',
                        json.dumps({
                            "product_id": market,
                            "side": 'buy',
                            "order_configuration": {
                                "market_market_ioc": {
                                    "quote_size": str(trade_amount)
                                }
                            }
                        }))

                except Exception as e:
                    logging.error(f"Error placing buy order for {market}: {e}")

            elif trade_decision['sell'] and balance is not None:
                print(f"Selling {market}")
                try:
                    # For sell orders
                    sell_order = send_request(
                        'POST',
                        '/api/v3/brokerage/orders',
                        json.dumps({
                            "product_id": market,
                            "side": 'sell',
                            "order_configuration": {
                                "market_market_ioc": {
                                    "base_size": balance  # Use the available balance for the sell order
                                }
                            }
                        }))

                except Exception as e:
                    logging.error(f"Error placing sell order for {market}: {e}")

        #time.sleep(60)



if __name__ == "__main__":
    trade_bot()

#trading_indicator.py

import pandas as pd
import numpy as np
import talib
from auth import Auth
import http.client
import json
import time

def send_request(method, path, payload, params='', max_retries=3, retry_delay=2):
    auth = Auth()
    headers = auth(method, path, payload)  # Pass the parameters separately
    conn = http.client.HTTPSConnection('api.coinbase.com')
    retry_count = 0

    while retry_count < max_retries:
        try:
            conn.request(method, path + params, payload, headers)
            response = conn.getresponse()

            if response.status == 429:  # Rate limit exceeded
                time.sleep(int(response.getheader("Retry-After", retry_delay)))
                retry_count += 1
                continue
            elif response.status != 200:  # Other non-successful status codes
                print(f"Failed request. Status: {response.status}, Headers: {response.getheaders()}, Body: {response.read()}")
                retry_count += 1
                time.sleep(retry_delay)
                continue

            data = json.loads(response.read())
            return data
        except Exception as e:
            print(f"Request failed with error: {e}")
            retry_count += 1
            time.sleep(retry_delay)

    raise Exception(f"Failed to send request after {max_retries} attempts.")


def get_available_products(markets):
    method = 'GET'
    path = '/api/v3/brokerage/products'
    payload = ''
    data = send_request(method, path, payload)
    
    if 'errors' in data:
        print("Error:", data['errors'][0]['message'])
        return []

    filtered_products = [product for product in data['products'] if product['product_id'] in markets]
    return filtered_products

def get_historical_data(product_id, granularity=300):
    method = 'GET'
    path = f'/products/{product_id}/candles'
    payload = ''
    data = send_request(method, path, payload, params=f'?granularity={granularity}')
    print("Raw data:", data)
    df = pd.DataFrame(data, columns=['time', 'low', 'high', 'open', 'close', 'volume'])
    print("DataFrame:", df)
    return df

def compute_sma(data, window):
    sma = talib.SMA(data.close, timeperiod = window)
    return sma

def compute_ema(data, window):
    ema = talib.EMA(data.close, timeperiod = window)
    return ema

def compute_macd(data, short_window, long_window):
    macd, signal, hist = talib.MACD(data.close, fastperiod=short_window, slowperiod=long_window, signalperiod=9)
    return macd, signal, hist

def compute_rsi(data, window):
    rsi = talib.RSI(data.close, timeperiod = window)
    return rsi

def compute_stochastic(data, window):
    slowk, slowd = talib.STOCH(data.high, data.low, data.close, fastk_period=14, slowk_period=window, slowk_matype=0, slowd_period=3, slowd_matype=0)
    return slowk, slowd

def compute_bollinger(data, window):
    upper, middle, lower = talib.BBANDS(data.close, timeperiod = window, nbdevup = 2, nbdevdn = 2, matype = 0)
    return upper, middle, lower

def trading_algo(data):
    rsi_threshold = 45
    sma_short_period = 5
    sma_long_period = 20
    sma_tolerance = 0.01

    rsi = compute_rsi(data, period=14)
    sma_short = compute_sma(data, window=sma_short_period)
    sma_long = compute_sma(data, window=sma_long_period)

    if rsi[-1] < rsi_threshold and \
        (1 - sma_tolerance) * sma_short[-1] < data['close'].iloc[-1] < (1 + sma_tolerance) * sma_short[-1] and \
        (1 - sma_tolerance) * sma_long[-1] < data['close'].iloc[-1] < (1 + sma_tolerance) * sma_long[-1]:
        return True  # Here, instead of placing a buy order, I'm returning True. Adapt this line to suit your actual trading environment.
    else:
        return False

if __name__ == "__main__":
    markets = ['MEDIA-USD', 'ETC-USD', 'ACH-USD', 'FET-USD', 'SOL-USD']
    available_products = get_available_products(markets)
    
    for product in available_products:
        product_id = product['product_id']
        print(f"Running trading algorithm for {product_id}")
        
        data = get_historical_data(product_id)  # Add this line
        buy_signal = trading_algo(data)  # Adjust this line

        print(f"Buy signal: {buy_signal}\n")  # Adjust this line

What exactly is the problem? Auth again?

its not making any purchases. I let it run for a week, and nothing.

#auth.py

import time, hmac, hashlib

API_KEY = ''
SECRET = ''

def get_api_credentials():
    API_KEY = ''
    SECRET = ''

    return API_KEY, SECRET


# for websocket
def sign_message(message):
    message = hmac.new(SECRET.encode('utf-8'), message.encode('utf-8'), digestmod=hashlib.sha256).hexdigest()
    return message

# for API
class Auth:
    def __init__(self):
        self.API_KEY = API_KEY
        self.SECRET = SECRET

    def __call__(self, method, path, payload):
        timestamp = str(int(time.time()))
        message = timestamp + method + path + payload
        signature = hmac.new(self.SECRET.encode('utf-8'), message.encode('utf-8'), hashlib.sha256).hexdigest()

        headers = {
            'CB-ACCESS-KEY': self.API_KEY,
            'CB-ACCESS-TIMESTAMP': timestamp,
            'CB-ACCESS-SIGN': signature,
            'Content-Type': 'application/json'
        }
        return headers

Ok well, it sounds like a testing/debugging problem right? So you need to figure out a way to test your code to see which specific part doesn’t work. How would you go about doing that?

Do you know if the list of errors codes for the Advanced Trade matches this: Error response | Coinbase Cloud?

What would you charge to get this tradbot to work for me?