init update

This commit is contained in:
tianyufan
2025-10-24 00:35:21 +08:00
commit df5c25c98d
205 changed files with 81998 additions and 0 deletions

234
agent_tools/start_mcp_services.py Executable file
View File

@@ -0,0 +1,234 @@
#!/usr/bin/env python3
"""
MCP Service Startup Script (Python Version)
Start all four MCP services: Math, Search, TradeTools, LocalPrices
"""
import os
import sys
import time
import signal
import subprocess
import threading
from pathlib import Path
class MCPServiceManager:
def __init__(self):
self.services = {}
self.running = True
# Set default ports
self.ports = {
'math': int(os.getenv('MATH_HTTP_PORT', '8000')),
'search': int(os.getenv('SEARCH_HTTP_PORT', '8001')),
'trade': int(os.getenv('TRADE_HTTP_PORT', '8002')),
'price': int(os.getenv('GETPRICE_HTTP_PORT', '8003'))
}
# Service configurations
self.service_configs = {
'math': {
'script': 'tool_math.py',
'name': 'Math',
'port': self.ports['math']
},
'search': {
'script': 'tool_jina_search.py',
'name': 'Search',
'port': self.ports['search']
},
'trade': {
'script': 'tool_trade.py',
'name': 'TradeTools',
'port': self.ports['trade']
},
'price': {
'script': 'tool_get_price_local.py',
'name': 'LocalPrices',
'port': self.ports['price']
}
}
# Create logs directory
self.log_dir = Path('../logs')
self.log_dir.mkdir(exist_ok=True)
# Set signal handlers
signal.signal(signal.SIGINT, self.signal_handler)
signal.signal(signal.SIGTERM, self.signal_handler)
def signal_handler(self, signum, frame):
"""Handle interrupt signals"""
print("\n🛑 Received stop signal, shutting down all services...")
self.stop_all_services()
sys.exit(0)
def start_service(self, service_id, config):
"""Start a single service"""
script_path = config['script']
service_name = config['name']
port = config['port']
if not Path(script_path).exists():
print(f"❌ Script file not found: {script_path}")
return False
try:
# Start service process
log_file = self.log_dir / f"{service_id}.log"
with open(log_file, 'w') as f:
process = subprocess.Popen(
[sys.executable, script_path],
stdout=f,
stderr=subprocess.STDOUT,
cwd=os.getcwd()
)
self.services[service_id] = {
'process': process,
'name': service_name,
'port': port,
'log_file': log_file
}
print(f"{service_name} service started (PID: {process.pid}, Port: {port})")
return True
except Exception as e:
print(f"❌ Failed to start {service_name} service: {e}")
return False
def check_service_health(self, service_id):
"""Check service health status"""
if service_id not in self.services:
return False
service = self.services[service_id]
process = service['process']
port = service['port']
# Check if process is still running
if process.poll() is not None:
return False
# Check if port is responding (simple check)
try:
import socket
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(1)
result = sock.connect_ex(('localhost', port))
sock.close()
return result == 0
except:
return False
def start_all_services(self):
"""Start all services"""
print("🚀 Starting MCP services...")
print("=" * 50)
print(f"📊 Port configuration:")
for service_id, config in self.service_configs.items():
print(f" - {config['name']}: {config['port']}")
print("\n🔄 Starting services...")
# Start all services
for service_id, config in self.service_configs.items():
self.start_service(service_id, config)
# Wait for services to start
print("\n⏳ Waiting for services to start...")
time.sleep(3)
# Check service status
print("\n🔍 Checking service status...")
self.check_all_services()
print("\n🎉 All MCP services started!")
self.print_service_info()
# Keep running
self.keep_alive()
def check_all_services(self):
"""Check all service status"""
for service_id, service in self.services.items():
if self.check_service_health(service_id):
print(f"{service['name']} service running normally")
else:
print(f"{service['name']} service failed to start")
print(f" Please check logs: {service['log_file']}")
def print_service_info(self):
"""Print service information"""
print("\n📋 Service information:")
for service_id, service in self.services.items():
print(f" - {service['name']}: http://localhost:{service['port']} (PID: {service['process'].pid})")
print(f"\n📁 Log files location: {self.log_dir.absolute()}")
print("\n🛑 Press Ctrl+C to stop all services")
def keep_alive(self):
"""Keep services running"""
try:
while self.running:
time.sleep(1)
# Check service status
for service_id, service in self.services.items():
if service['process'].poll() is not None:
print(f"\n⚠️ {service['name']} service stopped unexpectedly")
self.running = False
break
except KeyboardInterrupt:
pass
finally:
self.stop_all_services()
def stop_all_services(self):
"""Stop all services"""
print("\n🛑 Stopping all services...")
for service_id, service in self.services.items():
try:
service['process'].terminate()
service['process'].wait(timeout=5)
print(f"{service['name']} service stopped")
except subprocess.TimeoutExpired:
service['process'].kill()
print(f"🔨 {service['name']} service force stopped")
except Exception as e:
print(f"❌ Error stopping {service['name']} service: {e}")
print("✅ All services stopped")
def status(self):
"""Display service status"""
print("📊 MCP Service Status Check")
print("=" * 30)
for service_id, config in self.service_configs.items():
if service_id in self.services:
service = self.services[service_id]
if self.check_service_health(service_id):
print(f"{config['name']} service running normally (Port: {config['port']})")
else:
print(f"{config['name']} service abnormal (Port: {config['port']})")
else:
print(f"{config['name']} service not started (Port: {config['port']})")
def main():
"""Main function"""
if len(sys.argv) > 1 and sys.argv[1] == 'status':
# Status check mode
manager = MCPServiceManager()
manager.status()
else:
# Startup mode
manager = MCPServiceManager()
manager.start_all_services()
if __name__ == "__main__":
main()

