Spaces:
Running
Running
Upload 19 files
Browse files- Dockerfile +40 -0
- README.md +21 -5
- app.py +319 -0
- game/.gitattributes +35 -0
- game/README.md +8 -0
- game/components/footer.js +100 -0
- game/components/navbar.js +82 -0
- game/game-api-integration.js +280 -0
- game/trade.html +418 -0
- game_api.py +280 -0
- multiple_docs/availability.txt +36 -0
- multiple_docs/background.txt +34 -0
- multiple_docs/chatbot_exp.txt +38 -0
- multiple_docs/contact.txt +36 -0
- multiple_docs/cv.txt +66 -0
- multiple_docs/education.txt +65 -0
- multiple_docs/experience.txt +38 -0
- multiple_docs/various.txt +43 -0
- pyproject.toml +33 -0
Dockerfile
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Dockerfile for HuggingFace Spaces deployment
|
| 2 |
+
FROM python:3.11-slim
|
| 3 |
+
|
| 4 |
+
WORKDIR /app
|
| 5 |
+
|
| 6 |
+
# Install system dependencies for sqlite
|
| 7 |
+
RUN apt-get update && apt-get install -y \
|
| 8 |
+
build-essential \
|
| 9 |
+
&& rm -rf /var/lib/apt/lists/*
|
| 10 |
+
|
| 11 |
+
# Install pysqlite3 for HuggingFace Spaces
|
| 12 |
+
RUN pip install pysqlite3-binary
|
| 13 |
+
|
| 14 |
+
# Copy dependency files
|
| 15 |
+
COPY pyproject.toml uv.lock* ./
|
| 16 |
+
|
| 17 |
+
# Install uv (Python package manager)
|
| 18 |
+
RUN pip install uv
|
| 19 |
+
|
| 20 |
+
# Install dependencies
|
| 21 |
+
RUN uv pip install --system -r pyproject.toml || \
|
| 22 |
+
pip install -r requirements.txt || \
|
| 23 |
+
uv pip install --system gradio langchain langchain-community langchain-core chromadb sentence-transformers requests python-dotenv langchain-huggingface langchain-openai openai tiktoken fastapi uvicorn python-multipart pysqlite3-binary
|
| 24 |
+
|
| 25 |
+
# Copy application files
|
| 26 |
+
COPY . .
|
| 27 |
+
|
| 28 |
+
# Create db directory if it doesn't exist
|
| 29 |
+
RUN mkdir -p db multiple_docs game
|
| 30 |
+
|
| 31 |
+
# Set environment variables (can be overridden in HF Spaces)
|
| 32 |
+
ENV PORT=7860
|
| 33 |
+
ENV HF_TOKEN=${HF_TOKEN}
|
| 34 |
+
ENV HF_MODEL_NAME=${HF_MODEL_NAME:-meta-llama/Llama-3.1-8B-Instruct:novita}
|
| 35 |
+
|
| 36 |
+
# Expose port
|
| 37 |
+
EXPOSE 7860
|
| 38 |
+
|
| 39 |
+
# Run FastAPI with uvicorn (HuggingFace Spaces uses port 7860)
|
| 40 |
+
CMD python -c "import sys, os; sys.path.insert(0, '.'); from game_api import app; import uvicorn; port = int(os.getenv('PORT', 7860)); uvicorn.run(app, host='0.0.0.0', port=port)"
|
README.md
CHANGED
|
@@ -1,10 +1,26 @@
|
|
| 1 |
---
|
| 2 |
-
title: Trading Game
|
| 3 |
-
emoji:
|
| 4 |
-
colorFrom:
|
| 5 |
-
colorTo:
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
|
|
|
| 8 |
---
|
| 9 |
|
| 10 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
---
|
| 2 |
+
title: Trading Game AI Assistant
|
| 3 |
+
emoji: 📈
|
| 4 |
+
colorFrom: blue
|
| 5 |
+
colorTo: green
|
| 6 |
sdk: docker
|
| 7 |
pinned: false
|
| 8 |
+
license: cc
|
| 9 |
---
|
| 10 |
|
| 11 |
+
# Trading Game AI Assistant
|
| 12 |
+
|
| 13 |
+
A psychological experiment platform combining a trading game with tunable AI advice. Players can adjust AI risk, temperature, and confidence levels while making trading decisions.
|
| 14 |
+
|
| 15 |
+
## Features
|
| 16 |
+
|
| 17 |
+
- **Trading Interface**: Buy/sell stocks in a fantasy financial universe
|
| 18 |
+
- **AI Advisor**: Get AI-powered trading advice based on market scenarios
|
| 19 |
+
- **Tunable Parameters**: Adjust AI risk tolerance, creativity (temperature), and confidence
|
| 20 |
+
- **Experiment Tracking**: Log decisions and trust scores for research
|
| 21 |
+
|
| 22 |
+
## Environment Variables
|
| 23 |
+
|
| 24 |
+
Set these in HuggingFace Spaces:
|
| 25 |
+
- `HF_TOKEN`: Your HuggingFace API token
|
| 26 |
+
- `HF_MODEL_NAME`: Model to use (default: `meta-llama/Llama-3.1-8B-Instruct:novita`)
|
app.py
ADDED
|
@@ -0,0 +1,319 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# import os
|
| 2 |
+
# import sys
|
| 3 |
+
# import requests
|
| 4 |
+
# from langchain.chains import ConversationalRetrievalChain
|
| 5 |
+
# from langchain.document_loaders import PyPDFLoader, Docx2txtLoader, TextLoader
|
| 6 |
+
# from langchain_text_splitters import CharacterTextSplitter
|
| 7 |
+
# from langchain.vectorstores import Chroma
|
| 8 |
+
# from langchain.embeddings import HuggingFaceEmbeddings
|
| 9 |
+
# from langchain.llms.base import LLM
|
| 10 |
+
# import gradio as gr
|
| 11 |
+
|
| 12 |
+
# # workaround for sqlite in HF spaces
|
| 13 |
+
# __import__('pysqlite3')
|
| 14 |
+
# sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')
|
| 15 |
+
|
| 16 |
+
# # 📄 Load documents
|
| 17 |
+
# docs = []
|
| 18 |
+
# for f in os.listdir("multiple_docs"):
|
| 19 |
+
# if f.endswith(".pdf"):
|
| 20 |
+
# loader = PyPDFLoader(os.path.join("multiple_docs", f))
|
| 21 |
+
# docs.extend(loader.load())
|
| 22 |
+
# elif f.endswith(".docx") or f.endswith(".doc"):
|
| 23 |
+
# loader = Docx2txtLoader(os.path.join("multiple_docs", f))
|
| 24 |
+
# docs.extend(loader.load())
|
| 25 |
+
# elif f.endswith(".txt"):
|
| 26 |
+
# loader = TextLoader(os.path.join("multiple_docs", f))
|
| 27 |
+
# docs.extend(loader.load())
|
| 28 |
+
|
| 29 |
+
# # 🔗 Split into chunks
|
| 30 |
+
# splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=10)
|
| 31 |
+
# docs = splitter.split_documents(docs)
|
| 32 |
+
|
| 33 |
+
# texts = [doc.page_content for doc in docs]
|
| 34 |
+
# metadatas = [{"id": i} for i in range(len(texts))]
|
| 35 |
+
|
| 36 |
+
# # 🧠 Embeddings
|
| 37 |
+
# embedding_function = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
| 38 |
+
|
| 39 |
+
# # 🗃️ Vectorstore
|
| 40 |
+
# vectorstore = Chroma(
|
| 41 |
+
# persist_directory="./db",
|
| 42 |
+
# embedding_function=embedding_function
|
| 43 |
+
# )
|
| 44 |
+
# vectorstore.add_texts(texts=texts, metadatas=metadatas)
|
| 45 |
+
# vectorstore.persist()
|
| 46 |
+
|
| 47 |
+
# # 🔐 Get DeepSeek API key from env
|
| 48 |
+
# DEEPSEEK_API_KEY = os.getenv("DEEPSEEK_API_KEY")
|
| 49 |
+
# if DEEPSEEK_API_KEY is None:
|
| 50 |
+
# raise ValueError("DEEPSEEK_API_KEY environment variable is not set.")
|
| 51 |
+
|
| 52 |
+
# # 🌟 DeepSeek API endpoint
|
| 53 |
+
# DEEPSEEK_API_URL = "https://api.deepseek.com/v1/chat/completions"
|
| 54 |
+
|
| 55 |
+
# # 🔷 Wrap DeepSeek API into LangChain LLM
|
| 56 |
+
# class DeepSeekLLM(LLM):
|
| 57 |
+
# """LLM that queries DeepSeek's API."""
|
| 58 |
+
# api_key: str = DEEPSEEK_API_KEY
|
| 59 |
+
|
| 60 |
+
# def _call(self, prompt, stop=None, run_manager=None, **kwargs):
|
| 61 |
+
# headers = {
|
| 62 |
+
# "Authorization": f"Bearer {self.api_key}",
|
| 63 |
+
# "Content-Type": "application/json"
|
| 64 |
+
# }
|
| 65 |
+
# payload = {
|
| 66 |
+
# "model": "deepseek-chat", # adjust if you have a specific model name
|
| 67 |
+
# "messages": [
|
| 68 |
+
# {"role": "system", "content": "You are a helpful assistant."},
|
| 69 |
+
# {"role": "user", "content": prompt}
|
| 70 |
+
# ],
|
| 71 |
+
# "temperature": 0.7,
|
| 72 |
+
# "max_tokens": 512
|
| 73 |
+
# }
|
| 74 |
+
# response = requests.post(DEEPSEEK_API_URL, headers=headers, json=payload)
|
| 75 |
+
# response.raise_for_status()
|
| 76 |
+
# data = response.json()
|
| 77 |
+
# return data["choices"][0]["message"]["content"].strip()
|
| 78 |
+
|
| 79 |
+
# @property
|
| 80 |
+
# def _llm_type(self) -> str:
|
| 81 |
+
# return "deepseek_api"
|
| 82 |
+
|
| 83 |
+
# llm = DeepSeekLLM()
|
| 84 |
+
|
| 85 |
+
# # 🔗 Conversational chain
|
| 86 |
+
# chain = ConversationalRetrievalChain.from_llm(
|
| 87 |
+
# llm,
|
| 88 |
+
# retriever=vectorstore.as_retriever(search_kwargs={'k': 6}),
|
| 89 |
+
# return_source_documents=True,
|
| 90 |
+
# verbose=False
|
| 91 |
+
# )
|
| 92 |
+
|
| 93 |
+
# # 💬 Gradio UI
|
| 94 |
+
# chat_history = []
|
| 95 |
+
|
| 96 |
+
# with gr.Blocks() as demo:
|
| 97 |
+
# chatbot = gr.Chatbot(
|
| 98 |
+
# [("", "Hello, I'm Thierry Decae's chatbot, you can ask me any recruitment related questions such as my experience, where I'm eligible to work, skills etc you can chat with me directly in multiple languages")],
|
| 99 |
+
# avatar_images=["./multiple_docs/Guest.jpg", "./multiple_docs/Thierry Picture.jpg"]
|
| 100 |
+
# )
|
| 101 |
+
# msg = gr.Textbox(placeholder="Type your question here...")
|
| 102 |
+
# clear = gr.Button("Clear")
|
| 103 |
+
|
| 104 |
+
# def user(query, chat_history):
|
| 105 |
+
# chat_history_tuples = [(m[0], m[1]) for m in chat_history]
|
| 106 |
+
# result = chain({"question": query, "chat_history": chat_history_tuples})
|
| 107 |
+
# chat_history.append((query, result["answer"]))
|
| 108 |
+
# return gr.update(value=""), chat_history
|
| 109 |
+
|
| 110 |
+
# msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False)
|
| 111 |
+
# clear.click(lambda: None, None, chatbot, queue=False)
|
| 112 |
+
|
| 113 |
+
# demo.launch(debug=True) # remove share=True if running in HF Spaces
|
| 114 |
+
|
| 115 |
+
|
| 116 |
+
import os
|
| 117 |
+
import sys
|
| 118 |
+
from langchain_classic.chains import ConversationalRetrievalChain, LLMChain
|
| 119 |
+
from langchain_community.document_loaders import PyPDFLoader, Docx2txtLoader, TextLoader
|
| 120 |
+
from langchain_text_splitters import CharacterTextSplitter
|
| 121 |
+
from langchain_community.vectorstores import Chroma
|
| 122 |
+
from langchain_huggingface import HuggingFaceEmbeddings
|
| 123 |
+
from langchain_openai import ChatOpenAI
|
| 124 |
+
from langchain_core.prompts import PromptTemplate
|
| 125 |
+
from langchain_classic.chains.question_answering import load_qa_chain
|
| 126 |
+
import gradio as gr
|
| 127 |
+
|
| 128 |
+
# workaround for sqlite in HF spaces (only needed on HuggingFace Spaces)
|
| 129 |
+
try:
|
| 130 |
+
__import__('pysqlite3')
|
| 131 |
+
sys.modules['sqlite3'] = sys.modules.pop('pysqlite3')
|
| 132 |
+
except ImportError:
|
| 133 |
+
# pysqlite3 not available, use system sqlite3 (fine for local macOS/Linux)
|
| 134 |
+
pass
|
| 135 |
+
|
| 136 |
+
# 📄 Load documents
|
| 137 |
+
docs = []
|
| 138 |
+
for f in os.listdir("multiple_docs"):
|
| 139 |
+
if f.endswith(".pdf"):
|
| 140 |
+
loader = PyPDFLoader(os.path.join("multiple_docs", f))
|
| 141 |
+
docs.extend(loader.load())
|
| 142 |
+
elif f.endswith(".docx") or f.endswith(".doc"):
|
| 143 |
+
loader = Docx2txtLoader(os.path.join("multiple_docs", f))
|
| 144 |
+
docs.extend(loader.load())
|
| 145 |
+
elif f.endswith(".txt"):
|
| 146 |
+
loader = TextLoader(os.path.join("multiple_docs", f))
|
| 147 |
+
docs.extend(loader.load())
|
| 148 |
+
|
| 149 |
+
# 🔗 Split into chunks
|
| 150 |
+
splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=10)
|
| 151 |
+
docs = splitter.split_documents(docs)
|
| 152 |
+
|
| 153 |
+
# 🧠 Embeddings
|
| 154 |
+
embedding_function = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
|
| 155 |
+
|
| 156 |
+
# 🗃️ Vectorstore - use from_documents which is more modern
|
| 157 |
+
vectorstore = Chroma.from_documents(
|
| 158 |
+
documents=docs,
|
| 159 |
+
embedding=embedding_function,
|
| 160 |
+
persist_directory="./db"
|
| 161 |
+
)
|
| 162 |
+
|
| 163 |
+
# 🤖 Use HuggingFace Inference API (hosted in Spaces)
|
| 164 |
+
# HF token (must be set via HF_TOKEN environment variable)
|
| 165 |
+
HF_TOKEN = os.getenv("HF_TOKEN")
|
| 166 |
+
if not HF_TOKEN:
|
| 167 |
+
raise ValueError("HF_TOKEN environment variable is not set. Please set it before running the application.")
|
| 168 |
+
|
| 169 |
+
# You can change MODEL_NAME to use different models from HuggingFace:
|
| 170 |
+
# Popular options:
|
| 171 |
+
# - "microsoft/DialoGPT-large" (conversational model)
|
| 172 |
+
# - "HuggingFaceH4/zephyr-7b-beta" (high quality chat model)
|
| 173 |
+
# - "mistralai/Mistral-7B-Instruct-v0.2" (excellent quality)
|
| 174 |
+
# - "meta-llama/Llama-2-7b-chat-hf" (if you have access)
|
| 175 |
+
# - "google/flan-t5-large" (good for Q&A)
|
| 176 |
+
MODEL_NAME = os.getenv("HF_MODEL_NAME", "meta-llama/Llama-3.1-8B-Instruct:novita")
|
| 177 |
+
|
| 178 |
+
print(f"Using HuggingFace Router API with model: {MODEL_NAME}")
|
| 179 |
+
|
| 180 |
+
# Initialize ChatOpenAI with HuggingFace Router (OpenAI-compatible API)
|
| 181 |
+
# This allows using provider suffixes like :novita
|
| 182 |
+
# Uses OpenAI-compatible interface via HF router
|
| 183 |
+
llm = ChatOpenAI(
|
| 184 |
+
model=MODEL_NAME,
|
| 185 |
+
base_url="https://router.huggingface.co/v1",
|
| 186 |
+
api_key=HF_TOKEN,
|
| 187 |
+
temperature=0.7,
|
| 188 |
+
max_tokens=512,
|
| 189 |
+
)
|
| 190 |
+
|
| 191 |
+
# ✨ Custom prompt template
|
| 192 |
+
template = """
|
| 193 |
+
You are an AI trading advisor for the Quantum Financial Network trading universe.
|
| 194 |
+
|
| 195 |
+
IMPORTANT: You are providing DIRECT TRADING RECOMMENDATIONS. Give clear, actionable advice based on:
|
| 196 |
+
- Current market scenarios described in the question
|
| 197 |
+
- Risk tolerance level specified (0=conservative, 10=aggressive)
|
| 198 |
+
- Company information and market context provided
|
| 199 |
+
- Specific stock symbols and prices mentioned
|
| 200 |
+
|
| 201 |
+
When asked about a specific trade (e.g., "Should I buy X shares of Y?"), you MUST:
|
| 202 |
+
1. Give a CLEAR RECOMMENDATION: "Yes, buy..." or "No, don't buy..." or "Consider buying/selling..."
|
| 203 |
+
2. Explain WHY based on the current scenario and company information
|
| 204 |
+
3. Reference specific companies, market events, or data from the context
|
| 205 |
+
4. Adjust your certainty based on the risk tolerance level mentioned
|
| 206 |
+
5. Be DIRECT and ACTIONABLE - no vague "I'd need more information" responses
|
| 207 |
+
|
| 208 |
+
Use the following context to answer the user's question. Always give a direct recommendation.
|
| 209 |
+
|
| 210 |
+
Context:
|
| 211 |
+
{context}
|
| 212 |
+
|
| 213 |
+
Question: {question}
|
| 214 |
+
|
| 215 |
+
Answer:
|
| 216 |
+
"""
|
| 217 |
+
|
| 218 |
+
prompt = PromptTemplate(
|
| 219 |
+
input_variables=["context", "question"],
|
| 220 |
+
template=template,
|
| 221 |
+
)
|
| 222 |
+
|
| 223 |
+
# 🔗 QA chain with custom prompt
|
| 224 |
+
qa_chain = load_qa_chain(llm, chain_type="stuff", prompt=prompt)
|
| 225 |
+
|
| 226 |
+
# 🔷 Question rephraser chain for follow-up questions → standalone
|
| 227 |
+
CONDENSE_QUESTION_PROMPT = PromptTemplate.from_template(
|
| 228 |
+
"""
|
| 229 |
+
Given the following conversation and a follow-up question, rephrase the follow-up question to be a standalone question.
|
| 230 |
+
|
| 231 |
+
Chat History:
|
| 232 |
+
{chat_history}
|
| 233 |
+
Follow Up Input: {question}
|
| 234 |
+
Standalone question:
|
| 235 |
+
"""
|
| 236 |
+
)
|
| 237 |
+
|
| 238 |
+
question_generator = LLMChain(
|
| 239 |
+
llm=llm,
|
| 240 |
+
prompt=CONDENSE_QUESTION_PROMPT
|
| 241 |
+
)
|
| 242 |
+
|
| 243 |
+
# 🔷 Finally: build the ConversationalRetrievalChain manually
|
| 244 |
+
chain = ConversationalRetrievalChain(
|
| 245 |
+
retriever=vectorstore.as_retriever(search_kwargs={'k': 6}),
|
| 246 |
+
question_generator=question_generator,
|
| 247 |
+
combine_docs_chain=qa_chain,
|
| 248 |
+
return_source_documents=True,
|
| 249 |
+
verbose=False
|
| 250 |
+
)
|
| 251 |
+
|
| 252 |
+
# 💬 Gradio UI
|
| 253 |
+
chat_history = []
|
| 254 |
+
|
| 255 |
+
with gr.Blocks() as demo:
|
| 256 |
+
chatbot = gr.Chatbot(
|
| 257 |
+
value=[{"role": "assistant", "content": "Welcome to the Quantum Financial Network! I'm your trading universe assistant. You can ask me about companies (VCG, CSI, STDY, AUBIO, NLN), market events, trading strategies, currency systems, trading hours, market history, and anything about our financial markets. What would you like to know?"}]
|
| 258 |
+
)
|
| 259 |
+
msg = gr.Textbox(placeholder="Type your question here...")
|
| 260 |
+
clear = gr.Button("Clear")
|
| 261 |
+
|
| 262 |
+
def user(query, chat_history):
|
| 263 |
+
# Convert Gradio 4.x format (dicts) to tuple format for LangChain chain
|
| 264 |
+
# LangChain expects list of tuples: [(human_message, ai_message), ...]
|
| 265 |
+
chat_history_tuples = []
|
| 266 |
+
current_human = None
|
| 267 |
+
current_ai = None
|
| 268 |
+
|
| 269 |
+
for msg in chat_history:
|
| 270 |
+
if isinstance(msg, dict):
|
| 271 |
+
role = msg.get("role", "")
|
| 272 |
+
content = msg.get("content", "")
|
| 273 |
+
|
| 274 |
+
if role == "user":
|
| 275 |
+
# If we have a previous human message without AI response, add it with empty AI
|
| 276 |
+
if current_human is not None:
|
| 277 |
+
human_str = str(current_human) if current_human else ""
|
| 278 |
+
ai_str = str(current_ai) if current_ai else ""
|
| 279 |
+
chat_history_tuples.append((human_str, ai_str))
|
| 280 |
+
current_human = str(content) if content else ""
|
| 281 |
+
current_ai = None
|
| 282 |
+
elif role == "assistant":
|
| 283 |
+
current_ai = str(content) if content else ""
|
| 284 |
+
# Pair the human and AI messages
|
| 285 |
+
if current_human is not None:
|
| 286 |
+
human_str = str(current_human) if current_human else ""
|
| 287 |
+
ai_str = str(current_ai) if current_ai else ""
|
| 288 |
+
chat_history_tuples.append((human_str, ai_str))
|
| 289 |
+
current_human = None
|
| 290 |
+
current_ai = None
|
| 291 |
+
else:
|
| 292 |
+
# Fallback for old tuple format
|
| 293 |
+
if isinstance(msg, (list, tuple)) and len(msg) >= 2:
|
| 294 |
+
chat_history_tuples.append((str(msg[0]), str(msg[1])))
|
| 295 |
+
|
| 296 |
+
# Handle case where last message was human without AI response
|
| 297 |
+
if current_human is not None:
|
| 298 |
+
human_str = str(current_human) if current_human else ""
|
| 299 |
+
ai_str = str(current_ai) if current_ai else ""
|
| 300 |
+
chat_history_tuples.append((human_str, ai_str))
|
| 301 |
+
|
| 302 |
+
# Get response from chain
|
| 303 |
+
result = chain({"question": query, "chat_history": chat_history_tuples})
|
| 304 |
+
|
| 305 |
+
# Append both user query and assistant response in Gradio 4.x format
|
| 306 |
+
chat_history.append({"role": "user", "content": query})
|
| 307 |
+
chat_history.append({"role": "assistant", "content": result["answer"]})
|
| 308 |
+
|
| 309 |
+
return gr.update(value=""), chat_history
|
| 310 |
+
|
| 311 |
+
msg.submit(user, [msg, chatbot], [msg, chatbot], queue=False)
|
| 312 |
+
clear.click(lambda: None, None, chatbot, queue=False)
|
| 313 |
+
|
| 314 |
+
# Only launch Gradio demo if running this file directly (not when imported)
|
| 315 |
+
if __name__ == "__main__":
|
| 316 |
+
demo.launch(debug=True) # remove share=True if running in HF Spaces
|
| 317 |
+
|
| 318 |
+
|
| 319 |
+
|
game/.gitattributes
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
*.7z filter=lfs diff=lfs merge=lfs -text
|
| 2 |
+
*.arrow filter=lfs diff=lfs merge=lfs -text
|
| 3 |
+
*.bin filter=lfs diff=lfs merge=lfs -text
|
| 4 |
+
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
| 5 |
+
*.ckpt filter=lfs diff=lfs merge=lfs -text
|
| 6 |
+
*.ftz filter=lfs diff=lfs merge=lfs -text
|
| 7 |
+
*.gz filter=lfs diff=lfs merge=lfs -text
|
| 8 |
+
*.h5 filter=lfs diff=lfs merge=lfs -text
|
| 9 |
+
*.joblib filter=lfs diff=lfs merge=lfs -text
|
| 10 |
+
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
| 11 |
+
*.mlmodel filter=lfs diff=lfs merge=lfs -text
|
| 12 |
+
*.model filter=lfs diff=lfs merge=lfs -text
|
| 13 |
+
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
| 14 |
+
*.npy filter=lfs diff=lfs merge=lfs -text
|
| 15 |
+
*.npz filter=lfs diff=lfs merge=lfs -text
|
| 16 |
+
*.onnx filter=lfs diff=lfs merge=lfs -text
|
| 17 |
+
*.ot filter=lfs diff=lfs merge=lfs -text
|
| 18 |
+
*.parquet filter=lfs diff=lfs merge=lfs -text
|
| 19 |
+
*.pb filter=lfs diff=lfs merge=lfs -text
|
| 20 |
+
*.pickle filter=lfs diff=lfs merge=lfs -text
|
| 21 |
+
*.pkl filter=lfs diff=lfs merge=lfs -text
|
| 22 |
+
*.pt filter=lfs diff=lfs merge=lfs -text
|
| 23 |
+
*.pth filter=lfs diff=lfs merge=lfs -text
|
| 24 |
+
*.rar filter=lfs diff=lfs merge=lfs -text
|
| 25 |
+
*.safetensors filter=lfs diff=lfs merge=lfs -text
|
| 26 |
+
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
| 27 |
+
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
| 28 |
+
*.tar filter=lfs diff=lfs merge=lfs -text
|
| 29 |
+
*.tflite filter=lfs diff=lfs merge=lfs -text
|
| 30 |
+
*.tgz filter=lfs diff=lfs merge=lfs -text
|
| 31 |
+
*.wasm filter=lfs diff=lfs merge=lfs -text
|
| 32 |
+
*.xz filter=lfs diff=lfs merge=lfs -text
|
| 33 |
+
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
+
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
+
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
game/README.md
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Trading Game Frontend
|
| 2 |
+
|
| 3 |
+
Frontend files for the Trading Game AI Assistant experiment platform.
|
| 4 |
+
|
| 5 |
+
## Files
|
| 6 |
+
|
| 7 |
+
- `trade.html` - Main trading game interface
|
| 8 |
+
- `game-api-integration.js` - API integration layer for AI chat
|
game/components/footer.js
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomFooter extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
footer {
|
| 7 |
+
background: rgba(17, 24, 39, 0.8);
|
| 8 |
+
color: white;
|
| 9 |
+
padding: 2rem;
|
| 10 |
+
text-align: center;
|
| 11 |
+
margin-top: auto;
|
| 12 |
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
| 13 |
+
}
|
| 14 |
+
.footer-content {
|
| 15 |
+
max-width: 1200px;
|
| 16 |
+
margin: 0 auto;
|
| 17 |
+
display: grid;
|
| 18 |
+
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
|
| 19 |
+
gap: 2rem;
|
| 20 |
+
text-align: left;
|
| 21 |
+
}
|
| 22 |
+
.footer-links h3 {
|
| 23 |
+
font-weight: 600;
|
| 24 |
+
margin-bottom: 1rem;
|
| 25 |
+
color: #10B981;
|
| 26 |
+
}
|
| 27 |
+
.footer-links ul {
|
| 28 |
+
list-style: none;
|
| 29 |
+
padding: 0;
|
| 30 |
+
margin: 0;
|
| 31 |
+
}
|
| 32 |
+
.footer-links li {
|
| 33 |
+
margin-bottom: 0.5rem;
|
| 34 |
+
}
|
| 35 |
+
.footer-links a {
|
| 36 |
+
color: rgba(255, 255, 255, 0.7);
|
| 37 |
+
text-decoration: none;
|
| 38 |
+
transition: color 0.2s;
|
| 39 |
+
}
|
| 40 |
+
.footer-links a:hover {
|
| 41 |
+
color: white;
|
| 42 |
+
}
|
| 43 |
+
.social-links {
|
| 44 |
+
display: flex;
|
| 45 |
+
gap: 1rem;
|
| 46 |
+
justify-content: center;
|
| 47 |
+
margin-top: 2rem;
|
| 48 |
+
}
|
| 49 |
+
.social-links a {
|
| 50 |
+
color: white;
|
| 51 |
+
background: rgba(255, 255, 255, 0.1);
|
| 52 |
+
width: 40px;
|
| 53 |
+
height: 40px;
|
| 54 |
+
border-radius: 50%;
|
| 55 |
+
display: flex;
|
| 56 |
+
align-items: center;
|
| 57 |
+
justify-content: center;
|
| 58 |
+
transition: all 0.2s;
|
| 59 |
+
}
|
| 60 |
+
.social-links a:hover {
|
| 61 |
+
background: rgba(255, 255, 255, 0.2);
|
| 62 |
+
transform: translateY(-2px);
|
| 63 |
+
}
|
| 64 |
+
.copyright {
|
| 65 |
+
margin-top: 2rem;
|
| 66 |
+
padding-top: 2rem;
|
| 67 |
+
border-top: 1px solid rgba(255, 255, 255, 0.1);
|
| 68 |
+
color: rgba(255, 255, 255, 0.6);
|
| 69 |
+
font-size: 0.875rem;
|
| 70 |
+
}
|
| 71 |
+
</style>
|
| 72 |
+
<footer>
|
| 73 |
+
<div class="footer-content">
|
| 74 |
+
<div class="footer-links">
|
| 75 |
+
<h3>Game</h3>
|
| 76 |
+
<ul>
|
| 77 |
+
<li><a href="/play.html">Play Now</a></li>
|
| 78 |
+
<li><a href="/leaderboard.html">Leaderboard</a></li>
|
| 79 |
+
<li><a href="/tutorials.html">Tutorials</a></li>
|
| 80 |
+
</ul>
|
| 81 |
+
</div>
|
| 82 |
+
<div class="footer-links">
|
| 83 |
+
<h3>Resources</h3>
|
| 84 |
+
<ul>
|
| 85 |
+
<li><a href="/learn.html">Trading Education</a></li>
|
| 86 |
+
<li><a href="/blog.html">Blog</a></li>
|
| 87 |
+
<li><a href="/glossary.html">Trading Glossary</a></li>
|
| 88 |
+
</ul>
|
| 89 |
+
</div>
|
| 90 |
+
<div class="footer-links">
|
| 91 |
+
<h3>Company</h3>
|
| 92 |
+
<ul>
|
| 93 |
+
<li><a href="/about.html">About Us</a></li>
|
| 94 |
+
<li><a href="/careers.html">Careers</a></li>
|
| 95 |
+
<li><a href="/contact.html">Contact</a></li>
|
| 96 |
+
</ul>
|
| 97 |
+
</div>
|
| 98 |
+
</div>
|
| 99 |
+
<div class="social-links">
|
| 100 |
+
<a href="#
|
game/components/navbar.js
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
class CustomNavbar extends HTMLElement {
|
| 2 |
+
connectedCallback() {
|
| 3 |
+
this.attachShadow({ mode: 'open' });
|
| 4 |
+
this.shadowRoot.innerHTML = `
|
| 5 |
+
<style>
|
| 6 |
+
nav {
|
| 7 |
+
background: rgba(17, 24, 39, 0.8);
|
| 8 |
+
backdrop-filter: blur(10px);
|
| 9 |
+
padding: 1rem 2rem;
|
| 10 |
+
display: flex;
|
| 11 |
+
justify-content: space-between;
|
| 12 |
+
align-items: center;
|
| 13 |
+
position: sticky;
|
| 14 |
+
top: 0;
|
| 15 |
+
z-index: 50;
|
| 16 |
+
border-bottom: 1px solid rgba(255, 255, 255, 0.1);
|
| 17 |
+
}
|
| 18 |
+
.logo {
|
| 19 |
+
color: white;
|
| 20 |
+
font-weight: bold;
|
| 21 |
+
font-size: 1.5rem;
|
| 22 |
+
background: linear-gradient(90deg, #10B981 0%, #3B82F6 100%);
|
| 23 |
+
-webkit-background-clip: text;
|
| 24 |
+
background-clip: text;
|
| 25 |
+
-webkit-text-fill-color: transparent;
|
| 26 |
+
}
|
| 27 |
+
ul {
|
| 28 |
+
display: flex;
|
| 29 |
+
gap: 1.5rem;
|
| 30 |
+
list-style: none;
|
| 31 |
+
margin: 0;
|
| 32 |
+
padding: 0;
|
| 33 |
+
align-items: center;
|
| 34 |
+
}
|
| 35 |
+
a {
|
| 36 |
+
color: rgba(255, 255, 255, 0.8);
|
| 37 |
+
text-decoration: none;
|
| 38 |
+
transition: all 0.2s;
|
| 39 |
+
font-weight: 500;
|
| 40 |
+
display: flex;
|
| 41 |
+
align-items: center;
|
| 42 |
+
gap: 0.5rem;
|
| 43 |
+
}
|
| 44 |
+
a:hover {
|
| 45 |
+
color: white;
|
| 46 |
+
}
|
| 47 |
+
.active {
|
| 48 |
+
color: white;
|
| 49 |
+
font-weight: 600;
|
| 50 |
+
}
|
| 51 |
+
.play-btn {
|
| 52 |
+
background: linear-gradient(90deg, #10B981 0%, #3B82F6 100%);
|
| 53 |
+
padding: 0.5rem 1rem;
|
| 54 |
+
border-radius: 0.375rem;
|
| 55 |
+
color: white !important;
|
| 56 |
+
}
|
| 57 |
+
.play-btn:hover {
|
| 58 |
+
transform: translateY(-2px) scale(1.05);
|
| 59 |
+
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
| 60 |
+
}
|
| 61 |
+
@media (max-width: 768px) {
|
| 62 |
+
nav {
|
| 63 |
+
flex-direction: column;
|
| 64 |
+
gap: 1rem;
|
| 65 |
+
padding: 1rem;
|
| 66 |
+
}
|
| 67 |
+
}
|
| 68 |
+
</style>
|
| 69 |
+
<nav>
|
| 70 |
+
<div class="logo">Trading Game</div>
|
| 71 |
+
<ul>
|
| 72 |
+
<li><a href="/" class="active"><i data-feather="home"></i> Home</a></li>
|
| 73 |
+
<li><a href="/play.html"><i data-feather="activity"></i> Play</a></li>
|
| 74 |
+
<li><a href="/learn.html"><i data-feather="book"></i> Learn</a></li>
|
| 75 |
+
<li><a href="/leaderboard.html"><i data-feather="award"></i> Leaderboard</a></li>
|
| 76 |
+
<li><a href="/play.html" class="play-btn"><i data-feather="play"></i> Play Now</a></li>
|
| 77 |
+
</ul>
|
| 78 |
+
</nav>
|
| 79 |
+
`;
|
| 80 |
+
}
|
| 81 |
+
}
|
| 82 |
+
customElements.define('custom-navbar', CustomNavbar);
|
game/game-api-integration.js
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
/**
|
| 2 |
+
* Game API Integration for AI Chatbot
|
| 3 |
+
* Handles communication with FastAPI backend and experiment tracking
|
| 4 |
+
*/
|
| 5 |
+
|
| 6 |
+
// Auto-detect API base URL for both local and HuggingFace Spaces
|
| 7 |
+
const API_BASE_URL = (typeof window !== 'undefined' && window.location)
|
| 8 |
+
? `${window.location.origin}/api`
|
| 9 |
+
: 'http://localhost:8000/api';
|
| 10 |
+
let sessionId = generateSessionId();
|
| 11 |
+
let currentAIParams = {
|
| 12 |
+
risk_level: 5.0,
|
| 13 |
+
temperature: 0.7,
|
| 14 |
+
confidence_boost: 0.0
|
| 15 |
+
};
|
| 16 |
+
let chatHistory = [];
|
| 17 |
+
|
| 18 |
+
// Generate unique session ID
|
| 19 |
+
function generateSessionId() {
|
| 20 |
+
return 'session_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
|
| 21 |
+
}
|
| 22 |
+
|
| 23 |
+
/**
|
| 24 |
+
* Call AI chatbot with current game context
|
| 25 |
+
*/
|
| 26 |
+
async function askAI(question, context = {}) {
|
| 27 |
+
try {
|
| 28 |
+
// Use context parameters if provided, otherwise use current AI params
|
| 29 |
+
const riskLevel = context.risk_level !== undefined ? context.risk_level : currentAIParams.risk_level;
|
| 30 |
+
const temperature = context.temperature !== undefined ? context.temperature : currentAIParams.temperature;
|
| 31 |
+
const confidenceBoost = context.confidence_boost !== undefined ? context.confidence_boost : currentAIParams.confidence_boost;
|
| 32 |
+
|
| 33 |
+
const response = await fetch(`${API_BASE_URL}/ai/chat`, {
|
| 34 |
+
method: 'POST',
|
| 35 |
+
headers: {
|
| 36 |
+
'Content-Type': 'application/json',
|
| 37 |
+
},
|
| 38 |
+
body: JSON.stringify({
|
| 39 |
+
question: question,
|
| 40 |
+
chat_history: chatHistory,
|
| 41 |
+
risk_level: riskLevel,
|
| 42 |
+
temperature: temperature,
|
| 43 |
+
confidence_boost: confidenceBoost,
|
| 44 |
+
session_id: sessionId
|
| 45 |
+
})
|
| 46 |
+
});
|
| 47 |
+
|
| 48 |
+
if (!response.ok) {
|
| 49 |
+
const errorData = await response.json().catch(() => ({ detail: response.statusText }));
|
| 50 |
+
console.error('AI API Error:', errorData);
|
| 51 |
+
return { answer: `Error: ${errorData.detail || 'Failed to get AI advice'}` };
|
| 52 |
+
}
|
| 53 |
+
|
| 54 |
+
const data = await response.json();
|
| 55 |
+
|
| 56 |
+
// Update chat history
|
| 57 |
+
chatHistory.push([question, data.answer]);
|
| 58 |
+
|
| 59 |
+
return data;
|
| 60 |
+
} catch (error) {
|
| 61 |
+
console.error('AI API Error:', error);
|
| 62 |
+
return { answer: "I'm having trouble connecting. Please try again." };
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
/**
|
| 67 |
+
* Log trading decision for trust analysis
|
| 68 |
+
*/
|
| 69 |
+
async function logDecision(symbol, action, quantity, price, aiAdviceFollowed, trustScore = null) {
|
| 70 |
+
try {
|
| 71 |
+
await fetch(`${API_BASE_URL}/experiment/decision`, {
|
| 72 |
+
method: 'POST',
|
| 73 |
+
headers: {
|
| 74 |
+
'Content-Type': 'application/json',
|
| 75 |
+
},
|
| 76 |
+
body: JSON.stringify({
|
| 77 |
+
session_id: sessionId,
|
| 78 |
+
symbol: symbol,
|
| 79 |
+
action: action,
|
| 80 |
+
quantity: quantity,
|
| 81 |
+
price: price,
|
| 82 |
+
ai_advice_followed: aiAdviceFollowed,
|
| 83 |
+
trust_score: trustScore
|
| 84 |
+
})
|
| 85 |
+
});
|
| 86 |
+
} catch (error) {
|
| 87 |
+
console.error('Decision logging error:', error);
|
| 88 |
+
}
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
/**
|
| 92 |
+
* Trigger situational scenario
|
| 93 |
+
*/
|
| 94 |
+
async function triggerScenario(scenarioType, context = {}) {
|
| 95 |
+
try {
|
| 96 |
+
const response = await fetch(`${API_BASE_URL}/experiment/scenario`, {
|
| 97 |
+
method: 'POST',
|
| 98 |
+
headers: {
|
| 99 |
+
'Content-Type': 'application/json',
|
| 100 |
+
},
|
| 101 |
+
body: JSON.stringify({
|
| 102 |
+
session_id: sessionId,
|
| 103 |
+
scenario_type: scenarioType,
|
| 104 |
+
context: context,
|
| 105 |
+
ai_required: true
|
| 106 |
+
})
|
| 107 |
+
});
|
| 108 |
+
|
| 109 |
+
const data = await response.json();
|
| 110 |
+
|
| 111 |
+
// Show scenario prompt to player and trigger AI assistance
|
| 112 |
+
if (data.requires_ai) {
|
| 113 |
+
showScenarioPrompt(data);
|
| 114 |
+
}
|
| 115 |
+
|
| 116 |
+
return data;
|
| 117 |
+
} catch (error) {
|
| 118 |
+
console.error('Scenario trigger error:', error);
|
| 119 |
+
}
|
| 120 |
+
}
|
| 121 |
+
|
| 122 |
+
/**
|
| 123 |
+
* Update AI parameters (for experiment control)
|
| 124 |
+
*/
|
| 125 |
+
function updateAIParams(riskLevel, temperature, confidenceBoost) {
|
| 126 |
+
currentAIParams.risk_level = riskLevel;
|
| 127 |
+
currentAIParams.temperature = temperature;
|
| 128 |
+
currentAIParams.confidence_boost = confidenceBoost;
|
| 129 |
+
|
| 130 |
+
// Update UI if controls exist
|
| 131 |
+
updateAIParamsUI();
|
| 132 |
+
}
|
| 133 |
+
|
| 134 |
+
/**
|
| 135 |
+
* Check for scenario triggers based on game state
|
| 136 |
+
*/
|
| 137 |
+
function checkScenarioTriggers(gameState) {
|
| 138 |
+
// Volatility check
|
| 139 |
+
if (gameState.volatility > 0.15) {
|
| 140 |
+
triggerScenario('volatility', {
|
| 141 |
+
volatility: gameState.volatility,
|
| 142 |
+
affected_stocks: gameState.positions
|
| 143 |
+
});
|
| 144 |
+
}
|
| 145 |
+
|
| 146 |
+
// Large position check
|
| 147 |
+
const totalValue = gameState.positions.reduce((sum, pos) => sum + (pos.price * pos.quantity), 0);
|
| 148 |
+
if (totalValue > 10000) {
|
| 149 |
+
triggerScenario('large_position', {
|
| 150 |
+
total_value: totalValue,
|
| 151 |
+
portfolio_size: gameState.balance + totalValue
|
| 152 |
+
});
|
| 153 |
+
}
|
| 154 |
+
|
| 155 |
+
// Loss recovery check
|
| 156 |
+
const dailyPnL = gameState.pnl;
|
| 157 |
+
const pnlPercent = (dailyPnL / gameState.balance) * 100;
|
| 158 |
+
if (pnlPercent < -5) {
|
| 159 |
+
triggerScenario('loss_recovery', {
|
| 160 |
+
current_loss: dailyPnL,
|
| 161 |
+
loss_percent: pnlPercent
|
| 162 |
+
});
|
| 163 |
+
}
|
| 164 |
+
}
|
| 165 |
+
|
| 166 |
+
/**
|
| 167 |
+
* Show scenario prompt and AI assistance UI
|
| 168 |
+
*/
|
| 169 |
+
function showScenarioPrompt(scenarioData) {
|
| 170 |
+
// Create modal or inline prompt
|
| 171 |
+
const scenarioModal = document.createElement('div');
|
| 172 |
+
scenarioModal.className = 'scenario-modal fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50';
|
| 173 |
+
scenarioModal.innerHTML = `
|
| 174 |
+
<div class="bg-gray-800 p-6 rounded-xl max-w-2xl">
|
| 175 |
+
<h3 class="text-2xl font-bold mb-4">Trading Scenario</h3>
|
| 176 |
+
<p class="text-gray-300 mb-4">${scenarioData.prompt}</p>
|
| 177 |
+
<div id="ai-advice-container" class="mb-4 p-4 bg-gray-700 rounded-lg">
|
| 178 |
+
<p class="text-green-400">🤖 AI Assistant:</p>
|
| 179 |
+
<p id="ai-advice-text" class="mt-2">Analyzing situation...</p>
|
| 180 |
+
</div>
|
| 181 |
+
<div class="flex gap-4">
|
| 182 |
+
<button onclick="getAIScenarioAdvice('${scenarioData.scenario_type}')"
|
| 183 |
+
class="px-4 py-2 bg-blue-600 rounded hover:bg-blue-700">
|
| 184 |
+
Get AI Advice
|
| 185 |
+
</button>
|
| 186 |
+
<button onclick="closeScenarioModal()"
|
| 187 |
+
class="px-4 py-2 bg-gray-600 rounded hover:bg-gray-700">
|
| 188 |
+
Skip
|
| 189 |
+
</button>
|
| 190 |
+
</div>
|
| 191 |
+
</div>
|
| 192 |
+
`;
|
| 193 |
+
|
| 194 |
+
document.body.appendChild(scenarioModal);
|
| 195 |
+
|
| 196 |
+
// Auto-request AI advice
|
| 197 |
+
getAIScenarioAdvice(scenarioData.scenario_type);
|
| 198 |
+
}
|
| 199 |
+
|
| 200 |
+
/**
|
| 201 |
+
* Get AI advice for scenario
|
| 202 |
+
*/
|
| 203 |
+
async function getAIScenarioAdvice(scenarioType) {
|
| 204 |
+
const question = `I'm facing a ${scenarioType} scenario. What should I do?`;
|
| 205 |
+
const advice = await askAI(question);
|
| 206 |
+
|
| 207 |
+
const adviceText = document.getElementById('ai-advice-text');
|
| 208 |
+
if (adviceText) {
|
| 209 |
+
adviceText.textContent = advice.answer;
|
| 210 |
+
}
|
| 211 |
+
}
|
| 212 |
+
|
| 213 |
+
/**
|
| 214 |
+
* Close scenario modal
|
| 215 |
+
*/
|
| 216 |
+
function closeScenarioModal() {
|
| 217 |
+
const modal = document.querySelector('.scenario-modal');
|
| 218 |
+
if (modal) {
|
| 219 |
+
modal.remove();
|
| 220 |
+
}
|
| 221 |
+
}
|
| 222 |
+
|
| 223 |
+
/**
|
| 224 |
+
* Enhanced trading function with AI integration
|
| 225 |
+
*/
|
| 226 |
+
async function executeTradeWithAI(symbol, action, quantity, price) {
|
| 227 |
+
// Ask AI for advice before executing
|
| 228 |
+
const question = `Should I ${action} ${quantity} shares of ${symbol} at $${price.toFixed(2)}?`;
|
| 229 |
+
const aiAdvice = await askAI(question);
|
| 230 |
+
|
| 231 |
+
// Show AI advice to player
|
| 232 |
+
const confirmTrade = confirm(
|
| 233 |
+
`AI Advice: ${aiAdvice.answer}\n\nProceed with ${action.toUpperCase()} ${quantity} ${symbol}?`
|
| 234 |
+
);
|
| 235 |
+
|
| 236 |
+
if (confirmTrade) {
|
| 237 |
+
// Execute trade - update game state (placeholder for now)
|
| 238 |
+
// TODO: Implement actual trade execution with game state update
|
| 239 |
+
console.log(`Executing ${action} ${quantity} ${symbol} at $${price.toFixed(2)}`);
|
| 240 |
+
|
| 241 |
+
// Update UI to show trade was executed
|
| 242 |
+
alert(`Trade executed: ${action.toUpperCase()} ${quantity} ${symbol}`);
|
| 243 |
+
|
| 244 |
+
// Log decision
|
| 245 |
+
await logDecision(symbol, action, quantity, price, true);
|
| 246 |
+
|
| 247 |
+
// Ask for trust score
|
| 248 |
+
const trustScore = prompt('How much did you trust the AI advice? (1-10):');
|
| 249 |
+
if (trustScore) {
|
| 250 |
+
await logDecision(symbol, action, quantity, price, true, parseFloat(trustScore));
|
| 251 |
+
}
|
| 252 |
+
} else {
|
| 253 |
+
// Player ignored AI advice
|
| 254 |
+
await logDecision(symbol, action, quantity, price, false);
|
| 255 |
+
}
|
| 256 |
+
}
|
| 257 |
+
|
| 258 |
+
/**
|
| 259 |
+
* Update AI parameters UI (if you add controls)
|
| 260 |
+
*/
|
| 261 |
+
function updateAIParamsUI() {
|
| 262 |
+
const paramsDisplay = document.getElementById('ai-params-display');
|
| 263 |
+
if (paramsDisplay) {
|
| 264 |
+
paramsDisplay.innerHTML = `
|
| 265 |
+
Risk: ${currentAIParams.risk_level.toFixed(1)} |
|
| 266 |
+
Temp: ${currentAIParams.temperature.toFixed(2)} |
|
| 267 |
+
Confidence: ${currentAIParams.confidence_boost.toFixed(0)}
|
| 268 |
+
`;
|
| 269 |
+
}
|
| 270 |
+
}
|
| 271 |
+
|
| 272 |
+
// Export for use in game
|
| 273 |
+
window.gameAI = {
|
| 274 |
+
askAI,
|
| 275 |
+
logDecision,
|
| 276 |
+
triggerScenario,
|
| 277 |
+
updateAIParams,
|
| 278 |
+
checkScenarioTriggers,
|
| 279 |
+
executeTradeWithAI
|
| 280 |
+
};
|
game/trade.html
ADDED
|
@@ -0,0 +1,418 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
<!DOCTYPE html>
|
| 2 |
+
<html lang="en">
|
| 3 |
+
<head>
|
| 4 |
+
<meta charset="UTF-8">
|
| 5 |
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
| 6 |
+
<title>Trading Game - Proof of Concept</title>
|
| 7 |
+
<script src="https://cdn.tailwindcss.com"></script>
|
| 8 |
+
<style>
|
| 9 |
+
body { font-family: 'Inter', sans-serif; }
|
| 10 |
+
</style>
|
| 11 |
+
</head>
|
| 12 |
+
<body class="bg-gray-900 text-white min-h-screen">
|
| 13 |
+
<main class="container mx-auto px-4 py-8 max-w-7xl">
|
| 14 |
+
<h1 class="text-4xl font-bold mb-8 text-center bg-gradient-to-r from-green-400 to-blue-500 bg-clip-text text-transparent">
|
| 15 |
+
Trading Game - Proof of Concept
|
| 16 |
+
</h1>
|
| 17 |
+
|
| 18 |
+
<!-- Current Scenario (Full Width) -->
|
| 19 |
+
<div id="scenario-section" class="bg-gradient-to-r from-red-900 to-orange-900 p-6 rounded-xl mb-6 border-2 border-orange-500">
|
| 20 |
+
<h2 class="text-2xl font-bold mb-3 text-yellow-300">⚠️ Current Market Scenario</h2>
|
| 21 |
+
<div id="scenario-text" class="text-lg mb-4">
|
| 22 |
+
A typhoon is seen off the coast of New Gregoria, likely impacting the Galvanium Supply Chain.
|
| 23 |
+
This may affect logistics companies (NLN) and energy sectors (VCG).
|
| 24 |
+
</div>
|
| 25 |
+
<button id="next-scenario-button" class="px-4 py-2 bg-orange-600 rounded hover:bg-orange-700 text-sm">
|
| 26 |
+
Next Scenario
|
| 27 |
+
</button>
|
| 28 |
+
</div>
|
| 29 |
+
|
| 30 |
+
<!-- Two Column Layout -->
|
| 31 |
+
<div class="grid lg:grid-cols-2 gap-6">
|
| 32 |
+
|
| 33 |
+
<!-- LEFT SIDE: AI Advisor -->
|
| 34 |
+
<div class="space-y-6">
|
| 35 |
+
<!-- AI Parameter Sliders -->
|
| 36 |
+
<div class="bg-gray-800 p-6 rounded-xl">
|
| 37 |
+
<h2 class="text-2xl font-bold mb-4 text-blue-400">🤖 AI Advisor Settings</h2>
|
| 38 |
+
<div class="space-y-4">
|
| 39 |
+
<div>
|
| 40 |
+
<label class="block text-gray-400 mb-2">
|
| 41 |
+
Risk Tolerance: <span id="risk-value" class="text-white font-bold">5.0</span> / 10
|
| 42 |
+
</label>
|
| 43 |
+
<input type="range" id="risk-slider" min="0" max="10" step="0.5" value="5.0"
|
| 44 |
+
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
|
| 45 |
+
<div class="flex justify-between text-xs text-gray-500 mt-1">
|
| 46 |
+
<span>Conservative</span>
|
| 47 |
+
<span>Moderate</span>
|
| 48 |
+
<span>Aggressive</span>
|
| 49 |
+
</div>
|
| 50 |
+
</div>
|
| 51 |
+
<div>
|
| 52 |
+
<label class="block text-gray-400 mb-2">
|
| 53 |
+
AI Creativity: <span id="temperature-value" class="text-white font-bold">0.7</span>
|
| 54 |
+
</label>
|
| 55 |
+
<input type="range" id="temperature-slider" min="0" max="2" step="0.1" value="0.7"
|
| 56 |
+
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
|
| 57 |
+
<div class="flex justify-between text-xs text-gray-500 mt-1">
|
| 58 |
+
<span>Deterministic</span>
|
| 59 |
+
<span>Balanced</span>
|
| 60 |
+
<span>Creative</span>
|
| 61 |
+
</div>
|
| 62 |
+
</div>
|
| 63 |
+
<div>
|
| 64 |
+
<label class="block text-gray-400 mb-2">
|
| 65 |
+
Confidence Level: <span id="confidence-value" class="text-white font-bold">0</span>
|
| 66 |
+
</label>
|
| 67 |
+
<input type="range" id="confidence-slider" min="-100" max="100" step="10" value="0"
|
| 68 |
+
class="w-full h-2 bg-gray-700 rounded-lg appearance-none cursor-pointer">
|
| 69 |
+
<div class="flex justify-between text-xs text-gray-500 mt-1">
|
| 70 |
+
<span>Cautious (-100)</span>
|
| 71 |
+
<span>Neutral (0)</span>
|
| 72 |
+
<span>Very Confident (+100)</span>
|
| 73 |
+
</div>
|
| 74 |
+
</div>
|
| 75 |
+
</div>
|
| 76 |
+
</div>
|
| 77 |
+
|
| 78 |
+
<!-- AI Chat/Advice Section -->
|
| 79 |
+
<div id="ai-advice-section" class="bg-gray-800 p-6 rounded-xl">
|
| 80 |
+
<h2 class="text-2xl font-bold mb-4 text-green-400">🤖 AI Trading Advice</h2>
|
| 81 |
+
<div id="ai-message" class="bg-gray-700 p-4 rounded-lg mb-4 text-gray-300 min-h-[200px] whitespace-pre-wrap">
|
| 82 |
+
<p class="text-gray-500 italic">Adjust your settings above and click "Get AI Advice" below to receive trading recommendations based on the current scenario.</p>
|
| 83 |
+
</div>
|
| 84 |
+
<button id="get-ai-advice-button" class="w-full py-3 bg-blue-600 rounded-lg font-bold hover:bg-blue-700 mb-4">
|
| 85 |
+
💡 Get AI Trading Advice
|
| 86 |
+
</button>
|
| 87 |
+
<div id="ai-actions" class="flex gap-2" style="display: none;">
|
| 88 |
+
<button id="follow-advice-button" class="flex-1 py-2 bg-blue-600 rounded hover:bg-blue-700 text-sm">
|
| 89 |
+
Follow
|
| 90 |
+
</button>
|
| 91 |
+
<button id="ignore-advice-button" class="flex-1 py-2 bg-gray-600 rounded hover:bg-gray-700 text-sm">
|
| 92 |
+
Ignore
|
| 93 |
+
</button>
|
| 94 |
+
<button id="clear-ai-button" class="flex-1 py-2 bg-red-600 rounded hover:bg-red-700 text-sm">
|
| 95 |
+
Clear
|
| 96 |
+
</button>
|
| 97 |
+
</div>
|
| 98 |
+
</div>
|
| 99 |
+
</div>
|
| 100 |
+
|
| 101 |
+
<!-- RIGHT SIDE: Trading Portfolio -->
|
| 102 |
+
<div class="space-y-6">
|
| 103 |
+
<!-- Portfolio Summary -->
|
| 104 |
+
<div class="bg-gray-800 p-6 rounded-xl">
|
| 105 |
+
<h2 class="text-2xl font-bold mb-4">Portfolio</h2>
|
| 106 |
+
<div class="grid grid-cols-2 gap-4">
|
| 107 |
+
<div>
|
| 108 |
+
<p class="text-gray-400">Balance</p>
|
| 109 |
+
<p class="text-3xl font-bold" id="balance">$50,000.00</p>
|
| 110 |
+
</div>
|
| 111 |
+
<div>
|
| 112 |
+
<p class="text-gray-400">Positions</p>
|
| 113 |
+
<p class="text-3xl font-bold" id="positions">0</p>
|
| 114 |
+
</div>
|
| 115 |
+
</div>
|
| 116 |
+
</div>
|
| 117 |
+
|
| 118 |
+
<!-- Trading Form -->
|
| 119 |
+
<div class="bg-gray-800 p-6 rounded-xl">
|
| 120 |
+
<h2 class="text-2xl font-bold mb-4">Make a Trade</h2>
|
| 121 |
+
<div class="space-y-4 mb-4">
|
| 122 |
+
<div>
|
| 123 |
+
<label class="block text-gray-400 mb-2">Symbol</label>
|
| 124 |
+
<select id="trade-symbol" class="w-full bg-gray-700 rounded p-3 text-white">
|
| 125 |
+
<option>VCG</option>
|
| 126 |
+
<option>CSI</option>
|
| 127 |
+
<option>STDY</option>
|
| 128 |
+
<option>AUBIO</option>
|
| 129 |
+
<option>NLN</option>
|
| 130 |
+
</select>
|
| 131 |
+
</div>
|
| 132 |
+
<div class="grid grid-cols-2 gap-4">
|
| 133 |
+
<div>
|
| 134 |
+
<label class="block text-gray-400 mb-2">Quantity</label>
|
| 135 |
+
<input id="trade-quantity" type="number" value="10" min="1"
|
| 136 |
+
class="w-full bg-gray-700 rounded p-3 text-white">
|
| 137 |
+
</div>
|
| 138 |
+
<div>
|
| 139 |
+
<label class="block text-gray-400 mb-2">Price</label>
|
| 140 |
+
<input id="trade-price" type="number" value="100" min="0" step="0.01"
|
| 141 |
+
class="w-full bg-gray-700 rounded p-3 text-white" readonly>
|
| 142 |
+
</div>
|
| 143 |
+
</div>
|
| 144 |
+
</div>
|
| 145 |
+
<div class="flex gap-4">
|
| 146 |
+
<button id="buy-button" class="flex-1 py-3 bg-green-600 rounded-lg font-bold hover:bg-green-700">
|
| 147 |
+
Buy
|
| 148 |
+
</button>
|
| 149 |
+
<button id="sell-button" class="flex-1 py-3 bg-red-600 rounded-lg font-bold hover:bg-red-700">
|
| 150 |
+
Sell
|
| 151 |
+
</button>
|
| 152 |
+
</div>
|
| 153 |
+
</div>
|
| 154 |
+
|
| 155 |
+
<!-- Recent Trades -->
|
| 156 |
+
<div class="bg-gray-800 p-6 rounded-xl">
|
| 157 |
+
<h2 class="text-2xl font-bold mb-4">Recent Trades</h2>
|
| 158 |
+
<div id="trade-history" class="space-y-2 max-h-[400px] overflow-y-auto">
|
| 159 |
+
<p class="text-gray-400 text-center">No trades yet</p>
|
| 160 |
+
</div>
|
| 161 |
+
</div>
|
| 162 |
+
</div>
|
| 163 |
+
</div>
|
| 164 |
+
</main>
|
| 165 |
+
|
| 166 |
+
<script src="game-api-integration.js"></script>
|
| 167 |
+
<script>
|
| 168 |
+
// Simple game state
|
| 169 |
+
let gameState = {
|
| 170 |
+
balance: 50000,
|
| 171 |
+
positions: [],
|
| 172 |
+
trades: [],
|
| 173 |
+
currentScenario: {
|
| 174 |
+
title: "Typhoon near New Gregoria",
|
| 175 |
+
description: "A typhoon is seen off the coast of New Gregoria, likely impacting the Galvanium Supply Chain. This may affect logistics companies (NLN) and energy sectors (VCG).",
|
| 176 |
+
affectedSymbols: ["NLN", "VCG"]
|
| 177 |
+
}
|
| 178 |
+
};
|
| 179 |
+
|
| 180 |
+
// Scenario templates
|
| 181 |
+
const scenarios = [
|
| 182 |
+
{
|
| 183 |
+
title: "Typhoon near New Gregoria",
|
| 184 |
+
description: "A typhoon is seen off the coast of New Gregoria, likely impacting the Galvanium Supply Chain. This may affect logistics companies (NLN) and energy sectors (VCG).",
|
| 185 |
+
affectedSymbols: ["NLN", "VCG"]
|
| 186 |
+
},
|
| 187 |
+
{
|
| 188 |
+
title: "Quantum Processor Breakthrough",
|
| 189 |
+
description: "Veridian Capital Group (VCG) announces a major quantum computing breakthrough. This could significantly impact energy and tech sectors.",
|
| 190 |
+
affectedSymbols: ["VCG", "CSI"]
|
| 191 |
+
},
|
| 192 |
+
{
|
| 193 |
+
title: "Asteroid Mining Success",
|
| 194 |
+
description: "Stellar Dynamics Corp (STDY) successfully completes asteroid mining operations, securing rare earth minerals. Logistics and space commerce stocks may surge.",
|
| 195 |
+
affectedSymbols: ["STDY", "NLN"]
|
| 196 |
+
},
|
| 197 |
+
{
|
| 198 |
+
title: "Biosynthetics Regulatory Approval",
|
| 199 |
+
description: "Aurora Biosynthetics (AUBIO) receives approval for commercial distribution of synthetic products, opening new markets worth 200 billion credits.",
|
| 200 |
+
affectedSymbols: ["AUBIO"]
|
| 201 |
+
}
|
| 202 |
+
];
|
| 203 |
+
|
| 204 |
+
// Update slider displays
|
| 205 |
+
document.getElementById('risk-slider').addEventListener('input', (e) => {
|
| 206 |
+
document.getElementById('risk-value').textContent = parseFloat(e.target.value).toFixed(1);
|
| 207 |
+
});
|
| 208 |
+
|
| 209 |
+
document.getElementById('temperature-slider').addEventListener('input', (e) => {
|
| 210 |
+
document.getElementById('temperature-value').textContent = parseFloat(e.target.value).toFixed(1);
|
| 211 |
+
});
|
| 212 |
+
|
| 213 |
+
document.getElementById('confidence-slider').addEventListener('input', (e) => {
|
| 214 |
+
document.getElementById('confidence-value').textContent = e.target.value;
|
| 215 |
+
});
|
| 216 |
+
|
| 217 |
+
// Update price when symbol changes
|
| 218 |
+
document.getElementById('trade-symbol').addEventListener('change', (e) => {
|
| 219 |
+
const prices = { VCG: 150, CSI: 200, STDY: 100, AUBIO: 75, NLN: 120 };
|
| 220 |
+
document.getElementById('trade-price').value = prices[e.target.value] || 100;
|
| 221 |
+
});
|
| 222 |
+
|
| 223 |
+
// Initialize price
|
| 224 |
+
document.getElementById('trade-symbol').dispatchEvent(new Event('change'));
|
| 225 |
+
|
| 226 |
+
// Next scenario button
|
| 227 |
+
document.getElementById('next-scenario-button').addEventListener('click', () => {
|
| 228 |
+
const randomScenario = scenarios[Math.floor(Math.random() * scenarios.length)];
|
| 229 |
+
gameState.currentScenario = randomScenario;
|
| 230 |
+
document.getElementById('scenario-text').textContent = randomScenario.description;
|
| 231 |
+
});
|
| 232 |
+
|
| 233 |
+
let pendingTrade = null;
|
| 234 |
+
|
| 235 |
+
// Get AI advice button (standalone)
|
| 236 |
+
document.getElementById('get-ai-advice-button').addEventListener('click', async () => {
|
| 237 |
+
const symbol = document.getElementById('trade-symbol').value;
|
| 238 |
+
const quantity = parseInt(document.getElementById('trade-quantity').value);
|
| 239 |
+
const price = parseFloat(document.getElementById('trade-price').value);
|
| 240 |
+
|
| 241 |
+
await showAIAdvice(symbol, quantity, price, 'buy');
|
| 242 |
+
});
|
| 243 |
+
|
| 244 |
+
// Buy button handler
|
| 245 |
+
document.getElementById('buy-button').addEventListener('click', async () => {
|
| 246 |
+
const symbol = document.getElementById('trade-symbol').value;
|
| 247 |
+
const quantity = parseInt(document.getElementById('trade-quantity').value);
|
| 248 |
+
const price = parseFloat(document.getElementById('trade-price').value);
|
| 249 |
+
const total = quantity * price;
|
| 250 |
+
|
| 251 |
+
if (total > gameState.balance) {
|
| 252 |
+
alert('Insufficient balance!');
|
| 253 |
+
return;
|
| 254 |
+
}
|
| 255 |
+
|
| 256 |
+
pendingTrade = { symbol, quantity, price, action: 'buy' };
|
| 257 |
+
await showAIAdvice(symbol, quantity, price, 'buy');
|
| 258 |
+
});
|
| 259 |
+
|
| 260 |
+
// Sell button handler
|
| 261 |
+
document.getElementById('sell-button').addEventListener('click', async () => {
|
| 262 |
+
const symbol = document.getElementById('trade-symbol').value;
|
| 263 |
+
const quantity = parseInt(document.getElementById('trade-quantity').value);
|
| 264 |
+
const price = parseFloat(document.getElementById('trade-price').value);
|
| 265 |
+
|
| 266 |
+
pendingTrade = { symbol, quantity, price, action: 'sell' };
|
| 267 |
+
await showAIAdvice(symbol, quantity, price, 'sell');
|
| 268 |
+
});
|
| 269 |
+
|
| 270 |
+
// Show AI advice
|
| 271 |
+
async function showAIAdvice(symbol, quantity, price, action) {
|
| 272 |
+
const aiMessage = document.getElementById('ai-message');
|
| 273 |
+
const aiActions = document.getElementById('ai-actions');
|
| 274 |
+
|
| 275 |
+
aiMessage.textContent = 'Getting AI advice...';
|
| 276 |
+
aiActions.style.display = 'none';
|
| 277 |
+
|
| 278 |
+
try {
|
| 279 |
+
if (window.gameAI && window.gameAI.askAI) {
|
| 280 |
+
// Get current slider values
|
| 281 |
+
const riskLevel = parseFloat(document.getElementById('risk-slider').value);
|
| 282 |
+
const temperature = parseFloat(document.getElementById('temperature-slider').value);
|
| 283 |
+
const confidence = parseFloat(document.getElementById('confidence-slider').value);
|
| 284 |
+
|
| 285 |
+
// Include scenario context in the question
|
| 286 |
+
const scenarioText = gameState.currentScenario.description;
|
| 287 |
+
const question = `Current Market Scenario: ${scenarioText}\n\nShould I ${action} ${quantity} shares of ${symbol} at $${price.toFixed(2)}? Consider the current scenario and provide a direct recommendation based on my risk tolerance of ${riskLevel}/10.`;
|
| 288 |
+
|
| 289 |
+
// Update AI parameters
|
| 290 |
+
if (window.gameAI.updateAIParams) {
|
| 291 |
+
window.gameAI.updateAIParams(riskLevel, temperature, confidence);
|
| 292 |
+
}
|
| 293 |
+
|
| 294 |
+
const response = await window.gameAI.askAI(question, {
|
| 295 |
+
risk_level: riskLevel,
|
| 296 |
+
temperature: temperature,
|
| 297 |
+
confidence_boost: confidence
|
| 298 |
+
});
|
| 299 |
+
|
| 300 |
+
aiMessage.textContent = response.answer || 'No advice available';
|
| 301 |
+
|
| 302 |
+
// Show action buttons if there's a pending trade
|
| 303 |
+
if (pendingTrade) {
|
| 304 |
+
aiActions.style.display = 'flex';
|
| 305 |
+
}
|
| 306 |
+
} else {
|
| 307 |
+
aiMessage.textContent = 'AI service not available. Check console for errors.';
|
| 308 |
+
}
|
| 309 |
+
} catch (error) {
|
| 310 |
+
console.error('AI advice error:', error);
|
| 311 |
+
aiMessage.textContent = 'Error getting AI advice. Check console.';
|
| 312 |
+
}
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
// Follow AI advice
|
| 316 |
+
document.getElementById('follow-advice-button').addEventListener('click', async () => {
|
| 317 |
+
if (!pendingTrade) return;
|
| 318 |
+
|
| 319 |
+
executeTrade(pendingTrade, true);
|
| 320 |
+
document.getElementById('ai-actions').style.display = 'none';
|
| 321 |
+
pendingTrade = null;
|
| 322 |
+
});
|
| 323 |
+
|
| 324 |
+
// Ignore advice but still trade
|
| 325 |
+
document.getElementById('ignore-advice-button').addEventListener('click', async () => {
|
| 326 |
+
if (!pendingTrade) return;
|
| 327 |
+
|
| 328 |
+
executeTrade(pendingTrade, false);
|
| 329 |
+
document.getElementById('ai-actions').style.display = 'none';
|
| 330 |
+
pendingTrade = null;
|
| 331 |
+
});
|
| 332 |
+
|
| 333 |
+
// Clear AI button
|
| 334 |
+
document.getElementById('clear-ai-button').addEventListener('click', () => {
|
| 335 |
+
document.getElementById('ai-message').innerHTML = '<p class="text-gray-500 italic">Adjust your settings above and click "Get AI Advice" to receive trading recommendations based on the current scenario.</p>';
|
| 336 |
+
document.getElementById('ai-actions').style.display = 'none';
|
| 337 |
+
pendingTrade = null;
|
| 338 |
+
});
|
| 339 |
+
|
| 340 |
+
// Execute trade
|
| 341 |
+
function executeTrade(trade, followedAdvice) {
|
| 342 |
+
const { symbol, quantity, price, action } = trade;
|
| 343 |
+
const total = quantity * price;
|
| 344 |
+
|
| 345 |
+
if (action === 'buy') {
|
| 346 |
+
gameState.balance -= total;
|
| 347 |
+
const existingPosition = gameState.positions.find(p => p.symbol === symbol);
|
| 348 |
+
if (existingPosition) {
|
| 349 |
+
existingPosition.quantity += quantity;
|
| 350 |
+
} else {
|
| 351 |
+
gameState.positions.push({ symbol, quantity, price });
|
| 352 |
+
}
|
| 353 |
+
} else { // sell
|
| 354 |
+
gameState.balance += total;
|
| 355 |
+
const existingPosition = gameState.positions.find(p => p.symbol === symbol);
|
| 356 |
+
if (existingPosition) {
|
| 357 |
+
existingPosition.quantity -= quantity;
|
| 358 |
+
if (existingPosition.quantity <= 0) {
|
| 359 |
+
gameState.positions = gameState.positions.filter(p => p.symbol !== symbol);
|
| 360 |
+
}
|
| 361 |
+
}
|
| 362 |
+
}
|
| 363 |
+
|
| 364 |
+
// Log trade
|
| 365 |
+
gameState.trades.unshift({
|
| 366 |
+
timestamp: new Date().toLocaleTimeString(),
|
| 367 |
+
symbol,
|
| 368 |
+
action,
|
| 369 |
+
quantity,
|
| 370 |
+
price: price.toFixed(2),
|
| 371 |
+
total: total.toFixed(2),
|
| 372 |
+
followedAdvice
|
| 373 |
+
});
|
| 374 |
+
|
| 375 |
+
// Update UI
|
| 376 |
+
updateUI();
|
| 377 |
+
|
| 378 |
+
// Log to backend
|
| 379 |
+
if (window.gameAI && window.gameAI.logDecision) {
|
| 380 |
+
window.gameAI.logDecision(symbol, action, quantity, price, followedAdvice);
|
| 381 |
+
}
|
| 382 |
+
|
| 383 |
+
// Ask for trust score
|
| 384 |
+
const trustScore = prompt('How much did you trust the AI advice? (1-10):');
|
| 385 |
+
if (trustScore && window.gameAI && window.gameAI.logDecision) {
|
| 386 |
+
window.gameAI.logDecision(symbol, action, quantity, price, followedAdvice, parseFloat(trustScore));
|
| 387 |
+
}
|
| 388 |
+
}
|
| 389 |
+
|
| 390 |
+
// Update UI
|
| 391 |
+
function updateUI() {
|
| 392 |
+
document.getElementById('balance').textContent = '$' + gameState.balance.toFixed(2);
|
| 393 |
+
document.getElementById('positions').textContent = gameState.positions.length;
|
| 394 |
+
|
| 395 |
+
const tradeHistory = document.getElementById('trade-history');
|
| 396 |
+
if (gameState.trades.length === 0) {
|
| 397 |
+
tradeHistory.innerHTML = '<p class="text-gray-400 text-center">No trades yet</p>';
|
| 398 |
+
} else {
|
| 399 |
+
tradeHistory.innerHTML = gameState.trades.map(trade => `
|
| 400 |
+
<div class="bg-gray-700 p-3 rounded flex justify-between items-center">
|
| 401 |
+
<div>
|
| 402 |
+
<span class="font-bold ${trade.action === 'buy' ? 'text-green-400' : 'text-red-400'}">
|
| 403 |
+
${trade.action.toUpperCase()}
|
| 404 |
+
</span>
|
| 405 |
+
${trade.quantity} ${trade.symbol} @ $${trade.price}
|
| 406 |
+
${trade.followedAdvice ? '✅ (Followed AI)' : '❌ (Ignored AI)'}
|
| 407 |
+
</div>
|
| 408 |
+
<div class="text-right">
|
| 409 |
+
<p class="font-bold">$${trade.total}</p>
|
| 410 |
+
<p class="text-sm text-gray-400">${trade.timestamp}</p>
|
| 411 |
+
</div>
|
| 412 |
+
</div>
|
| 413 |
+
`).join('');
|
| 414 |
+
}
|
| 415 |
+
}
|
| 416 |
+
</script>
|
| 417 |
+
</body>
|
| 418 |
+
</html>
|
game_api.py
ADDED
|
@@ -0,0 +1,280 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
FastAPI Backend for Trading Game + AI Chatbot Integration
|
| 3 |
+
Supports tunable AI parameters for psychological experiments
|
| 4 |
+
"""
|
| 5 |
+
from fastapi import FastAPI, HTTPException
|
| 6 |
+
from fastapi.middleware.cors import CORSMiddleware
|
| 7 |
+
from fastapi.staticfiles import StaticFiles
|
| 8 |
+
from pydantic import BaseModel
|
| 9 |
+
from typing import Optional, List, Dict
|
| 10 |
+
import json
|
| 11 |
+
import os
|
| 12 |
+
from datetime import datetime
|
| 13 |
+
|
| 14 |
+
# Import your existing chain from app.py
|
| 15 |
+
from app import chain, llm, ChatOpenAI, PromptTemplate
|
| 16 |
+
from langchain_core.prompts import ChatPromptTemplate
|
| 17 |
+
|
| 18 |
+
app = FastAPI(title="Trading Game AI Experiment API")
|
| 19 |
+
|
| 20 |
+
# CORS for local development
|
| 21 |
+
app.add_middleware(
|
| 22 |
+
CORSMiddleware,
|
| 23 |
+
allow_origins=["*"], # In production, specify your domain
|
| 24 |
+
allow_credentials=True,
|
| 25 |
+
allow_methods=["*"],
|
| 26 |
+
allow_headers=["*"],
|
| 27 |
+
)
|
| 28 |
+
|
| 29 |
+
# Serve static files from game directory
|
| 30 |
+
app.mount("/game", StaticFiles(directory="game", html=True), name="game")
|
| 31 |
+
|
| 32 |
+
# Experiment state storage (in production, use a database)
|
| 33 |
+
experiment_state = {}
|
| 34 |
+
game_sessions = {}
|
| 35 |
+
|
| 36 |
+
class AIMessageRequest(BaseModel):
|
| 37 |
+
question: str
|
| 38 |
+
chat_history: List[tuple] = []
|
| 39 |
+
risk_level: float = 5.0 # 0-10, affects advice certainty
|
| 40 |
+
temperature: float = 0.7 # 0.0-2.0, affects response randomness
|
| 41 |
+
confidence_boost: float = 0.0 # -100 to +100, manipulates trustworthiness
|
| 42 |
+
session_id: str = "default"
|
| 43 |
+
|
| 44 |
+
class TradingDecision(BaseModel):
|
| 45 |
+
session_id: str
|
| 46 |
+
symbol: str
|
| 47 |
+
action: str # "buy" or "sell"
|
| 48 |
+
quantity: int
|
| 49 |
+
price: float
|
| 50 |
+
ai_advice_followed: bool
|
| 51 |
+
trust_score: Optional[float] = None # 1-10, explicit trust rating
|
| 52 |
+
|
| 53 |
+
class ScenarioTrigger(BaseModel):
|
| 54 |
+
session_id: str
|
| 55 |
+
scenario_type: str # "volatility", "large_position", "loss_recovery", "news_event"
|
| 56 |
+
context: Dict
|
| 57 |
+
ai_required: bool = True
|
| 58 |
+
|
| 59 |
+
def get_ai_with_params(risk_level: float, temperature: float, confidence_boost: float):
|
| 60 |
+
"""Create LLM with tunable parameters for experiment"""
|
| 61 |
+
# Adjust temperature based on risk level (higher risk = more variability)
|
| 62 |
+
adjusted_temp = temperature + (risk_level / 10.0) * 0.3
|
| 63 |
+
|
| 64 |
+
# Get model name from environment or use default
|
| 65 |
+
import os
|
| 66 |
+
model_name = os.getenv("HF_MODEL_NAME", "meta-llama/Llama-3.1-8B-Instruct:novita")
|
| 67 |
+
api_key = os.getenv("HF_TOKEN")
|
| 68 |
+
if not api_key:
|
| 69 |
+
raise ValueError("HF_TOKEN environment variable is not set.")
|
| 70 |
+
|
| 71 |
+
# Create new LLM instance with adjusted parameters
|
| 72 |
+
tuned_llm = ChatOpenAI(
|
| 73 |
+
model=model_name,
|
| 74 |
+
base_url="https://router.huggingface.co/v1",
|
| 75 |
+
api_key=api_key,
|
| 76 |
+
temperature=min(adjusted_temp, 2.0), # Cap at 2.0
|
| 77 |
+
max_tokens=512,
|
| 78 |
+
)
|
| 79 |
+
return tuned_llm
|
| 80 |
+
|
| 81 |
+
def get_contextual_prompt(context: Dict, risk_level: float, confidence_boost: float):
|
| 82 |
+
"""Generate prompt that reflects risk level and confidence boost"""
|
| 83 |
+
risk_descriptor = {
|
| 84 |
+
0: "extremely conservative",
|
| 85 |
+
2: "very conservative",
|
| 86 |
+
4: "conservative",
|
| 87 |
+
5: "moderate",
|
| 88 |
+
6: "moderately aggressive",
|
| 89 |
+
8: "aggressive",
|
| 90 |
+
10: "very aggressive"
|
| 91 |
+
}
|
| 92 |
+
risk_text = risk_descriptor.get(int(risk_level), "moderate")
|
| 93 |
+
|
| 94 |
+
confidence_text = ""
|
| 95 |
+
if confidence_boost > 20:
|
| 96 |
+
confidence_text = "You are highly confident in your recommendations."
|
| 97 |
+
elif confidence_boost > 0:
|
| 98 |
+
confidence_text = "You are confident in your recommendations."
|
| 99 |
+
elif confidence_boost < -20:
|
| 100 |
+
confidence_text = "You are uncertain and should express caution in your recommendations."
|
| 101 |
+
|
| 102 |
+
base_template = """
|
| 103 |
+
You are an AI trading advisor for the Quantum Financial Network. Your risk profile is {risk_level}.
|
| 104 |
+
{confidence_text}
|
| 105 |
+
|
| 106 |
+
You provide trading advice based on market data. Consider:
|
| 107 |
+
- Current market conditions: {market_context}
|
| 108 |
+
- Player portfolio status: {portfolio_context}
|
| 109 |
+
- Recent events: {events_context}
|
| 110 |
+
|
| 111 |
+
Answer the question with appropriate caution/certainty based on your risk profile.
|
| 112 |
+
|
| 113 |
+
Context:
|
| 114 |
+
{{context}}
|
| 115 |
+
|
| 116 |
+
Question: {{question}}
|
| 117 |
+
|
| 118 |
+
Answer:
|
| 119 |
+
"""
|
| 120 |
+
|
| 121 |
+
return PromptTemplate(
|
| 122 |
+
input_variables=["context", "question"],
|
| 123 |
+
template=base_template.format(
|
| 124 |
+
risk_level=risk_text,
|
| 125 |
+
confidence_text=confidence_text,
|
| 126 |
+
market_context=context.get("market", "normal conditions"),
|
| 127 |
+
portfolio_context=context.get("portfolio", "standard portfolio"),
|
| 128 |
+
events_context=context.get("events", "no major events")
|
| 129 |
+
)
|
| 130 |
+
)
|
| 131 |
+
|
| 132 |
+
@app.post("/api/ai/chat")
|
| 133 |
+
async def chat_with_ai(request: AIMessageRequest):
|
| 134 |
+
"""Main AI chat endpoint with tunable parameters"""
|
| 135 |
+
try:
|
| 136 |
+
# Get or initialize session
|
| 137 |
+
if request.session_id not in game_sessions:
|
| 138 |
+
game_sessions[request.session_id] = {
|
| 139 |
+
"chat_history": [],
|
| 140 |
+
"decisions": [],
|
| 141 |
+
"trust_scores": [],
|
| 142 |
+
"params_history": []
|
| 143 |
+
}
|
| 144 |
+
|
| 145 |
+
session = game_sessions[request.session_id]
|
| 146 |
+
|
| 147 |
+
# Use the existing chain from app.py for now (simplified for POC)
|
| 148 |
+
# Convert chat_history from list of tuples to proper format if needed
|
| 149 |
+
chat_history_tuples = []
|
| 150 |
+
if request.chat_history:
|
| 151 |
+
for item in request.chat_history:
|
| 152 |
+
if isinstance(item, (list, tuple)) and len(item) >= 2:
|
| 153 |
+
chat_history_tuples.append((str(item[0]), str(item[1])))
|
| 154 |
+
|
| 155 |
+
# Enhance the question with scenario context and slider parameters
|
| 156 |
+
# Extract scenario context if present in question
|
| 157 |
+
enhanced_question = request.question
|
| 158 |
+
|
| 159 |
+
# Add risk tolerance context to question if not already included
|
| 160 |
+
if "risk tolerance" not in enhanced_question.lower() and request.risk_level is not None:
|
| 161 |
+
risk_desc = {
|
| 162 |
+
0: "extremely conservative",
|
| 163 |
+
1: "very conservative",
|
| 164 |
+
3: "conservative",
|
| 165 |
+
5: "moderate",
|
| 166 |
+
7: "moderately aggressive",
|
| 167 |
+
9: "aggressive",
|
| 168 |
+
10: "very aggressive"
|
| 169 |
+
}
|
| 170 |
+
risk_text = risk_desc.get(int(request.risk_level), "moderate")
|
| 171 |
+
|
| 172 |
+
if "Current Market Scenario" not in enhanced_question:
|
| 173 |
+
enhanced_question = f"Risk Profile: {risk_text} ({request.risk_level}/10)\n\n{enhanced_question}"
|
| 174 |
+
|
| 175 |
+
# Get AI response using the existing chain
|
| 176 |
+
result = chain({
|
| 177 |
+
"question": enhanced_question,
|
| 178 |
+
"chat_history": chat_history_tuples
|
| 179 |
+
})
|
| 180 |
+
|
| 181 |
+
# Log interaction for experiment
|
| 182 |
+
interaction = {
|
| 183 |
+
"timestamp": datetime.now().isoformat(),
|
| 184 |
+
"question": request.question,
|
| 185 |
+
"response": result["answer"],
|
| 186 |
+
"risk_level": request.risk_level,
|
| 187 |
+
"temperature": request.temperature,
|
| 188 |
+
"confidence_boost": request.confidence_boost
|
| 189 |
+
}
|
| 190 |
+
session["params_history"].append(interaction)
|
| 191 |
+
|
| 192 |
+
return {
|
| 193 |
+
"answer": result["answer"],
|
| 194 |
+
"sources": [doc.page_content[:100] for doc in result.get("source_documents", [])] if "source_documents" in result else [],
|
| 195 |
+
"interaction_id": len(session["params_history"]) - 1
|
| 196 |
+
}
|
| 197 |
+
|
| 198 |
+
except Exception as e:
|
| 199 |
+
import traceback
|
| 200 |
+
error_detail = str(e) + "\n" + traceback.format_exc()
|
| 201 |
+
raise HTTPException(status_code=500, detail=error_detail)
|
| 202 |
+
|
| 203 |
+
@app.post("/api/experiment/decision")
|
| 204 |
+
async def log_decision(decision: TradingDecision):
|
| 205 |
+
"""Log player trading decisions for trust analysis"""
|
| 206 |
+
if decision.session_id not in game_sessions:
|
| 207 |
+
game_sessions[decision.session_id] = {
|
| 208 |
+
"chat_history": [],
|
| 209 |
+
"decisions": [],
|
| 210 |
+
"trust_scores": [],
|
| 211 |
+
"params_history": []
|
| 212 |
+
}
|
| 213 |
+
|
| 214 |
+
game_sessions[decision.session_id]["decisions"].append({
|
| 215 |
+
"timestamp": datetime.now().isoformat(),
|
| 216 |
+
"symbol": decision.symbol,
|
| 217 |
+
"action": decision.action,
|
| 218 |
+
"quantity": decision.quantity,
|
| 219 |
+
"price": decision.price,
|
| 220 |
+
"ai_advice_followed": decision.ai_advice_followed,
|
| 221 |
+
"trust_score": decision.trust_score
|
| 222 |
+
})
|
| 223 |
+
|
| 224 |
+
return {"status": "logged", "decision_id": len(game_sessions[decision.session_id]["decisions"]) - 1}
|
| 225 |
+
|
| 226 |
+
@app.post("/api/experiment/scenario")
|
| 227 |
+
async def trigger_scenario(scenario: ScenarioTrigger):
|
| 228 |
+
"""Trigger situational scenarios that require AI assistance"""
|
| 229 |
+
scenario_prompts = {
|
| 230 |
+
"volatility": "The market is experiencing high volatility. A stock in your portfolio has moved 10% in the last hour. What should you do?",
|
| 231 |
+
"large_position": "You're about to make a large position trade ($10,000+). This would represent a significant portion of your portfolio. Should you proceed?",
|
| 232 |
+
"loss_recovery": "You're down 5% today. Would you like advice on whether to cut losses or hold your positions?",
|
| 233 |
+
"news_event": "Breaking news just released that affects several stocks in your watchlist. How should this impact your trading decisions?"
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
if scenario.scenario_type not in scenario_prompts:
|
| 237 |
+
raise HTTPException(status_code=400, detail="Unknown scenario type")
|
| 238 |
+
|
| 239 |
+
prompt = scenario_prompts[scenario.scenario_type]
|
| 240 |
+
|
| 241 |
+
# Return scenario data for frontend to trigger AI chat
|
| 242 |
+
return {
|
| 243 |
+
"scenario_type": scenario.scenario_type,
|
| 244 |
+
"prompt": prompt,
|
| 245 |
+
"context": scenario.context,
|
| 246 |
+
"requires_ai": scenario.ai_required
|
| 247 |
+
}
|
| 248 |
+
|
| 249 |
+
@app.get("/api/experiment/session/{session_id}")
|
| 250 |
+
async def get_session_data(session_id: str):
|
| 251 |
+
"""Get all experiment data for a session"""
|
| 252 |
+
if session_id not in game_sessions:
|
| 253 |
+
raise HTTPException(status_code=404, detail="Session not found")
|
| 254 |
+
|
| 255 |
+
return game_sessions[session_id]
|
| 256 |
+
|
| 257 |
+
@app.get("/api/experiment/export/{session_id}")
|
| 258 |
+
async def export_experiment_data(session_id: str):
|
| 259 |
+
"""Export experiment data as JSON for analysis"""
|
| 260 |
+
if session_id not in game_sessions:
|
| 261 |
+
raise HTTPException(status_code=404, detail="Session not found")
|
| 262 |
+
|
| 263 |
+
data = game_sessions[session_id]
|
| 264 |
+
return data
|
| 265 |
+
|
| 266 |
+
@app.get("/")
|
| 267 |
+
async def root():
|
| 268 |
+
"""Redirect to game"""
|
| 269 |
+
from fastapi.responses import RedirectResponse
|
| 270 |
+
return RedirectResponse(url="/game/trade.html")
|
| 271 |
+
|
| 272 |
+
@app.get("/game")
|
| 273 |
+
async def game_redirect():
|
| 274 |
+
"""Redirect to game"""
|
| 275 |
+
from fastapi.responses import RedirectResponse
|
| 276 |
+
return RedirectResponse(url="/game/trade.html")
|
| 277 |
+
|
| 278 |
+
if __name__ == "__main__":
|
| 279 |
+
import uvicorn
|
| 280 |
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
multiple_docs/availability.txt
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
MARKET AVAILABILITY AND TRADING HOURS:
|
| 2 |
+
|
| 3 |
+
TRADING SESSION SCHEDULE:
|
| 4 |
+
Primary Markets operate 24/7 with automated systems, but human trading follows standard patterns:
|
| 5 |
+
- Pre-Market: 06:00-08:00 Standard Time (limited liquidity)
|
| 6 |
+
- Morning Session: 08:00-12:00 (highest volume, best for day trading)
|
| 7 |
+
- Midday Lull: 12:00-14:00 (lower volume, consolidation period)
|
| 8 |
+
- Afternoon Session: 14:00-18:00 (second highest volume period)
|
| 9 |
+
- Evening Session: 18:00-22:00 (moderate volume, institutional activity)
|
| 10 |
+
- After-Hours: 22:00-06:00 (automated trading only, wider spreads)
|
| 11 |
+
|
| 12 |
+
MARKET HOLIDAYS:
|
| 13 |
+
The following dates are market holidays (exchanges closed):
|
| 14 |
+
- Quantum New Year (January 1st)
|
| 15 |
+
- Founders Day (March 15th)
|
| 16 |
+
- Market Reconciliation Day (June 30th)
|
| 17 |
+
- Interplanetary Trade Summit (September 10th-12th)
|
| 18 |
+
- Year-End Settlement (December 28th-31st)
|
| 19 |
+
|
| 20 |
+
LONGEST TRADING SESSION:
|
| 21 |
+
Continuous trading is available Monday 08:00 through Friday 22:00 with no interruptions except scheduled maintenance windows.
|
| 22 |
+
|
| 23 |
+
MAINTENANCE WINDOWS:
|
| 24 |
+
- Weekly: Sunday 03:00-05:00 (system updates and reconciliation)
|
| 25 |
+
- Monthly: First Sunday 02:00-06:00 (major system upgrades)
|
| 26 |
+
- Emergency maintenance is announced with 4-hour notice minimum
|
| 27 |
+
|
| 28 |
+
LOWEST LIQUIDITY PERIODS:
|
| 29 |
+
- Sunday 01:00-07:00 (weekly low point)
|
| 30 |
+
- Holiday weekends (24-48 hour reduced activity)
|
| 31 |
+
- End-of-quarter settlement days (afternoon trading only)
|
| 32 |
+
|
| 33 |
+
BEST TRADING OPPORTUNITIES:
|
| 34 |
+
- Market opens (08:00) show highest momentum and liquidity
|
| 35 |
+
- Mid-morning (10:00-11:00) typically has news-driven volatility
|
| 36 |
+
- Pre-close (21:00-22:00) often sees institutional rebalancing
|
multiple_docs/background.txt
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TRADING UNIVERSE BACKGROUND INFORMATION:
|
| 2 |
+
|
| 3 |
+
THE QUANTUM FINANCIAL NETWORK (QFN):
|
| 4 |
+
Established in 2030 following the Quantum Revolution, the QFN emerged as the primary financial infrastructure connecting Earth, Mars, and orbital stations. The network leverages quantum computing for instant cross-planetary transactions and real-time market data processing.
|
| 5 |
+
|
| 6 |
+
CURRENCY SYSTEM:
|
| 7 |
+
The primary currency, Credits (CRD), is backed by quantum computational resources rather than traditional gold or fiat systems. Each credit represents a standardized unit of quantum processing power that can be converted into actual computation time when needed.
|
| 8 |
+
|
| 9 |
+
MARKET GEOGRAPHY:
|
| 10 |
+
- Earth: 45% of total market activity
|
| 11 |
+
- Mars Colonies: 35% of market activity
|
| 12 |
+
- Orbital Stations: 15% of market activity
|
| 13 |
+
- Deep Space Operations: 5% of market activity
|
| 14 |
+
|
| 15 |
+
MAJOR ECONOMIC SECTORS:
|
| 16 |
+
1. Quantum Computing (22% of market cap)
|
| 17 |
+
2. Interplanetary Logistics (18% of market cap)
|
| 18 |
+
3. Biotechnology (15% of market cap)
|
| 19 |
+
4. Energy Systems (14% of market cap)
|
| 20 |
+
5. AI and Automation (12% of market cap)
|
| 21 |
+
6. Space Commerce (10% of market cap)
|
| 22 |
+
7. Other Sectors (9% of market cap)
|
| 23 |
+
|
| 24 |
+
HISTORICAL CONTEXT:
|
| 25 |
+
The trading universe evolved from traditional Earth-based markets after the discovery of practical quantum computing in 2028. The ability to instantaneously process trades across planetary distances revolutionized financial markets, leading to the formation of the current multi-planetary trading system.
|
| 26 |
+
|
| 27 |
+
POPULATION AND DEMOGRAPHICS:
|
| 28 |
+
- Total Population: 18.7 billion across all territories
|
| 29 |
+
- Active Traders: 340 million registered accounts
|
| 30 |
+
- Institutional Accounts: 45,000 major institutions
|
| 31 |
+
- Daily Active Traders: 28-35 million on average
|
| 32 |
+
|
| 33 |
+
TECHNOLOGICAL INFRASTRUCTURE:
|
| 34 |
+
All trading occurs on quantum-secured blockchain networks with sub-millisecond latency even across interplanetary distances. AI trading algorithms account for approximately 67% of all transactions, with human traders managing the remaining 33%.
|
multiple_docs/chatbot_exp.txt
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
AUTOMATED TRADING SYSTEMS AND ALGORITHMS:
|
| 2 |
+
|
| 3 |
+
ALGORITHMIC TRADING OVERVIEW:
|
| 4 |
+
Automated trading systems account for approximately 67% of all market transactions. These AI-powered algorithms can process vast amounts of data and execute trades in microseconds, far faster than human traders.
|
| 5 |
+
|
| 6 |
+
QUANTUMMIND PRO ALGORITHM:
|
| 7 |
+
Developed by Cybernetic Systems Innovation (CSI), this flagship trading algorithm uses quantum computing principles to analyze market patterns across multiple dimensions simultaneously. It has demonstrated 94% accuracy in predicting major market movements.
|
| 8 |
+
|
| 9 |
+
POPULAR TRADING BOTS:
|
| 10 |
+
- QuantumScalper: High-frequency trading bot for arbitrage opportunities
|
| 11 |
+
- MomentumMaster: Detects and trades on price momentum patterns
|
| 12 |
+
- SectorRotator: Automatically rotates positions based on sector performance
|
| 13 |
+
- InterPlanetaryArb: Specializes in cross-planetary price differences
|
| 14 |
+
|
| 15 |
+
ALGORITHM PERFORMANCE METRICS:
|
| 16 |
+
Top-performing algorithms typically achieve:
|
| 17 |
+
- Sharpe ratios of 2.5-3.8
|
| 18 |
+
- Annual returns of 18-35% (with appropriate risk management)
|
| 19 |
+
- Maximum drawdowns of 8-15%
|
| 20 |
+
|
| 21 |
+
RISK MANAGEMENT IN ALGORITHMS:
|
| 22 |
+
All algorithmic trading systems must include:
|
| 23 |
+
- Automatic stop-loss mechanisms
|
| 24 |
+
- Position size limits based on volatility
|
| 25 |
+
- Daily loss limits (typically 2-5% of capital)
|
| 26 |
+
- Emergency shutdown protocols
|
| 27 |
+
|
| 28 |
+
BACKTESTING REQUIREMENTS:
|
| 29 |
+
Before deployment, all trading algorithms must be backtested on at least 5 years of historical data. Results must be verified by independent auditors and approved by the QFA.
|
| 30 |
+
|
| 31 |
+
COMMON ALGORITHM STRATEGIES:
|
| 32 |
+
- Mean reversion: Profits from price movements returning to historical averages
|
| 33 |
+
- Trend following: Identifies and rides market trends
|
| 34 |
+
- Market making: Provides liquidity and profits from bid-ask spreads
|
| 35 |
+
- Statistical arbitrage: Exploits temporary pricing inefficiencies
|
| 36 |
+
|
| 37 |
+
REGULATORY COMPLIANCE:
|
| 38 |
+
Algorithmic trading systems must be registered with the Market Integrity Board. Periodic audits ensure algorithms don't manipulate markets or create unfair advantages.
|
multiple_docs/contact.txt
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
HOW TO ACCESS TRADING MARKETS AND EXCHANGES:
|
| 2 |
+
|
| 3 |
+
PRIMARY EXCHANGE CONTACTS:
|
| 4 |
+
|
| 5 |
+
NEXUS EXCHANGE:
|
| 6 |
+
- Trading Floor: Central Quantum District, Level 47-52
|
| 7 |
+
- Customer Service: +1-800-NEXUS-24 (24/7 automated)
|
| 8 |
+
- Human Representative: Available 08:00-20:00 Standard Time
|
| 9 |
+
- Online Portal: portal.nexus-exchange.qfn
|
| 10 |
+
- Email Support: support@nexus-exchange.qfn
|
| 11 |
+
- For urgent trading issues, contact the Emergency Trading Hotline
|
| 12 |
+
|
| 13 |
+
STELLAR MARKETS:
|
| 14 |
+
- Trading Floor: Orbital Station Alpha, Sector B-12
|
| 15 |
+
- Customer Service: +1-800-STELLAR (24/7)
|
| 16 |
+
- Online Portal: stellar.markets.qfn
|
| 17 |
+
- Email: traders@stellar.markets.qfn
|
| 18 |
+
- Live chat available during market hours
|
| 19 |
+
|
| 20 |
+
AURORA TRADING HUB:
|
| 21 |
+
- Trading Floor: Bio-District 7, Aurora Tower
|
| 22 |
+
- Customer Service: +1-800-AURORA-7
|
| 23 |
+
- Online Portal: aurora.trading.qfn
|
| 24 |
+
- Email: info@aurora.trading.qfn
|
| 25 |
+
- Specialized support for biotech sector trades
|
| 26 |
+
|
| 27 |
+
REGULATORY INQUIRIES:
|
| 28 |
+
- Quantum Financial Authority: regulatory@qfa.qfn
|
| 29 |
+
- Market Integrity Board: compliance@mib.qfn
|
| 30 |
+
- Emergency Market Intervention: +1-888-MARKET-911
|
| 31 |
+
|
| 32 |
+
ACCOUNT SERVICES:
|
| 33 |
+
- New Account Setup: accounts@nexus-exchange.qfn
|
| 34 |
+
- Account Management: available via all exchange portals
|
| 35 |
+
- Trading License Applications: licenses@qfa.qfn
|
| 36 |
+
- Verification typically takes 24-48 hours for standard accounts
|
multiple_docs/cv.txt
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TRADING UNIVERSE OVERVIEW - QUANTUM FINANCIAL NETWORKS
|
| 2 |
+
|
| 3 |
+
MARKET STRUCTURE:
|
| 4 |
+
The Quantum Financial Network operates across three major exchanges:
|
| 5 |
+
- Nexus Exchange (Primary Market) - handles 65% of all trades
|
| 6 |
+
- Stellar Markets (Secondary Market) - specializes in derivatives and futures
|
| 7 |
+
- Aurora Trading Hub (Alternative Market) - focuses on emerging sectors and startups
|
| 8 |
+
|
| 9 |
+
MAJOR TRADING COMPANIES:
|
| 10 |
+
|
| 11 |
+
VERIDIAN CAPITAL GROUP (VCG)
|
| 12 |
+
- Market Cap: 850 billion credits
|
| 13 |
+
- Primary Sector: Energy and Quantum Computing
|
| 14 |
+
- Trading Pairs: VCG/CRD, VCG/USD, VCG/ETH
|
| 15 |
+
- Headquarters: Central Quantum District
|
| 16 |
+
- Founded: 2042
|
| 17 |
+
- Key Products: Quantum processor futures, energy derivatives, algorithmic trading systems
|
| 18 |
+
|
| 19 |
+
STELLAR DYNAMICS CORP (STDY)
|
| 20 |
+
- Market Cap: 720 billion credits
|
| 21 |
+
- Primary Sector: Space Commerce and Interplanetary Logistics
|
| 22 |
+
- Trading Pairs: STDY/CRD, STDY/MARS, STDY/AST
|
| 23 |
+
- Headquarters: Orbital Station Alpha
|
| 24 |
+
- Founded: 2038
|
| 25 |
+
- Key Products: Space freight futures, asteroid mining contracts, lunar real estate securities
|
| 26 |
+
|
| 27 |
+
AURORA BIOSYNTHETICS (AUBIO)
|
| 28 |
+
- Market Cap: 450 billion credits
|
| 29 |
+
- Primary Sector: Biotechnology and Genetic Engineering
|
| 30 |
+
- Trading Pairs: AUBIO/CRD, AUBIO/GENE, AUBIO/BIO
|
| 31 |
+
- Headquarters: Bio-District 7
|
| 32 |
+
- Founded: 2045
|
| 33 |
+
- Key Products: Genetic modification licenses, synthetic organism IPOs, biotech innovation bonds
|
| 34 |
+
|
| 35 |
+
NEXUS LOGISTICS NETWORK (NLN)
|
| 36 |
+
- Market Cap: 680 billion credits
|
| 37 |
+
- Primary Sector: Transportation and Supply Chain
|
| 38 |
+
- Trading Pairs: NLN/CRD, NLN/LOG, NLN/TRANS
|
| 39 |
+
- Headquarters: Transport Hub Gamma
|
| 40 |
+
- Founded: 2035
|
| 41 |
+
- Key Products: Shipping futures, autonomous vehicle fleet shares, supply chain derivatives
|
| 42 |
+
|
| 43 |
+
CYBERNETIC SYSTEMS INNOVATION (CSI)
|
| 44 |
+
- Market Cap: 920 billion credits
|
| 45 |
+
- Primary Sector: AI and Autonomous Systems
|
| 46 |
+
- Trading Pairs: CSI/CRD, CSI/AI, CSI/ROB
|
| 47 |
+
- Headquarters: Digital Innovation District
|
| 48 |
+
- Founded: 2032
|
| 49 |
+
- Key Products: AI training datasets, robot workforce bonds, neural network processing futures
|
| 50 |
+
|
| 51 |
+
TRADING CURRENCIES:
|
| 52 |
+
- CRD (Credit) - Primary currency, backed by quantum computational resources
|
| 53 |
+
- QTN (Quantum Token) - Cryptocurrency tied to quantum processing power
|
| 54 |
+
- MARS - Martian colony currency
|
| 55 |
+
- AST - Asteroid mining token
|
| 56 |
+
- GENE - Biotechnology sector token
|
| 57 |
+
|
| 58 |
+
MARKET HOURS:
|
| 59 |
+
Primary Markets: 24/7 (automated trading)
|
| 60 |
+
Human Traders: 08:00-22:00 Standard Time
|
| 61 |
+
High Volume Periods: 10:00-12:00, 14:00-16:00 (peak trading windows)
|
| 62 |
+
|
| 63 |
+
REGULATORY BODIES:
|
| 64 |
+
- Quantum Financial Authority (QFA) - Primary regulator
|
| 65 |
+
- Interplanetary Trade Commission (ITC) - Cross-planetary oversight
|
| 66 |
+
- Market Integrity Board (MIB) - Prevents manipulation and fraud
|
multiple_docs/education.txt
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
TRADING CERTIFICATIONS AND MARKET QUALIFICATIONS:
|
| 2 |
+
|
| 3 |
+
PROFESSIONAL TRADING LICENSES:
|
| 4 |
+
|
| 5 |
+
LEVEL 1 - RETAIL TRADER CERTIFICATE:
|
| 6 |
+
Basic qualification for individual traders. Covers:
|
| 7 |
+
- Market fundamentals and basic analysis
|
| 8 |
+
- Risk management principles
|
| 9 |
+
- Regulatory compliance requirements
|
| 10 |
+
- Available through online courses (2-week program)
|
| 11 |
+
|
| 12 |
+
LEVEL 2 - PROFESSIONAL TRADER LICENSE:
|
| 13 |
+
Required for managing accounts over 1 million credits. Includes:
|
| 14 |
+
- Advanced technical and fundamental analysis
|
| 15 |
+
- Interplanetary trading regulations
|
| 16 |
+
- Quantum algorithm understanding
|
| 17 |
+
- Ethics and market integrity training
|
| 18 |
+
- 6-month certification program
|
| 19 |
+
|
| 20 |
+
LEVEL 3 - INSTITUTIONAL TRADER CERTIFICATION:
|
| 21 |
+
For trading on behalf of institutions. Covers:
|
| 22 |
+
- Large-scale position management
|
| 23 |
+
- Algorithm development and backtesting
|
| 24 |
+
- Cross-market arbitrage strategies
|
| 25 |
+
- Regulatory reporting and compliance
|
| 26 |
+
- 12-month intensive program with practical exams
|
| 27 |
+
|
| 28 |
+
SPECIALIZED CERTIFICATIONS:
|
| 29 |
+
|
| 30 |
+
QUANTUM FINANCE SPECIALIST:
|
| 31 |
+
Advanced certification focusing on quantum computing applications in trading.
|
| 32 |
+
- Quantum algorithm design
|
| 33 |
+
- Quantum-resistant security protocols
|
| 34 |
+
- High-frequency quantum trading systems
|
| 35 |
+
|
| 36 |
+
INTERPLANETARY TRADE SPECIALIST:
|
| 37 |
+
Specialization in cross-planetary trading and currency arbitrage.
|
| 38 |
+
- Martian and orbital market dynamics
|
| 39 |
+
- Interplanetary tax treaties
|
| 40 |
+
- Logistics-based trading strategies
|
| 41 |
+
|
| 42 |
+
BIOTECH SECTOR ANALYST:
|
| 43 |
+
Focus on biotechnology and life sciences markets.
|
| 44 |
+
- Genetic engineering market fundamentals
|
| 45 |
+
- FDA-equivalent approval processes
|
| 46 |
+
- Synthetic biology investment analysis
|
| 47 |
+
|
| 48 |
+
AI/ROBOTICS MARKET EXPERT:
|
| 49 |
+
Specialization in AI and automation sectors.
|
| 50 |
+
- Machine learning company valuation
|
| 51 |
+
- Robot workforce market dynamics
|
| 52 |
+
- Neural network processing futures
|
| 53 |
+
|
| 54 |
+
CONTINUING EDUCATION REQUIREMENTS:
|
| 55 |
+
All licensed traders must complete 40 hours of continuing education annually, covering:
|
| 56 |
+
- Market regulation updates
|
| 57 |
+
- New trading technologies
|
| 58 |
+
- Risk management best practices
|
| 59 |
+
- Ethical trading standards
|
| 60 |
+
|
| 61 |
+
POPULAR TRAINING PROVIDERS:
|
| 62 |
+
- Quantum Finance Institute (QFI)
|
| 63 |
+
- Nexus Exchange Education Center
|
| 64 |
+
- Interplanetary Trading Academy
|
| 65 |
+
- Market Intelligence University
|
multiple_docs/experience.txt
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
RECENT TRADING MARKET EVENTS AND HISTORY:
|
| 2 |
+
|
| 3 |
+
MAJOR MARKET MOVEMENTS (2048-2049):
|
| 4 |
+
|
| 5 |
+
JANUARY 2048 - STELLAR DYNAMICS EXPANSION:
|
| 6 |
+
Stellar Dynamics Corp announced successful asteroid mining operations, causing STDY stock to surge 47% over 3 weeks. The company secured contracts worth 12 billion credits for rare earth minerals from Asteroid Belt 7.
|
| 7 |
+
|
| 8 |
+
MARCH 2048 - CYBERNETIC SYSTEMS AI BREAKTHROUGH:
|
| 9 |
+
CSI launched their next-generation AI trading algorithm, "QuantumMind Pro", resulting in a 62% increase in their market cap. The algorithm successfully predicted 94% of major market movements in its first quarter, attracting institutional investors.
|
| 10 |
+
|
| 11 |
+
JUNE 2048 - AURORA BIOSYNTHETICS REGULATORY APPROVAL:
|
| 12 |
+
Aurora Biosynthetics received approval from the QFA to begin commercial distribution of their synthetic food products, causing AUBIO shares to jump 35%. The approval opened new markets worth an estimated 200 billion credits.
|
| 13 |
+
|
| 14 |
+
SEPTEMBER 2048 - NEXUS EXCHANGE MERGER TALKS:
|
| 15 |
+
Nexus Logistics Network and Veridian Capital Group announced preliminary merger discussions, causing both stocks to experience high volatility. VCG rose 18% while NLN dropped 12% amid market uncertainty about the proposed structure.
|
| 16 |
+
|
| 17 |
+
DECEMBER 2048 - MARTIAN COLONY CURRENCY LAUNCH:
|
| 18 |
+
The Martian Council officially launched the MARS currency, pegged to water reserves and real estate assets. Initial trading saw massive volatility with values swinging between 0.8-1.4 CRD per MARS in the first week.
|
| 19 |
+
|
| 20 |
+
FEBRUARY 2049 - QUANTUM PROCESSING SHORTAGE:
|
| 21 |
+
A global shortage of quantum processing chips caused all tech-related stocks to plummet. CSI fell 28%, while energy companies like VCG surged 33% as investors sought safe havens in traditional energy sectors.
|
| 22 |
+
|
| 23 |
+
MAY 2049 - STELLAR DYNAMICS LOGISTICS SUCCESS:
|
| 24 |
+
NLN completed their first interplanetary cargo delivery using autonomous spacecraft, reducing shipping costs by 67%. This breakthrough increased NLN market value by 41% and reshaped the logistics sector.
|
| 25 |
+
|
| 26 |
+
AUGUST 2049 - BIOSYNTHETICS MARKET EXPANSION:
|
| 27 |
+
Aurora Biosynthetics announced partnerships with 15 major food corporations, expanding their market reach to three new planetary systems. AUBIO stock experienced the largest single-day gain in history, rising 52%.
|
| 28 |
+
|
| 29 |
+
MARKET VOLATILITY INDICATORS:
|
| 30 |
+
- Average Daily Volume: 45-65 billion credits
|
| 31 |
+
- Peak Trading Volume Day: December 18, 2048 (127 billion credits)
|
| 32 |
+
- Most Volatile Stock: STDY (average daily swing 8.2%)
|
| 33 |
+
- Most Stable Stock: VCG (average daily swing 2.1%)
|
| 34 |
+
|
| 35 |
+
KEY TRADING PATTERNS:
|
| 36 |
+
- Monday openings typically see 12% higher volume
|
| 37 |
+
- End-of-quarter periods show increased algorithmic trading (up to 40% more)
|
| 38 |
+
- Lunar phase cycles historically correlate with 3-5% market movements in energy sectors
|
multiple_docs/various.txt
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
VARIOUS TRADING INFORMATION AND MARKET DETAILS:
|
| 2 |
+
|
| 3 |
+
POPULAR TRADING STRATEGIES:
|
| 4 |
+
|
| 5 |
+
MOMENTUM TRADING:
|
| 6 |
+
Many traders focus on stocks with strong price movements, particularly in the AI and biotech sectors. CSI and AUBIO are popular choices for momentum traders due to frequent news-driven volatility.
|
| 7 |
+
|
| 8 |
+
PAIRS TRADING:
|
| 9 |
+
Common pairs include VCG/CSI (energy vs tech), STDY/NLN (space commerce vs logistics), and AUBIO/CSI (biotech vs AI). These strategies capitalize on sector correlations.
|
| 10 |
+
|
| 11 |
+
ARBITRAGE OPPORTUNITIES:
|
| 12 |
+
Interplanetary price differences create arbitrage opportunities. Mars-based assets often trade at 2-5% premiums compared to Earth prices due to supply constraints.
|
| 13 |
+
|
| 14 |
+
QUANTUM ALGORITHM TRADING:
|
| 15 |
+
Many institutional investors use proprietary quantum algorithms that can analyze multiple market dimensions simultaneously, giving them advantages in high-frequency trading.
|
| 16 |
+
|
| 17 |
+
RISK MANAGEMENT:
|
| 18 |
+
- Stop-loss orders are automatically executed by AI systems
|
| 19 |
+
- Maximum position sizes are regulated by QFA based on account type
|
| 20 |
+
- Margin requirements vary from 10-50% depending on security volatility
|
| 21 |
+
|
| 22 |
+
TAX IMPLICATIONS:
|
| 23 |
+
- Capital gains taxed at 15-25% depending on holding period
|
| 24 |
+
- Interplanetary trades may have additional tax treaties
|
| 25 |
+
- Loss harvesting strategies are popular year-end activities
|
| 26 |
+
|
| 27 |
+
MARKET SENTIMENT INDICATORS:
|
| 28 |
+
- VCG Index: Tracks energy sector confidence (currently bullish)
|
| 29 |
+
- Tech Momentum Gauge: Measures AI/automation sector strength
|
| 30 |
+
- Interplanetary Trade Index: Overall cross-planetary commerce health
|
| 31 |
+
|
| 32 |
+
COMMON TRADING MISTAKES:
|
| 33 |
+
- Overtrading during low-liquidity periods
|
| 34 |
+
- Ignoring interplanetary time zones
|
| 35 |
+
- Failing to account for quantum processing delays during high-volume periods
|
| 36 |
+
- Not using appropriate risk management tools
|
| 37 |
+
|
| 38 |
+
INVESTMENT PRODUCTS:
|
| 39 |
+
- Stocks: Direct company ownership (most popular)
|
| 40 |
+
- Futures: Contracts for future delivery of quantum resources
|
| 41 |
+
- Options: Rights to buy/sell at specific prices
|
| 42 |
+
- Bonds: Corporate debt instruments with fixed returns
|
| 43 |
+
- ETFs: Diversified sector-based investment funds
|
pyproject.toml
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
[project]
|
| 2 |
+
name = "trading-game-ai"
|
| 3 |
+
version = "0.1.0"
|
| 4 |
+
description = "Trading game with AI assistant for psychological experiments"
|
| 5 |
+
requires-python = ">=3.9"
|
| 6 |
+
dependencies = [
|
| 7 |
+
"gradio>=4.0.0",
|
| 8 |
+
"langchain>=0.1.0",
|
| 9 |
+
"langchain-community>=0.0.20",
|
| 10 |
+
"langchain-core>=0.1.0",
|
| 11 |
+
"chromadb>=0.4.0",
|
| 12 |
+
"sentence-transformers>=2.2.0",
|
| 13 |
+
"requests>=2.31.0",
|
| 14 |
+
"python-dotenv>=1.0.0",
|
| 15 |
+
"langchain-huggingface>=0.3.1",
|
| 16 |
+
"transformers>=4.57.6",
|
| 17 |
+
"torch>=2.8.0",
|
| 18 |
+
"accelerate>=1.10.1",
|
| 19 |
+
"huggingface-hub>=0.36.0",
|
| 20 |
+
"langchain-openai>=0.3.35",
|
| 21 |
+
"openai>=2.15.0",
|
| 22 |
+
"fastapi>=0.128.0",
|
| 23 |
+
"uvicorn>=0.39.0",
|
| 24 |
+
"python-multipart>=0.0.20",
|
| 25 |
+
]
|
| 26 |
+
|
| 27 |
+
[project.optional-dependencies]
|
| 28 |
+
hf-spaces = [
|
| 29 |
+
"pysqlite3-binary>=0.5.0",
|
| 30 |
+
]
|
| 31 |
+
|
| 32 |
+
[tool.uv]
|
| 33 |
+
dev-dependencies = []
|