Port of my original traderJS module from 2 years ago to Python. Python is a more utilitarian language when it comes to automation of trading, so this module will allow for users to utilize this language with their Tradovate accounts.
- Create a Tradovate account and register an app
- Create accessToken.json (leave blank or if there's an error, setup as {"accessToken":""})
- Create a .env (fill it in with all your account details)
- Run the file calling the refresh access token function in order to obtain an access token. This is how you will authenticate sending orders and performing core account actions.
- Place orders and call the refres access token function in a loop in order to ensure that it never expires
- Get rich? I don't know - up to you.
If you do not understand any variable names defined in the trader.py file, please refer to Tradovate's official documentation. I have kept all of the variable names the same as the ones which they use on their website. https://api.tradovate.com/#section/Getting-Started-With-the-Tradovate-API
import datetime
import requests
import json
import dotenv
import os
# Toggle between sending requests to the live or demo environment
# I would recommend using the demo environment to test your code before going live
# WARNING: DO NOT SWITCH TO MD - THIS WILL BREAK EVERYTHING
# MD IS MARKET DATA AND DOES NOT ALLOW FOR ORDER PLACEMENT OR ACCOUNT AUTHENTICATION
# version = "live"
version = "demo"
dotenv.load_dotenv()
# These are the environment variables that you need to set in your .env file
name = os.getenv('TRADOVATE_NAME')
password = os.getenv('TRADOVATE_PASSWORD')
app_id = os.getenv('TRADOVATE_APP_ID')
app_version = os.getenv('TRADOVATE_APP_VERSION')
cid = os.getenv('TRADOVATE_CID')
sec = os.getenv('TRADOVATE_SEC')
device_id = os.getenv('TRADOVATE_DEVICE_ID')
# This function will place an order for you
def place_order(symbol, order_action, quantity, account_spec, account_id):
with open('./accessToken.json') as f:
access_token = json.load(f)['accessToken']
initial = {
"accountSpec": account_spec,
"accountId": account_id,
"symbol": symbol,
"action": order_action,
"orderQty": quantity,
"orderType": "Market",
"isAutomated": True
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}"
}
response = requests.post(f"https://{version}.tradovateapi.com/v1/order/placeorder", headers=headers, data=json.dumps(initial))
print(f'Tradovate Status: {response.status_code}')
print(response.text)
# This function will cancel an order for you
def cancel_order(account_id, contract_id):
with open('./accessToken.json') as f:
access_token = json.load(f)['accessToken']
body = {
"accountId": account_id,
"contractId": contract_id,
"admin": False
}
headers = {
"Content-Type": "application/json",
"Authorization": f"Bearer {access_token}"
}
response = requests.post(f'https://{version}.tradovateapi.com/v1/order/liquidateposition', headers=headers, data=json.dumps(body))
print(f'Tradovate Status: {response.status_code}')
print(response.text)
# This function will refresh your access token
# It is advisable to call this function in a loop to keep your access token fresh
# As of my present knowledge, the access token expires every hour
def refresh_access_token():
print(f"Refreshing your access token... [{datetime.datetime.now()}]")
# You must register an app on tradovate's website prior to these variables working properly
body = {
"name": f"{name}",
"password": f"{password}",
"appId": f"{app_id}",
"appVersion": f"{app_version}",
"cid": cid,
"sec": f"{sec}",
"deviceId": f"{device_id}"
}
response = requests.post(f'https://{version}.tradovateapi.com/v1/auth/accesstokenrequest', json=body)
if response.status_code == 200:
print(f"Access token successfully refreshed [{datetime.datetime.now().isoformat()}]")
try:
data = {"accessToken": response.json()['accessToken']}
except Exception as e:
# Most common exception is thrown because you forgot to register your app properly
print(f"Error: {e}")
print(f"Response: {response.text}")
return
with open("./accessToken.json", 'w') as f:
json.dump(data, f)In the event that you do not know your device ID and are hellbent on using python, it becomes difficult to get an actual unique identifier which works with Tradovate. The brokerage themselves recommends the npm library "device-uuid" in javascript, however, js and py are a massive headache to use in conjunction, in my opinion. This is why I have also created a program which replicates the uuid generated by the device-uuid npm library. In the event that this does not work for some reason, just use the npm library - this usually only works with windows and linux devices (sorry mac users).
python -m pip install screeninfo
import hashlib
import platform
import psutil
import uuid
import socket
import uuid
from datetime import datetime, time, timezone
from screeninfo import get_monitors
from PIL import Image
def get_time_zone_offset():
utc_offset_sec = datetime.now(timezone.utc).astimezone().utcoffset().total_seconds()
utc_offset_hours = int(utc_offset_sec / 3600)
return str(utc_offset_hours)
def get_screen_resolution():
monitors = get_monitors()
if monitors:
monitor = monitors[0]
return f"{monitor.width}x{monitor.height}"
return "Unknown"
def get_color_depth():
img = Image.new('RGB', (1, 1))
return img.mode
def format_uuid(uuid_str):
return f"{uuid_str[:8]}-{uuid_str[8:12]}-{uuid_str[12:16]}-{uuid_str[16:20]}-{uuid_str[20:]}"
def get_device_uuid():
user_agent = f"Python/{platform.python_version()}"
os_info = platform.platform()
cpu_info = str(psutil.cpu_count())
mac_address = uuid.UUID(int=uuid.getnode()).hex[-12:]
screen_resolution = get_screen_resolution()
color_depth = get_color_depth()
time_zone = get_time_zone_offset()
hostname = socket.gethostname()
combined_info = ":".join([
user_agent,
os_info,
cpu_info,
mac_address,
screen_resolution,
color_depth,
time_zone,
hostname
])
hashed_uuid = hashlib.md5(combined_info.encode()).hexdigest()
formatted_uuid = format_uuid(hashed_uuid)
return formatted_uuid
device_uuid = get_device_uuid()
print(device_uuid)