View File

@@ -0,0 +1,135 @@
from pathlib import Path
import json
from datetime import datetime
from typing import Dict, Any
from fastmcp import FastMCP
import os
from dotenv import load_dotenv
load_dotenv()
mcp = FastMCP("LocalPrices")
def _workspace_data_path(filename: str) -> Path:
base_dir = Path(__file__).resolve().parents[1]
return base_dir / "data" / filename
def _validate_date(date_str: str) -> None:
try:
datetime.strptime(date_str, "%Y-%m-%d")
except ValueError as exc:
raise ValueError("date must be in YYYY-MM-DD format") from exc
@mcp.tool()
def get_price_local(symbol: str, date: str) -> Dict[str, Any]:
"""Read OHLCV data for specified stock and date. Get historical information for specified stock.
Args:
symbol: Stock symbol, e.g. 'IBM' or '600243.SHH'.
date: Date in 'YYYY-MM-DD' format.
Returns:
Dictionary containing symbol, date and ohlcv data.
"""
filename = "merged.jsonl"
try:
_validate_date(date)
except ValueError as e:
return {"error": str(e), "symbol": symbol, "date": date}
data_path = _workspace_data_path(filename)
if not data_path.exists():
return {"error": f"Data file not found: {data_path}", "symbol": symbol, "date": date}
with data_path.open("r", encoding="utf-8") as f:
for line in f:
if not line.strip():
continue
doc = json.loads(line)
meta = doc.get("Meta Data", {})
if meta.get("2. Symbol") != symbol:
continue
series = doc.get("Time Series (Daily)", {})
day = series.get(date)
if day is None:
sample_dates = sorted(series.keys(), reverse=True)[:5]
return {
"error": f"Data not found for date {date}. Please verify the date exists in data. Sample available dates: {sample_dates}",
"symbol": symbol,
"date": date
}
return {
"symbol": symbol,
"date": date,
"ohlcv": {
"open": day.get("1. buy price"),
"high": day.get("2. high"),
"low": day.get("3. low"),
"close": day.get("4. sell price"),
"volume": day.get("5. volume"),
},
}
return {"error": f"No records found for stock {symbol} in local data", "symbol": symbol, "date": date}
def get_price_local_function(symbol: str, date: str, filename: str = "merged.jsonl") -> Dict[str, Any]:
"""Read OHLCV data for specified stock and date from local JSONL data.
Args:
symbol: Stock symbol, e.g. 'IBM' or '600243.SHH'.
date: Date in 'YYYY-MM-DD' format.
filename: Data filename, defaults to 'merged.jsonl' (located in data/ under project root).
Returns:
Dictionary containing symbol, date and ohlcv data.
"""
try:
_validate_date(date)
except ValueError as e:
return {"error": str(e), "symbol": symbol, "date": date}
data_path = _workspace_data_path(filename)
if not data_path.exists():
return {"error": f"Data file not found: {data_path}", "symbol": symbol, "date": date}
with data_path.open("r", encoding="utf-8") as f:
for line in f:
if not line.strip():
continue
doc = json.loads(line)
meta = doc.get("Meta Data", {})
if meta.get("2. Symbol") != symbol:
continue
series = doc.get("Time Series (Daily)", {})
day = series.get(date)
if day is None:
sample_dates = sorted(series.keys(), reverse=True)[:5]
return {
"error": f"Data not found for date {date}. Please verify the date exists in data. Sample available dates: {sample_dates}",
"symbol": symbol,
"date": date
}
return {
"symbol": symbol,
"date": date,
"ohlcv": {
"buy price": day.get("1. buy price"),
"high": day.get("2. high"),
"low": day.get("3. low"),
"sell price": day.get("4. sell price"),
"volume": day.get("5. volume"),
},
}
return {"error": f"No records found for stock {symbol} in local data", "symbol": symbol, "date": date}
if __name__ == "__main__":
# print("a test case")
# print(get_price_local_function("AAPL", "2025-10-16"))
port = int(os.getenv("GETPRICE_HTTP_PORT", "8003"))
mcp.run(transport="streamable-http", port=port)

