From 126abd64a150fd8054ebe2076869770982681865 Mon Sep 17 00:00:00 2001 From: Stupdi Go Date: Sun, 10 Aug 2025 13:14:34 -0500 Subject: [PATCH] Preperations... --- .gitignore | 1 + main.py | 23 ++++++++++++ producers/sp500.py | 0 utils.py | 89 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 113 insertions(+) create mode 100644 .gitignore create mode 100644 main.py create mode 100644 producers/sp500.py create mode 100644 utils.py diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..78bcc9e --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +creds.toml diff --git a/main.py b/main.py new file mode 100644 index 0000000..e5d5a0a --- /dev/null +++ b/main.py @@ -0,0 +1,23 @@ +import sys +import json +import tomllib +import paho.mqtt.client as paho +import time +from utils import load_config, authenticate + +config = load_config() +#client = paho.Client(paho.CallbackAPIVersion.VERSION2) +#client.username_pw_set(username=config.mqtt.username,password=config.mqtt.password) + +#if client.connect("proliant.lan", 1883, 60) != 0: + #print("Couldn't connect to the mqtt broker") + #sys.exit(1) + + +#client.publish("SP500", json.dumps({"value":6349}), 0) +#time.sleep(0.5) + + +#client.disconnect() + +authenticate() diff --git a/producers/sp500.py b/producers/sp500.py new file mode 100644 index 0000000..e69de29 diff --git a/utils.py b/utils.py new file mode 100644 index 0000000..2695913 --- /dev/null +++ b/utils.py @@ -0,0 +1,89 @@ +from typing import NamedTuple +import tomllib +def dict_to_namedtuple(name, dictionary): + fields = {} + for key, value in dictionary.items(): + if isinstance(value, dict): + fields[key] = dict_to_namedtuple(key.capitalize(), value) + else: + fields[key] = value + return NamedTuple(name, [(k, type(v)) for k, v in fields.items()])(**fields) + + +def load_config(): + with open("creds.toml", "rb") as f: + config_dict = tomllib.load(f) + config = dict_to_namedtuple("Config", config_dict) + return config +import webbrowser +import requests +import threading +from flask import Flask, request + +def authenticate(client_id, client_secret, scopes=None, port=5000): + """ + Opens the browser for OAuth authorization and returns token data. + + Args: + client_id (str): Your Tastytrade client ID + client_secret (str): Your Tastytrade client secret + scopes (list[str]): Optional list of scopes (default: None) + port (int): Port for local callback server (default: 5000) + + Returns: + dict: Token response JSON (access_token, refresh_token, etc.) + """ + redirect_uri = f"http://127.0.0.1:{port}/callback" + auth_url = "https://api.tastytrade.com/oauth/authorize" + token_url = "https://api.tastytrade.com/oauth/token" + + app = Flask(__name__) + auth_code_container = {} + + @app.route("/callback") + def callback(): + code = request.args.get("code") + auth_code_container["code"] = code + return "Authorization successful. You may close this window." + + # Start local server in background + def run_server(): + app.run(port=port, debug=False, use_reloader=False) + threading.Thread(target=run_server, daemon=True).start() + + # Build auth URL + params = { + "response_type": "code", + "client_id": client_id, + "redirect_uri": redirect_uri + } + if scopes: + params["scope"] = " ".join(scopes) + + # Open browser + webbrowser.open(f"{auth_url}?{requests.compat.urlencode(params)}") + + # Wait for code + print("Waiting for authorization...") + while "code" not in auth_code_container: + pass + + # Exchange code for token + token_resp = requests.post(token_url, data={ + "grant_type": "authorization_code", + "code": auth_code_container["code"], + "redirect_uri": redirect_uri, + "client_id": client_id, + "client_secret": client_secret + }) + token_resp.raise_for_status() + + return token_resp.json() + +# Example usage +if __name__ == "__main__": + client_id = "YOUR_CLIENT_ID" + client_secret = "YOUR_CLIENT_SECRET" + token_data = authenticate(client_id, client_secret, scopes=["accounts", "orders"]) + print(token_data) +