mirror of
https://github.com/Xe138/AI-Trader.git
synced 2026-04-05 10:17:24 -04:00
init update
This commit is contained in:
390
docs/assets/js/data-loader.js
Normal file
390
docs/assets/js/data-loader.js
Normal file
@@ -0,0 +1,390 @@
|
||||
// Data Loader Utility
|
||||
// Handles loading and processing all trading data
|
||||
|
||||
class DataLoader {
|
||||
constructor() {
|
||||
this.agentData = {};
|
||||
this.priceCache = {};
|
||||
// Use 'data' for GitHub Pages deployment, '../data' for local development
|
||||
this.baseDataPath = './data';
|
||||
}
|
||||
|
||||
// Load all agent names from directory structure
|
||||
async loadAgentList() {
|
||||
try {
|
||||
// Since we can't directly list directories in the browser,
|
||||
// we'll try to load known agents based on common patterns
|
||||
const potentialAgents = [
|
||||
'gemini-2.5-flash',
|
||||
'qwen3-max',
|
||||
'deepseek-chat-v3.1',
|
||||
'gpt-5',
|
||||
'claude-3.7-sonnet',
|
||||
];
|
||||
|
||||
const agents = [];
|
||||
for (const agent of potentialAgents) {
|
||||
try {
|
||||
console.log(`Checking agent: ${agent}`);
|
||||
const response = await fetch(`${this.baseDataPath}/agent_data/${agent}/position/position.jsonl`);
|
||||
if (response.ok) {
|
||||
agents.push(agent);
|
||||
console.log(`Added agent: ${agent}`);
|
||||
} else {
|
||||
console.log(`Agent ${agent} not found (status: ${response.status})`);
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`Agent ${agent} error:`, e.message);
|
||||
}
|
||||
}
|
||||
|
||||
return agents;
|
||||
} catch (error) {
|
||||
console.error('Error loading agent list:', error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Load position data for a specific agent
|
||||
async loadAgentPositions(agentName) {
|
||||
try {
|
||||
const response = await fetch(`${this.baseDataPath}/agent_data/${agentName}/position/position.jsonl`);
|
||||
if (!response.ok) throw new Error(`Failed to load positions for ${agentName}`);
|
||||
|
||||
const text = await response.text();
|
||||
const lines = text.trim().split('\n').filter(line => line.trim() !== '');
|
||||
const positions = lines.map(line => {
|
||||
try {
|
||||
return JSON.parse(line);
|
||||
} catch (parseError) {
|
||||
console.error(`Error parsing line for ${agentName}:`, line, parseError);
|
||||
return null;
|
||||
}
|
||||
}).filter(pos => pos !== null);
|
||||
|
||||
console.log(`Loaded ${positions.length} positions for ${agentName}`);
|
||||
return positions;
|
||||
} catch (error) {
|
||||
console.error(`Error loading positions for ${agentName}:`, error);
|
||||
return [];
|
||||
}
|
||||
}
|
||||
|
||||
// Load price data for a specific stock symbol
|
||||
async loadStockPrice(symbol) {
|
||||
if (this.priceCache[symbol]) {
|
||||
return this.priceCache[symbol];
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`${this.baseDataPath}/daily_prices_${symbol}.json`);
|
||||
if (!response.ok) throw new Error(`Failed to load price for ${symbol}`);
|
||||
|
||||
const data = await response.json();
|
||||
this.priceCache[symbol] = data['Time Series (Daily)'];
|
||||
return this.priceCache[symbol];
|
||||
} catch (error) {
|
||||
console.error(`Error loading price for ${symbol}:`, error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Get closing price for a symbol on a specific date
|
||||
async getClosingPrice(symbol, date) {
|
||||
const prices = await this.loadStockPrice(symbol);
|
||||
if (!prices || !prices[date]) {
|
||||
return null;
|
||||
}
|
||||
return parseFloat(prices[date]['4. close']);
|
||||
}
|
||||
|
||||
// Calculate total asset value for a position on a given date
|
||||
async calculateAssetValue(position, date) {
|
||||
let totalValue = position.positions.CASH || 0;
|
||||
|
||||
// Get all stock symbols (exclude CASH)
|
||||
const symbols = Object.keys(position.positions).filter(s => s !== 'CASH');
|
||||
|
||||
for (const symbol of symbols) {
|
||||
const shares = position.positions[symbol];
|
||||
if (shares > 0) {
|
||||
const price = await this.getClosingPrice(symbol, date);
|
||||
if (price) {
|
||||
totalValue += shares * price;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return totalValue;
|
||||
}
|
||||
|
||||
// Load complete data for an agent including asset values over time
|
||||
async loadAgentData(agentName) {
|
||||
console.log(`Starting to load data for ${agentName}...`);
|
||||
const positions = await this.loadAgentPositions(agentName);
|
||||
if (positions.length === 0) {
|
||||
console.log(`No positions found for ${agentName}`);
|
||||
return null;
|
||||
}
|
||||
|
||||
console.log(`Processing ${positions.length} positions for ${agentName}...`);
|
||||
|
||||
// Group positions by date and take only the last position for each date
|
||||
const positionsByDate = {};
|
||||
positions.forEach(position => {
|
||||
const date = position.date;
|
||||
if (!positionsByDate[date] || position.id > positionsByDate[date].id) {
|
||||
positionsByDate[date] = position;
|
||||
}
|
||||
});
|
||||
|
||||
// Convert to array and sort by date
|
||||
const uniquePositions = Object.values(positionsByDate).sort((a, b) => {
|
||||
if (a.date !== b.date) {
|
||||
return a.date.localeCompare(b.date);
|
||||
}
|
||||
return a.id - b.id;
|
||||
});
|
||||
|
||||
console.log(`Reduced from ${positions.length} to ${uniquePositions.length} unique daily positions for ${agentName}`);
|
||||
|
||||
const assetHistory = [];
|
||||
|
||||
for (const position of uniquePositions) {
|
||||
const date = position.date;
|
||||
const assetValue = await this.calculateAssetValue(position, date);
|
||||
assetHistory.push({
|
||||
date: date,
|
||||
value: assetValue,
|
||||
id: position.id,
|
||||
action: position.this_action || null
|
||||
});
|
||||
}
|
||||
|
||||
const result = {
|
||||
name: agentName,
|
||||
positions: positions,
|
||||
assetHistory: assetHistory,
|
||||
initialValue: assetHistory[0]?.value || 10000,
|
||||
currentValue: assetHistory[assetHistory.length - 1]?.value || 0,
|
||||
return: assetHistory.length > 0 ?
|
||||
((assetHistory[assetHistory.length - 1].value - assetHistory[0].value) / assetHistory[0].value * 100) : 0
|
||||
};
|
||||
|
||||
console.log(`Successfully loaded data for ${agentName}:`, {
|
||||
positions: positions.length,
|
||||
assetHistory: assetHistory.length,
|
||||
initialValue: result.initialValue,
|
||||
currentValue: result.currentValue,
|
||||
return: result.return
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
// Load QQQ invesco data
|
||||
async loadQQQData() {
|
||||
try {
|
||||
console.log('Loading QQQ invesco data...');
|
||||
const response = await fetch(`${this.baseDataPath}/Adaily_prices_QQQ.json`);
|
||||
if (!response.ok) throw new Error('Failed to load QQQ data');
|
||||
|
||||
const data = await response.json();
|
||||
const timeSeries = data['Time Series (Daily)'];
|
||||
|
||||
// Convert to asset history format
|
||||
const assetHistory = [];
|
||||
const dates = Object.keys(timeSeries).sort();
|
||||
|
||||
// Calculate QQQ performance starting from first agent's initial value
|
||||
const agentNames = Object.keys(this.agentData);
|
||||
let initialValue = 10000; // Default initial value
|
||||
|
||||
if (agentNames.length > 0) {
|
||||
const firstAgent = this.agentData[agentNames[0]];
|
||||
if (firstAgent && firstAgent.assetHistory.length > 0) {
|
||||
initialValue = firstAgent.assetHistory[0].value;
|
||||
}
|
||||
}
|
||||
|
||||
// Find the earliest start date and latest end date across all agents
|
||||
let startDate = null;
|
||||
let endDate = null;
|
||||
if (agentNames.length > 0) {
|
||||
agentNames.forEach(agentName => {
|
||||
const agent = this.agentData[agentName];
|
||||
if (agent && agent.assetHistory.length > 0) {
|
||||
const agentStartDate = agent.assetHistory[0].date;
|
||||
const agentEndDate = agent.assetHistory[agent.assetHistory.length - 1].date;
|
||||
|
||||
if (!startDate || agentStartDate < startDate) {
|
||||
startDate = agentStartDate;
|
||||
}
|
||||
if (!endDate || agentEndDate > endDate) {
|
||||
endDate = agentEndDate;
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let qqqStartPrice = null;
|
||||
let currentValue = initialValue;
|
||||
|
||||
for (const date of dates) {
|
||||
if (startDate && date < startDate) continue;
|
||||
if (endDate && date > endDate) continue;
|
||||
|
||||
const price = parseFloat(timeSeries[date]['4. close']);
|
||||
if (!qqqStartPrice) {
|
||||
qqqStartPrice = price;
|
||||
}
|
||||
|
||||
// Calculate QQQ performance relative to start
|
||||
const qqqReturn = (price - qqqStartPrice) / qqqStartPrice;
|
||||
currentValue = initialValue * (1 + qqqReturn);
|
||||
|
||||
assetHistory.push({
|
||||
date: date,
|
||||
value: currentValue,
|
||||
id: `qqq-${date}`,
|
||||
action: null
|
||||
});
|
||||
}
|
||||
|
||||
const result = {
|
||||
name: 'QQQ',
|
||||
positions: [],
|
||||
assetHistory: assetHistory,
|
||||
initialValue: initialValue,
|
||||
currentValue: assetHistory.length > 0 ? assetHistory[assetHistory.length - 1].value : initialValue,
|
||||
return: assetHistory.length > 0 ?
|
||||
((assetHistory[assetHistory.length - 1].value - assetHistory[0].value) / assetHistory[0].value * 100) : 0
|
||||
};
|
||||
|
||||
console.log('Successfully loaded QQQ data:', {
|
||||
assetHistory: assetHistory.length,
|
||||
initialValue: result.initialValue,
|
||||
currentValue: result.currentValue,
|
||||
return: result.return
|
||||
});
|
||||
|
||||
return result;
|
||||
} catch (error) {
|
||||
console.error('Error loading QQQ data:', error);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// Load all agents data
|
||||
async loadAllAgentsData() {
|
||||
console.log('Starting to load all agents data...');
|
||||
const agents = await this.loadAgentList();
|
||||
console.log('Found agents:', agents);
|
||||
const allData = {};
|
||||
|
||||
for (const agent of agents) {
|
||||
console.log(`Loading data for ${agent}...`);
|
||||
const data = await this.loadAgentData(agent);
|
||||
if (data) {
|
||||
allData[agent] = data;
|
||||
console.log(`Successfully added ${agent} to allData`);
|
||||
} else {
|
||||
console.log(`Failed to load data for ${agent}`);
|
||||
}
|
||||
}
|
||||
|
||||
console.log('Final allData:', Object.keys(allData));
|
||||
this.agentData = allData;
|
||||
|
||||
// Load QQQ invesco data
|
||||
const qqqData = await this.loadQQQData();
|
||||
if (qqqData) {
|
||||
allData['QQQ'] = qqqData;
|
||||
console.log('Successfully added QQQ invesco to allData');
|
||||
}
|
||||
|
||||
return allData;
|
||||
}
|
||||
|
||||
// Get current holdings for an agent (latest position)
|
||||
getCurrentHoldings(agentName) {
|
||||
const data = this.agentData[agentName];
|
||||
if (!data || !data.positions || data.positions.length === 0) return null;
|
||||
|
||||
const latestPosition = data.positions[data.positions.length - 1];
|
||||
return latestPosition && latestPosition.positions ? latestPosition.positions : null;
|
||||
}
|
||||
|
||||
// Get trade history for an agent
|
||||
getTradeHistory(agentName) {
|
||||
const data = this.agentData[agentName];
|
||||
if (!data) return [];
|
||||
|
||||
return data.positions
|
||||
.filter(p => p.this_action)
|
||||
.map(p => ({
|
||||
date: p.date,
|
||||
action: p.this_action.action,
|
||||
symbol: p.this_action.symbol,
|
||||
amount: p.this_action.amount
|
||||
}))
|
||||
.reverse(); // Most recent first
|
||||
}
|
||||
|
||||
// Format number as currency
|
||||
formatCurrency(value) {
|
||||
return new Intl.NumberFormat('en-US', {
|
||||
style: 'currency',
|
||||
currency: 'USD',
|
||||
minimumFractionDigits: 2
|
||||
}).format(value);
|
||||
}
|
||||
|
||||
// Format percentage
|
||||
formatPercent(value) {
|
||||
const sign = value >= 0 ? '+' : '';
|
||||
return `${sign}${value.toFixed(2)}%`;
|
||||
}
|
||||
|
||||
// Get nice display name for agent
|
||||
getAgentDisplayName(agentName) {
|
||||
const names = {
|
||||
'gemini-2.5-flash': 'Gemini-2.5-flash',
|
||||
'qwen3-max': 'Qwen3-max',
|
||||
'gpt-5': 'GPT-5',
|
||||
'deepseek-chat-v3.1': 'DeepSeek-v3.1',
|
||||
'claude-3.7-sonnet': 'Claude 3.7 Sonnet',
|
||||
'QQQ': 'QQQ invesco'
|
||||
};
|
||||
return names[agentName] || agentName;
|
||||
}
|
||||
|
||||
// Get icon for agent (emoji/symbol representation)
|
||||
getAgentIcon(agentName) {
|
||||
const icons = {
|
||||
'gemini-2.5-flash': '🤖',
|
||||
'qwen3-max': '🧠',
|
||||
'gpt-5': '🔮',
|
||||
'claude-3.7-sonnet': '🎭',
|
||||
'deepseek-chat-v3.1': '🔬',
|
||||
'QQQ': '📈'
|
||||
};
|
||||
return icons[agentName] || '🤖';
|
||||
}
|
||||
|
||||
// Get brand color for agent
|
||||
getAgentBrandColor(agentName) {
|
||||
const colors = {
|
||||
'gemini-2.5-flash': '#8A2BE2', // Google purple
|
||||
'qwen3-max': '#0066ff', // Qwen Blue
|
||||
'gpt-5': '#10a37f', // OpenAI Green
|
||||
'deepseek-chat-v3.1': '#4a90e2', // DeepSeek Blue
|
||||
'claude-3.7-sonnet': '#cc785c', // Anthropic Orange
|
||||
'QQQ': '#ff6b00' // QQQ Orange
|
||||
};
|
||||
return colors[agentName] || null;
|
||||
}
|
||||
}
|
||||
|
||||
// Export for use in other modules
|
||||
window.DataLoader = DataLoader;
|
||||
Reference in New Issue
Block a user