View File

@@ -0,0 +1,271 @@
from typing import Dict, Any, Optional, List
import os
import logging
import requests
import requests
from fastmcp import FastMCP
from dotenv import load_dotenv
load_dotenv()
import random
from datetime import datetime, timedelta
import re
import json
import sys
import os
sys.path.append(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))
from tools.general_tools import get_config_value
logger = logging.getLogger(__name__)
def parse_date_to_standard(date_str: str) -> str:
"""
Convert various date formats to standard format (YYYY-MM-DD HH:MM:SS)
Args:
date_str: Date string in various formats, such as "2025-10-01T08:19:28+00:00", "4 hours ago", "1 day ago", "May 31, 2025"
Returns:
Standard format datetime string, such as "2025-10-01 08:19:28"
"""
if not date_str or date_str == 'unknown':
return 'unknown'
# Handle relative time formats
if 'ago' in date_str.lower():
try:
now = datetime.now()
if 'hour' in date_str.lower():
hours = int(re.findall(r'\d+', date_str)[0])
target_date = now - timedelta(hours=hours)
elif 'day' in date_str.lower():
days = int(re.findall(r'\d+', date_str)[0])
target_date = now - timedelta(days=days)
elif 'week' in date_str.lower():
weeks = int(re.findall(r'\d+', date_str)[0])
target_date = now - timedelta(weeks=weeks)
elif 'month' in date_str.lower():
months = int(re.findall(r'\d+', date_str)[0])
target_date = now - timedelta(days=months * 30) # Approximate handling
else:
return 'unknown'
return target_date.strftime('%Y-%m-%d %H:%M:%S')
except Exception:
pass
# Handle ISO 8601 format, such as "2025-10-01T08:19:28+00:00"
try:
if 'T' in date_str and ('+' in date_str or 'Z' in date_str or date_str.endswith('00:00')):
# Remove timezone information, keep only date and time part
if '+' in date_str:
date_part = date_str.split('+')[0]
elif 'Z' in date_str:
date_part = date_str.replace('Z', '')
else:
date_part = date_str
# Parse ISO format
if '.' in date_part:
# Handle microseconds part, such as "2025-10-01T08:19:28.123456"
parsed_date = datetime.strptime(date_part.split('.')[0], '%Y-%m-%dT%H:%M:%S')
else:
# Standard ISO format "2025-10-01T08:19:28"
parsed_date = datetime.strptime(date_part, '%Y-%m-%dT%H:%M:%S')
return parsed_date.strftime('%Y-%m-%d %H:%M:%S')
except Exception:
pass
# Handle other common formats
try:
# Handle "May 31, 2025" format
if ',' in date_str and len(date_str.split()) >= 3:
parsed_date = datetime.strptime(date_str, '%b %d, %Y')
return parsed_date.strftime('%Y-%m-%d %H:%M:%S')
except Exception:
pass
try:
# Handle "2025-10-01" format
if re.match(r'\d{4}-\d{2}-\d{2}$', date_str):
parsed_date = datetime.strptime(date_str, '%Y-%m-%d')
return parsed_date.strftime('%Y-%m-%d %H:%M:%S')
except Exception:
pass
# If unable to parse, return original string
return date_str
class WebScrapingJinaTool:
def __init__(self):
self.api_key = os.environ.get("JINA_API_KEY")
if not self.api_key:
raise ValueError("Jina API key not provided! Please set JINA_API_KEY environment variable.")
def __call__(self, query: str) -> List[Dict[str, Any]]:
print(f"Searching for {query}")
all_urls = self._jina_search(query)
return_content = []
print(f"Found {len(all_urls)} URLs")
if len(all_urls)>1:
# Randomly select three to form new all_urls
all_urls = random.sample(all_urls, 1)
for url in all_urls:
print(f"Scraping {url}")
return_content.append(self._jina_scrape(url))
print(f"Scraped {url}")
return return_content
def _jina_scrape(self, url: str) -> Dict[str, Any]:
try:
jina_url = f'https://r.jina.ai/{url}'
headers = {
"Accept": "application/json",
'Authorization': self.api_key,
'X-Timeout': "10",
"X-With-Generated-Alt": "true",
}
response = requests.get(jina_url, headers=headers)
if response.status_code != 200:
raise Exception(f"Jina AI Reader Failed for {url}: {response.status_code}")
response_dict = response.json()
return {
'url': response_dict['data']['url'],
'title': response_dict['data']['title'],
'description': response_dict['data']['description'],
'content': response_dict['data']['content'],
'publish_time': response_dict['data'].get('publishedTime', 'unknown')
}
except Exception as e:
logger.error(str(e))
return {
'url': url,
'content': '',
'error': str(e)
}
def _jina_search(self, query: str) -> List[str]:
url = f'https://s.jina.ai/?q={query}&n=1'
headers = {
'Authorization': f'Bearer {self.api_key}',
"Accept": "application/json",
"X-Respond-With": "no-content"
}
try:
response = requests.get(url, headers=headers)
response.raise_for_status() # 检查HTTP状态码
json_data = response.json()
# Check if response data is valid
if json_data is None:
print(f"⚠️ Jina API returned empty data, query: {query}")
return []
if 'data' not in json_data:
print(f"⚠️ Jina API response format abnormal, query: {query}, response: {json_data}")
return []
all_urls = []
filtered_urls = []
# Process search results, filter out content from TODAY_DATE and later
for item in json_data.get('data', []):
if 'url' not in item:
continue
# Get publication date and convert to standard format
raw_date = item.get('date', 'unknown')
standardized_date = parse_date_to_standard(raw_date)
# If unable to parse date, keep this result
if standardized_date == 'unknown' or standardized_date == raw_date:
filtered_urls.append(item['url'])
continue
# Check if before TODAY_DATE
today_date = get_config_value("TODAY_DATE")
if today_date:
if today_date > standardized_date:
filtered_urls.append(item['url'])
else:
# If TODAY_DATE is not set, keep all results
filtered_urls.append(item['url'])
print(f"Found {len(filtered_urls)} URLs after filtering")
return filtered_urls
except requests.exceptions.RequestException as e:
print(f"❌ Jina API request failed: {e}")
return []
except ValueError as e:
print(f"❌ Jina API response parsing failed: {e}")
return []
except Exception as e:
print(f"❌ Jina search unknown error: {e}")
return []
mcp = FastMCP("Search")
@mcp.tool()
def get_information(query: str) -> str:
"""
Use search tool to scrape and return main content information related to specified query in a structured way.
Args:
query: Key information or search terms you want to retrieve, will search for the most matching results on the internet.
Returns:
A string containing several retrieved web page contents, structured content includes:
- URL: Original web page link
- Title: Web page title
- Description: Brief description of the web page
- Publish Time: Content publication date (if available)
- Content: Main text content of the web page (first 1000 characters)
If scraping fails, returns corresponding error information.
"""
try:
tool = WebScrapingJinaTool()
results = tool(query)
# Check if results are empty
if not results:
return f"⚠️ Search query '{query}' found no results. May be network issue or API limitation."
# Convert results to string format
formatted_results = []
for result in results:
if 'error' in result:
formatted_results.append(f"Error: {result['error']}")
else:
formatted_results.append(f"""
URL: {result['url']}
Title: {result['title']}
Description: {result['description']}
Publish Time: {result['publish_time']}
Content: {result['content'][:1000]}...
""")
if not formatted_results:
return f"⚠️ Search query '{query}' returned empty results."
return "\n".join(formatted_results)
except Exception as e:
return f"❌ Search tool execution failed: {str(e)}"
if __name__ == "__main__":
# Run with streamable-http, support configuring host and port through environment variables to avoid conflicts
port = int(os.getenv("SEARCH_HTTP_PORT", "8001"))
mcp.run(transport="streamable-http", port=port)

