Cloud-Deploy Your Trading Bot — Charles Schwab APIs + Google Cloud

Carsten Savage
3 min readJul 16, 2024

--

In this article, we will harness Google Cloud to automate the token refresh workflow — a foundational component of your trading bot system. This article builds off of my first article, “The (Unofficial) Guide to Charles Schwab’s Trader APIs”.

This article is beginner-friendly and is meant to be supplementary to my two-part Charles Schwab API Foundation series on YouTube, “Build Your Own Trading Bot (Part 1)” and “Build Your Own Cloud-Deployed Trading Bot (Part 2)”.

Even if you may prefer creating the actual trading bot with another programming language, I recommend Python for the authentication components.

Directly following my YouTube videos are three Python functions essential to deploying your trading bot in Google Cloud, as well as the cloudbuild.yaml file to build your cloud function in Google Cloud.

Build Your Own Trading Bot (Part 1)

Build Your Own Cloud-Deployed Trading Bot (Part 2)

Functions

To automate the token refresh, we will need three key Python functions. Whether we’re running these locally or in the cloud, we will use these functions to grab our secrets from Google Cloud Secret Manager and store and retrieve data from Cloud Firestore.

This first Python function, “Retrieve Google Secret Dictionary”, allows us to retrieve a dictionary of secret keys and values from Secret Manager, which is where we store our most sensitive information related to logins, e.g. client keys and secrets.

from loguru import logger
import json
from google.cloud import secretmanager, firestore


def retrieve_google_secret_dict(
gcp_id, secret_id, version_id="latest"
) -> dict:
client = secretmanager.SecretManagerServiceClient()

name = f"projects/{gcp_id}/secrets/{secret_id}/versions/{version_id}"

secret_response = client.access_secret_version(request={"name": name})

secret_string = secret_response.payload.data.decode("UTF-8")

secret_dict = json.loads(secret_string)

logger.debug(f"Retrieved {version_id} secret value for {secret_id}.")

return secret_dict

Next, the “Retrieve Firestore Value” function enables us to retrieve the value of a key in a specific document of a specific collection in Cloud Firestore. Firestore serves as an efficient and cost-effective way to store our authentication tokens, which are useless without the client key and client secret.

def retrieve_firestore_value(collection_id, document_id, key) -> str:
db = firestore.Client()

try:
document = db.collection(collection_id).document(document_id)

doc = document.get()

if doc.exists:
logger.debug(f"Successfully retrieved {key} value.")
return doc.get(key)

else:
logger.error(f"Failed to retrieve {key} value")

except Exception as e:

logger.error(f"Failed to retrieve {key} value")

return None

Our last Python function, “Store Firestore Value”, enables us to write data to Cloud Firestore. In our case, we can use this to overwrite our initial authentication tokens with the new batch of tokens that we’ve refreshed with the refresh token.

def store_firestore_value(project_id, collection_id, document_id, value):
db = firestore.Client(project=project_id)

collection = db.collection(collection_id)

document = collection.document(document_id)

document.set(value)

logger.debug(f"Updated {document_id} value.")

Cloudbuild.yaml

Last but not least, below is the cloudbuild.yaml file structure that you’ll need to build you Cloud Function in Google Cloud.

steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
args:
- gcloud
- functions
- deploy
- NAME_OF_YOUR_CLOUD_FUNCTION
- --entry-point=NAME_OF_FUNCTION_TO_RUN_IN_MAIN
- --region=us-west1
- --source=.
- --trigger-http
- --gen2
- --runtime=python312
- --memory=256MB
- --timeout=90
- --min-instances=0
- --max-instances=1
- --ingress-settings=all

Note — This cloudbuild.yaml file assumes your main.py file, which is the Python code executing the token refresh, is in your root directory. If that is not the case, you’ll want to specify a subdirectory, like this:

steps:
- name: 'gcr.io/google.com/cloudsdktool/cloud-sdk'
args:
- gcloud
- functions
- deploy
- NAME_OF_YOUR_CLOUD_FUNCTION
- --entry-point=NAME_OF_FUNCTION_TO_RUN_IN_MAIN
- --region=us-west1
- --source=SUBFOLDER1/SUBFOLDER2/.
- --trigger-http
- --gen2
- --runtime=python312
- --memory=256MB
- --timeout=90
- --min-instances=0
- --max-instances=1
- --ingress-settings=all

Finally, below is Functions Framework command structure to run your Python file locally in terminal for testing:

functions-framework --target=YOUR_FUNCTION_NAME --source=FILE_NAME.py --debug

If your Python file is in a subdirectory, the command changes to:

functions-framework --target=YOUR_FUNCTION_NAME --source=SUBFOLDER1/SUBFOLDER2/FILE_NAME.py --debug

Thank you so much for reading my second Medium post. I have many more articles coming — Please make sure to subscribe and follow me for my newest content on finance, trading, and programming.

You can also find me on LinkedIn and GitHub.

--

--