Invalid API Key - PLEASE HELP

I appreciate everyone’s help. I fixed the issue, but based on your original sell code you gave me.

To make it simpler. I created a buy script, sell script, and a script to call the sell script at certain intervals.

Nice work. I updated the Auth class if you want to check it out https://forums.coinbasecloud.dev/t/tutorial-auth-websocket-send-order-get-order-python/3422

2 Likes

Sorry, I am back.

I applied your same buy code to a sell code, and for some reason, it is not selling at all.
The keys are in the auth.py script you originally showed me, so there are no changes there.

sell_script.py

import json, http.client
import numpy as np
from auth import Auth
import coinbasepro
import logging

logging.basicConfig(filename='tradebot.log', level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')

public_client = coinbasepro.PublicClient()

def get_current_price(product_id):
    ticker = public_client.get_product_ticker(product_id)
    return float(ticker['price'])

def get_crypto_balance(crypto):
    method = 'GET'
    path = '/api/v3/accounts'
    auth = Auth()
    headers = auth(method, path, '')
    conn = http.client.HTTPSConnection('api.coinbase.com')
    conn.request(method, path, '', headers)
    res = conn.getresponse()
    accounts = json.loads(res.read())

    for account in accounts:
        if account['currency'] == crypto:
            return float(account['balance'])

    return 0

def sell_crypto(cryptos):
    markets = [f"{crypto}-USD" for crypto in cryptos]
    side = 'SELL'
    max_amount_per_order = 0.10  # Example value; replace with the desired amount in the base currency

    conn = http.client.HTTPSConnection('api.coinbase.com')
    method = 'POST'
    path = '/api/v3/brokerage/orders'

    for market, crypto in zip(markets, cryptos):
        # Get the available balance for the current crypto
        available_crypto_balance = get_crypto_balance(crypto)

        # Calculate the amount to sell based on the available balance
        order_size = min(max_amount_per_order, available_crypto_balance)

        # If there is not enough balance, continue to the next crypto
        if order_size < 0.0001:
            continue

        if side == 'BUY':
            amount_id = 'quote_size'
        if side == 'SELL':
            amount_id = 'base_size'
        
        current_price = get_current_price(market)
        
        client_order_id = str(np.random.randint(2**30))
        
        payload = {
            'client_order_id': client_order_id,
            'product_id': market,
            'side': side,
            'order_configuration': {
                'market_market_ioc': {
                    amount_id: str(order_size)
                }
            }
        }
        
        payload = json.dumps(payload)
        
        auth = Auth()
        headers = auth(method, path, payload)
        
        conn.request(method, path, payload, headers)
        res = conn.getresponse()
        data = json.loads(res.read())
        
        success = data['success']
        order_id = data['order_id']

        # Add the print statements here
        print(f'Payload: {payload}')
        print(f'Headers: {headers}')
        print(f'Response: {res.status} {res.reason}')
        print(f'Data: {data}')
        
        print(f'Order for {market} placed: {success}')
        print(f'Order ID: {order_id}\n')

#auth.py

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

Are your base/quote sizes correct?

# quote_size required for BUY
# base_size required for SELL
1 Like

I put a small number, just to test that it works.

def sell_crypto(cryptos):

markets = [f"{crypto}-USD" for crypto in cryptos]
side = 'SELL'
max_amount_per_order = 0.10
1 Like

I’m not sure what the problem is, but I’ll tell you how I handle a problem that I can’t figure out. I create a new script wih a new function, then copy the code over one line at a time, testing each line as I go, so I can isolate which specific line is the problem. Hope this helps.

Thank you Mike. I will give it a try.

It weirdly had worked once, but, if I tried to place an order again, it wouldn’t go through.

1 Like

Hhmm, that sounds like a good piece of debugging evidence

It works. I know what the issue is, but not sure on how to fix it.
The issue is that the code allows for 'base_size = ‘0.5’, which if a crypto have different minimum sell amount, then it won’t work.

I am trying to make it in a way that it sells a percentage instead.

#sell_script.py

import json, http.client
import numpy as np
import logging
from datetime import datetime
from auth import Auth

# Configure logging
logging.basicConfig(filename='tradebot.log', level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')

logging.info("sell_script.py script started at: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S"))

# Inputs
markets = ['MEDIA-USD', 'ETC-USD', 'ACH-USD', 'FET-USD', 'SOL-USD']
side = 'SELL'

# Function to execute sell orders
def execute_sell_orders():
    conn = http.client.HTTPSConnection('api.coinbase.com')
    method = 'POST'
    path = '/api/v3/brokerage/orders'

    for market in markets:
        crypto_currency = market.split('-')[0]
        base_size = '0.5' # Modify this according to the available balance of each crypto

        payload = {
            'client_order_id': str(np.random.randint(2**30)),
            'product_id': market,
            'side': side,
            'order_configuration': {
                'market_market_ioc': {
                    'base_size': base_size
                }
            }
        }

        payload = json.dumps(payload)

        auth = Auth()
        headers = auth(method, path, payload)

        conn.request(method, path, payload, headers)
        res = conn.getresponse()
        data = json.loads(res.read())

        success = data['success']
        order_id = data['order_id']

        logging.debug(f"Market: {market}, Side: {side}, Success: {success}, Order ID: {order_id}")

        if not success:
            logging.error(f"Error placing order for {market}:")
            logging.error(f"Payload: {payload}")
            logging.error(f"Headers: {headers}")
            logging.error(f"Response status: {res.status}, reason: {res.reason}")
            logging.error(f"Data: {data}")

execute_sell_orders()

This makes me think of this:

For BTC-USD, BTC is base, USD is quote.

This is the response for get product of BTC_USD:

{
    "product_id": "BTC-USD",
    "price": "140.21",
    "price_percentage_change_24h": "9.43%",
    "volume_24h": "1908432",
    "volume_percentage_change_24h": "9.43%",
    "base_increment": "0.00000001",
    "quote_increment": "0.00000001",
    "quote_min_size": "0.00000001",
    "quote_max_size": "1000",
    "base_min_size": "0.00000001",
    "base_max_size": "1000",
    "base_name": "Bitcoin",
    "quote_name": "US Dollar",
    "watched": true,
    "is_disabled": "boolean",
    "new": true,
    "status": "string",
    "cancel_only": true,
    "limit_only": true,
    "post_only": true,
    "trading_disabled": "boolean",
    "auction_mode": true,
    "product_type": "string",
    "quote_currency_id": "USD",
    "base_currency_id": "BTC",
    "mid_market_price": "140.22",
    "base_display_symbol": "BTC",
    "quote_display_symbol": "USD"
  }

base_min_size is the minimum size, in BTC, that the system will accept.

quote_min_size is the minimum size, in USD, that the system will accept.

base_increment is the minimum increment that you order can be. For BTC, you can send an order of 0.000,000,01 or 0.000,000,02, but not 0.000,000,001

So, some example math that might help

import math

# round 28855.123456789 to 28855.12345679
number = 28855.123456789

base_min_size = '0.00000001'

# get number of decimal places as a number; e.g. 8
decimal_places = len(base_min_size.split(".")[1])
print(decimal_places)

# floor to decimal places
floored = math.floor(number / float(base_min_size)) * float(base_min_size)
print(floored)

# round to decimal places
rounded = round(number, decimal_places)
print(rounded)

But this is for only one crypto, when I am trying to get it to sell all listed at once, but based on percentage of each balance.

1 Like

Don’t know if I should keep this conversation going, or start a new one, but I have 2 questions.

#how to get crypto account balance?
#how to create a sell all script? Meaning to sell a ‘list’ of cryptos at once.

This is my current auth.py

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

This is my current function to get my account balances

def send_request(method, path, payload, params=''):
    auth = Auth()
    headers = auth(method + path + payload)[0]
    conn = http.client.HTTPSConnection('api.coinbase.com')
    conn.request(method, path + params, payload, headers)
    data = json.loads(conn.getresponse().read())
    return data


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 == None:
        print(data)
    elif currency != None:
        for i in data["accounts"]:
            if i["currency"] == currency:
                uuid = i["uuid"]
                balance = i["available_balance"]["value"]
    return uuid, balance


def get_account(uuid):
    method = 'GET'
    path = '/api/v3/brokerage/accounts/' + uuid
    payload = ''
    data = send_request(method, path, payload)
    print(data)


# list all accounts
list_accounts()
# list specific account
uuid, balance = list_accounts('BTC')
print(uuid)
print(balance)
get_account(uuid)

When you say sell all do you mean convert all the crypto balances you have to something like USD or USDT? If that’s what you mean I think you would have to get all accounts with non-zero balances, add the product_id’s and balance’s to a list, then use that list to loop over your sell script.

yeah, I am referring to USD. But I was trying to also write code to convert USD to USDC, and I couldn’t get it to work at all.

#usdc.py

#!/usr/bin/env python3
import json, http.client
import numpy as np
import logging
from datetime import datetime
from auth import Auth, get_api_credentials
from coinbase.wallet.client import Client as CoinbaseClient
import os

os.environ['TZ'] = 'Asia/Shanghai'
API_KEY, SECRET = get_api_credentials()

logging.basicConfig(filename='tradebot.log', level=logging.INFO, format='%(asctime)s - %(message)s', datefmt='%Y-%m-%d %H:%M:%S')

logging.info("USDC.py script started at: " + datetime.now().strftime("%Y-%m-%d %H:%M:%S"))


# Function to get the current price of a cryptocurrency
def get_current_price(product_id):
    client = CoinbaseClient(api_key=API_KEY, api_secret=SECRET)
    ticker = client.get_buy_price(currency_pair=product_id)
    return float(ticker['amount'])


## Function to get the available USD balance
def get_usd_balance():
    method = 'GET'
    path = '/api/v3/brokerage/accounts'
    auth = Auth()
    headers = auth(method, path, '')
    
    conn = http.client.HTTPSConnection('api.coinbase.com')
    conn.request(method, path, '', headers)
    res = conn.getresponse()
    data = json.loads(res.read())

    if 'data' in data:
        for account in data['data']:
            if 'currency' in account and account['currency'] == 'USD':
                return float(account['available'])
    return 0

def execute_buy_orders():
    # Initialize the Coinbase client
    api_key = 'your_api_key'
    api_secret = 'your_api_secret'
    client = CoinbaseClient(api_key, api_secret)

    # Get the available USD balance
    usd_account_id = None
    accounts = client.get_accounts()
    for account in accounts['data']:
        if account['currency'] == 'USD':
            usd_account_id = account['id']
            break
    if not usd_account_id:
        logging.error("No USD account found.")
        return

    # inputs
    markets = ['USDC-USD']
    max_amount_per_order = 5

    for market in markets:
        # calculate the size for the order
        current_price = get_current_price(market)
        amount_to_spend = min(max_amount_per_order, get_usd_balance())

        # create the order
        buy = client.buy(amount=str(amount_to_spend),
                         currency='USD',
                         payment_method='USD Wallet',
                         account=usd_account_id)

        success = buy.get('status') == 'completed'
        order_id = buy.get('id')
        if success:
            logging.info(f'Order for {market} placed: {success}')
            logging.info(f'Order ID: {order_id}\n')
            # Update the available USD balance
            available_usd_balance = get_usd_balance() - amount_to_spend
            with open('purchase_prices.json', 'a') as f:
                f.write(json.dumps({"crypto": market.split('-')[0], "purchase_price": current_price, "purchase_amount": amount_to_spend}) + '\n')
        else:
            logging.error(f"Order for {market} failed: {buy.get('status')}")

product_id = 'USDC-USD'
order_amount = 5
client = CoinbaseClient(api_key=API_KEY, api_secret=SECRET)
buy = client.buy(price=None,          # Buy at market price
                 size=order_amount,   # Amount of USDC to buy
                 currency_pair=product_id,
                 commit=True)         # Commit the order immediately
logging.info(f'Order for {product_id} placed: {buy.status}')
logging.info(f'Order ID: {buy.id}')
1 Like

I ran your code to get the balances. You are awesome. Do you have any instagram or youtube that I can follow? You have been a serious big help.

I don’t have any social media. I’ll probably set something up at some point though. Wouldn’t converting USD to USDC be the same as converting BTC to USD, so basically creating an order?

No, because you cannot trade USDC. it is not in advanced trading.

The only thing that I can see is there are USDT-USDC and USDT-USD markets, so you can technically convert USD to USDC by going USD to USDT to USDC, although that brings in an extra fee.

If you each of the trades from USDT-USD to USDT-USDC as a maker and your fees will be 0%.

That’s how I do it anytime I need to convert between USD and USDC programmatically.

3 Likes

New issue. hopefully I could still get your help.