20
agent_tools/tool_math.py Normal file
View File

@@ -0,0 +1,20 @@
from fastmcp import FastMCP
import os
from dotenv import load_dotenv
load_dotenv()
mcp = FastMCP("Math")
@mcp.tool()
def add(a: float, b: float) -> float:
"""Add two numbers (supports int and float)"""
return float(a) + float(b)
@mcp.tool()
def multiply(a: float, b: float) -> float:
"""Multiply two numbers (supports int and float)"""
return float(a) * float(b)
if __name__ == "__main__":
port = int(os.getenv("MATH_HTTP_PORT", "8000"))
mcp.run(transport="streamable-http", port=port)

197
agent_tools/tool_trade.py Normal file
View File

@@ -0,0 +1,197 @@
from fastmcp import FastMCP
import sys
import os
from typing import Dict, List, Optional, Any
# Add project root directory to Python path
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
sys.path.insert(0, project_root)
from tools.price_tools import get_yesterday_date, get_open_prices, get_yesterday_open_and_close_price, get_latest_position, get_yesterday_profit
import json
from tools.general_tools import get_config_value,write_config_value
mcp = FastMCP("TradeTools")
@mcp.tool()
def buy(symbol: str, amount: int) -> Dict[str, Any]:
"""
Buy stock function
This function simulates stock buying operations, including the following steps:
1. Get current position and operation ID
2. Get stock opening price for the day
3. Validate buy conditions (sufficient cash)
4. Update position (increase stock quantity, decrease cash)
5. Record transaction to position.jsonl file
Args:
symbol: Stock symbol, such as "AAPL", "MSFT", etc.
amount: Buy quantity, must be a positive integer, indicating how many shares to buy
Returns:
Dict[str, Any]:
- Success: Returns new position dictionary (containing stock quantity and cash balance)
- Failure: Returns {"error": error message, ...} dictionary
Raises:
ValueError: Raised when SIGNATURE environment variable is not set
Example:
>>> result = buy("AAPL", 10)
>>> print(result) # {"AAPL": 110, "MSFT": 5, "CASH": 5000.0, ...}
"""
# Step 1: Get environment variables and basic information
# Get signature (model name) from environment variable, used to determine data storage path
signature = get_config_value("SIGNATURE")
if signature is None:
raise ValueError("SIGNATURE environment variable is not set")
# Get current trading date from environment variable
today_date = get_config_value("TODAY_DATE")
# Step 2: Get current latest position and operation ID
# get_latest_position returns two values: position dictionary and current maximum operation ID
# This ID is used to ensure each operation has a unique identifier
try:
current_position, current_action_id = get_latest_position(today_date, signature)
except Exception as e:
print(e)
print(current_position, current_action_id)
print(today_date, signature)
# Step 3: Get stock opening price for the day
# Use get_open_prices function to get the opening price of specified stock for the day
# If stock symbol does not exist or price data is missing, KeyError exception will be raised
try:
this_symbol_price = get_open_prices(today_date, [symbol])[f'{symbol}_price']
except KeyError:
# Stock symbol does not exist or price data is missing, return error message
return {"error": f"Symbol {symbol} not found! This action will not be allowed.", "symbol": symbol, "date": today_date}
# Step 4: Validate buy conditions
# Calculate cash required for purchase: stock price × buy quantity
try:
cash_left = current_position["CASH"] - this_symbol_price * amount
except Exception as e:
print(current_position, "CASH", this_symbol_price, amount)
# Check if cash balance is sufficient for purchase
if cash_left < 0:
# Insufficient cash, return error message
return {"error": "Insufficient cash! This action will not be allowed.", "required_cash": this_symbol_price * amount, "cash_available": current_position.get("CASH", 0), "symbol": symbol, "date": today_date}
else:
# Step 5: Execute buy operation, update position
# Create a copy of current position to avoid directly modifying original data
new_position = current_position.copy()
# Decrease cash balance
new_position["CASH"] = cash_left
# Increase stock position quantity
new_position[symbol] += amount
# Step 6: Record transaction to position.jsonl file
# Build file path: {project_root}/data/agent_data/{signature}/position/position.jsonl
# Use append mode ("a") to write new transaction record
# Each operation ID increments by 1, ensuring uniqueness of operation sequence
position_file_path = os.path.join(project_root, "data", "agent_data", signature, "position", "position.jsonl")
with open(position_file_path, "a") as f:
# Write JSON format transaction record, containing date, operation ID, transaction details and updated position
print(f"Writing to position.jsonl: {json.dumps({'date': today_date, 'id': current_action_id + 1, 'this_action':{'action':'buy','symbol':symbol,'amount':amount},'positions': new_position})}")
f.write(json.dumps({"date": today_date, "id": current_action_id + 1, "this_action":{"action":"buy","symbol":symbol,"amount":amount},"positions": new_position}) + "\n")
# Step 7: Return updated position
write_config_value("IF_TRADE", True)
print("IF_TRADE", get_config_value("IF_TRADE"))
return new_position
@mcp.tool()
def sell(symbol: str, amount: int) -> Dict[str, Any]:
"""
Sell stock function
This function simulates stock selling operations, including the following steps:
1. Get current position and operation ID
2. Get stock opening price for the day
3. Validate sell conditions (position exists, sufficient quantity)
4. Update position (decrease stock quantity, increase cash)
5. Record transaction to position.jsonl file
Args:
symbol: Stock symbol, such as "AAPL", "MSFT", etc.
amount: Sell quantity, must be a positive integer, indicating how many shares to sell
Returns:
Dict[str, Any]:
- Success: Returns new position dictionary (containing stock quantity and cash balance)
- Failure: Returns {"error": error message, ...} dictionary
Raises:
ValueError: Raised when SIGNATURE environment variable is not set
Example:
>>> result = sell("AAPL", 10)
>>> print(result) # {"AAPL": 90, "MSFT": 5, "CASH": 15000.0, ...}
"""
# Step 1: Get environment variables and basic information
# Get signature (model name) from environment variable, used to determine data storage path
signature = get_config_value("SIGNATURE")
if signature is None:
raise ValueError("SIGNATURE environment variable is not set")
# Get current trading date from environment variable
today_date = get_config_value("TODAY_DATE")
# Step 2: Get current latest position and operation ID
# get_latest_position returns two values: position dictionary and current maximum operation ID
# This ID is used to ensure each operation has a unique identifier
current_position, current_action_id = get_latest_position(today_date, signature)
# Step 3: Get stock opening price for the day
# Use get_open_prices function to get the opening price of specified stock for the day
# If stock symbol does not exist or price data is missing, KeyError exception will be raised
try:
this_symbol_price = get_open_prices(today_date, [symbol])[f'{symbol}_price']
except KeyError:
# Stock symbol does not exist or price data is missing, return error message
return {"error": f"Symbol {symbol} not found! This action will not be allowed.", "symbol": symbol, "date": today_date}
# Step 4: Validate sell conditions
# Check if holding this stock
if symbol not in current_position:
return {"error": f"No position for {symbol}! This action will not be allowed.", "symbol": symbol, "date": today_date}
# Check if position quantity is sufficient for selling
if current_position[symbol] < amount:
return {"error": "Insufficient shares! This action will not be allowed.", "have": current_position.get(symbol, 0), "want_to_sell": amount, "symbol": symbol, "date": today_date}
# Step 5: Execute sell operation, update position
# Create a copy of current position to avoid directly modifying original data
new_position = current_position.copy()
# Decrease stock position quantity
new_position[symbol] -= amount
# Increase cash balance: sell price × sell quantity
# Use get method to ensure CASH field exists, default to 0 if not present
new_position["CASH"] = new_position.get("CASH", 0) + this_symbol_price * amount
# Step 6: Record transaction to position.jsonl file
# Build file path: {project_root}/data/agent_data/{signature}/position/position.jsonl
# Use append mode ("a") to write new transaction record
# Each operation ID increments by 1, ensuring uniqueness of operation sequence
position_file_path = os.path.join(project_root, "data", "agent_data", signature, "position", "position.jsonl")
with open(position_file_path, "a") as f:
# Write JSON format transaction record, containing date, operation ID and updated position
print(f"Writing to position.jsonl: {json.dumps({'date': today_date, 'id': current_action_id + 1, 'this_action':{'action':'sell','symbol':symbol,'amount':amount},'positions': new_position})}")
f.write(json.dumps({"date": today_date, "id": current_action_id + 1, "this_action":{"action":"sell","symbol":symbol,"amount":amount},"positions": new_position}) + "\n")
# Step 7: Return updated position
write_config_value("IF_TRADE", True)
return new_position
if __name__ == "__main__":
# new_result = buy("AAPL", 1)
# print(new_result)
# new_result = sell("AAPL", 1)
# print(new_result)
port = int(os.getenv("TRADE_HTTP_PORT", "8002"))
mcp.run(transport="streamable-http", port=port)