Spaces:
Configuration error
Configuration error
Download static/js/api_client.js from BlueMen/tradesenseAi: direct link, hf CLI and curl.
- Browser
- Download file 17.2 kB
-
https://huggingface.co/spaces/BlueMen/tradesenseAi/resolve/main/static/js/api_client.js
- Command line
-
hf download hf://spaces/BlueMen/tradesenseAi/static/js/api_client.js
-
curl -L -o api_client.js https://huggingface.co/spaces/BlueMen/tradesenseAi/resolve/main/static/js/api_client.js
17.2 kB
| /** | |
| * TradeSense AI β Authoritative Institutional API Client & Adapters (Milestone 23 Master) | |
| * Maps 1-to-1 with openapi/tradesense-openapi.json. | |
| * Strict zero-credential leakage; in-memory session handling. | |
| */ | |
| class TradeSenseAPIClient { | |
| constructor(baseUrl = '') { | |
| this.baseUrl = baseUrl; | |
| this.accessToken = null; | |
| this.tenantId = 'default_tenant'; | |
| this.onUnauthorizedCallback = null; | |
| } | |
| setToken(token) { | |
| this.accessToken = token; | |
| } | |
| setTenant(tenantId) { | |
| this.tenantId = tenantId; | |
| } | |
| onUnauthorized(callback) { | |
| this.onUnauthorizedCallback = callback; | |
| } | |
| async request(endpoint, options = {}) { | |
| const headers = options.headers || {}; | |
| if (this.accessToken) { | |
| headers['Authorization'] = `Bearer ${this.accessToken}`; | |
| } | |
| headers['X-Request-ID'] = `req_${Math.random().toString(36).substring(2, 10)}`; | |
| const config = { | |
| ...options, | |
| headers, | |
| }; | |
| try { | |
| const response = await fetch(`${this.baseUrl}${endpoint}`, config); | |
| if (response.status === 401) { | |
| if (this.onUnauthorizedCallback) { | |
| this.onUnauthorizedCallback(); | |
| } | |
| const errData = await response.json().catch(() => ({})); | |
| throw new Error(errData.detail || 'Authentication required or session expired.'); | |
| } | |
| if (!response.ok) { | |
| const errData = await response.json().catch(() => ({})); | |
| throw new Error(errData.detail || errData.error?.message || `HTTP ${response.status}: Request failed.`); | |
| } | |
| const contentType = response.headers.get('content-type'); | |
| if (contentType && contentType.includes('application/json')) { | |
| return await response.json(); | |
| } | |
| return await response.text(); | |
| } catch (err) { | |
| console.error(`API Error [${endpoint}]:`, err); | |
| throw err; | |
| } | |
| } | |
| // ββ Authentication ββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async login(username, password, tenantId = 'default_tenant') { | |
| const query = new URLSearchParams({ username, password, tenant_id: tenantId }); | |
| const res = await this.request(`/api/v1/auth/login?${query.toString()}`, { | |
| method: 'POST', | |
| }); | |
| this.setToken(res.access_token); | |
| this.setTenant(res.tenant_id); | |
| return res; | |
| } | |
| async logout() { | |
| try { | |
| await this.request('/api/v1/auth/logout', { method: 'POST' }); | |
| } finally { | |
| this.accessToken = null; | |
| } | |
| } | |
| // ββ Research Plane: Runs, Specs, Evidence βββββββββββββββββββββββββββββββββ | |
| async listResearchRuns(experimentId = null, limit = 50, offset = 0) { | |
| const params = new URLSearchParams({ limit, offset }); | |
| if (experimentId) params.append('experiment_id', experimentId); | |
| return await this.request(`/api/v1/research/runs?${params.toString()}`); | |
| } | |
| async getResearchRun(runId) { | |
| return await this.request(`/api/v1/research/runs/${encodeURIComponent(runId)}`); | |
| } | |
| async submitResearchRun(config, experimentId, strategyId, idempotencyKey) { | |
| const params = new URLSearchParams({ experiment_id: experimentId, strategy_id: strategyId }); | |
| return await this.request(`/api/v1/research/runs?${params.toString()}`, { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| 'Idempotency-Key': idempotencyKey || `idem_${Date.now()}`, | |
| }, | |
| body: JSON.stringify(config), | |
| }); | |
| } | |
| async getEvidencePackage(runId) { | |
| return await this.request(`/api/v1/research/runs/${encodeURIComponent(runId)}/evidence_package`); | |
| } | |
| async reproduceRun(runId) { | |
| return await this.request(`/api/v1/research/runs/${encodeURIComponent(runId)}/reproduce`, { | |
| method: 'POST', | |
| }); | |
| } | |
| async getStockResearch(ticker, horizonSessions = 21) { | |
| const params = new URLSearchParams({ horizon_sessions: horizonSessions }); | |
| return await this.request(`/api/v1/research/${encodeURIComponent(ticker)}?${params.toString()}`); | |
| } | |
| // ββ Module 05: Asynchronous Compute Job Infrastructure ββββββββββββββββββ | |
| async submitResearchJob(ticker, horizonSessions = 21, priority = 1, idempotencyKey = null) { | |
| return await this.request('/api/v1/jobs/research', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| ticker, | |
| horizon_sessions: horizonSessions, | |
| priority, | |
| idempotency_key: idempotencyKey, | |
| }), | |
| }); | |
| } | |
| async getJobStatus(jobId) { | |
| return await this.request(`/api/v1/jobs/${encodeURIComponent(jobId)}`); | |
| } | |
| async cancelJob(jobId) { | |
| return await this.request(`/api/v1/jobs/${encodeURIComponent(jobId)}/cancel`, { | |
| method: 'POST', | |
| }); | |
| } | |
| async getJobResult(jobId) { | |
| return await this.request(`/api/v1/jobs/${encodeURIComponent(jobId)}/result`); | |
| } | |
| async listJobs(status = null, limit = 50, offset = 0) { | |
| const params = new URLSearchParams({ limit, offset }); | |
| if (status) params.append('status', status); | |
| return await this.request(`/api/v1/jobs?${params.toString()}`); | |
| } | |
| async getJobEvents(jobId) { | |
| return await this.request(`/api/v1/jobs/${encodeURIComponent(jobId)}/events`); | |
| } | |
| // ββ Module 06: Independent Quant Validation & Reproducibility ββββββββββββ | |
| async submitValidationJob(ticker, horizonSessions = 21, depth = 'FAST', randomSeed = 42) { | |
| return await this.request('/api/v1/jobs/validation', { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ | |
| ticker, | |
| horizon_sessions: horizonSessions, | |
| depth, | |
| random_seed: randomSeed, | |
| }), | |
| }); | |
| } | |
| async getValidationRun(validationId) { | |
| return await this.request(`/api/v1/validation/${encodeURIComponent(validationId)}`); | |
| } | |
| async getValidationReport(validationId) { | |
| return await this.request(`/api/v1/validation/${encodeURIComponent(validationId)}/report`); | |
| } | |
| async getValidationCertificate(validationId) { | |
| return await this.request(`/api/v1/validation/${encodeURIComponent(validationId)}/certificate`); | |
| } | |
| async replayValidation(validationId) { | |
| return await this.request(`/api/v1/validation/${encodeURIComponent(validationId)}/replay`, { | |
| method: 'POST', | |
| }); | |
| } | |
| // ββ Governance & Control Benchmarks (Module 07 Lifecycle) βββββββββββββββββ | |
| async preRegisterResearch(researchId, protocolData) { | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/pre-register`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(protocolData), | |
| }); | |
| } | |
| async startGovernanceResearch(researchId, version = 1) { | |
| const params = new URLSearchParams({ version }); | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/start?${params.toString()}`, { | |
| method: 'POST', | |
| }); | |
| } | |
| async submitResearchForReview(researchId, reviewData) { | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/submit-review`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(reviewData), | |
| }); | |
| } | |
| async approveGovernanceDecision(researchId, approvalData) { | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/approve`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(approvalData), | |
| }); | |
| } | |
| async rejectGovernanceDecision(researchId, rejectData) { | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/reject`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(rejectData), | |
| }); | |
| } | |
| async revokeGovernanceApproval(researchId, revokeData) { | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/revoke`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(revokeData), | |
| }); | |
| } | |
| async archiveGovernanceResearch(researchId, archiveData) { | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/archive`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify(archiveData), | |
| }); | |
| } | |
| async getGovernanceState(researchId, version = 1) { | |
| const params = new URLSearchParams({ version }); | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}?${params.toString()}`); | |
| } | |
| async getGovernanceHistory(researchId) { | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/history`); | |
| } | |
| async getGovernanceEvidence(researchId, version = 1) { | |
| const params = new URLSearchParams({ version }); | |
| return await this.request(`/api/v1/governance/${encodeURIComponent(researchId)}/evidence?${params.toString()}`); | |
| } | |
| async exportAuditPackage(researchId, version = 1) { | |
| const params = new URLSearchParams({ version }); | |
| return await this.request(`/api/v1/audit/export/${encodeURIComponent(researchId)}?${params.toString()}`); | |
| } | |
| async listGovernanceDecisions(experimentId = null, limit = 50, offset = 0) { | |
| const params = new URLSearchParams({ limit, offset }); | |
| if (experimentId) params.append('experiment_id', experimentId); | |
| return await this.request(`/api/v1/governance/decisions?${params.toString()}`); | |
| } | |
| async submitPeerReview(decisionId, verdict, comments) { | |
| return await this.request(`/api/v1/governance/decisions/${encodeURIComponent(decisionId)}/peer_review`, { | |
| method: 'POST', | |
| headers: { 'Content-Type': 'application/json' }, | |
| body: JSON.stringify({ verdict, comments }), | |
| }); | |
| } | |
| async getFamily1Archive() { | |
| return await this.request('/api/v1/governance/family_1_archive'); | |
| } | |
| // ββ Cryptographic Audit Ledger ββββββββββββββββββββββββββββββββββββββββββββ | |
| async listAuditEvents(limit = 100, offset = 0) { | |
| const params = new URLSearchParams({ limit, offset }); | |
| return await this.request(`/api/v1/audit/events?${params.toString()}`); | |
| } | |
| async verifyAuditLedger() { | |
| return await this.request('/api/v1/audit/verify'); | |
| } | |
| // ββ Indian Market Endpoints (Live Ticker, Quotes, Charts, SEBI Friction) β | |
| async getLiveMarketTicker() { | |
| return await this.request('/api/v1/market/live_ticker'); | |
| } | |
| async getStockQuote(symbol, exchange = 'NSE') { | |
| const params = new URLSearchParams({ exchange }); | |
| return await this.request(`/api/v1/market/quote/${encodeURIComponent(symbol)}?${params.toString()}`); | |
| } | |
| async getStockChartBars(symbol, exchange = 'NSE', period = '1d', interval = '5m') { | |
| const params = new URLSearchParams({ exchange, period, interval }); | |
| return await this.request(`/api/v1/market/chart/${encodeURIComponent(symbol)}?${params.toString()}`); | |
| } | |
| async getCompanyOverview(exchange = 'NSE', refresh = false) { | |
| const params = new URLSearchParams({ exchange, refresh: refresh ? 'true' : 'false' }); | |
| return await this.request(`/api/v1/market/company_overview?${params.toString()}`); | |
| } | |
| async calculateStatutoryCosts(buyPrice, sellPrice, quantity = 10, segment = 'EQUITY_DELIVERY') { | |
| const params = new URLSearchParams({ buy_price: buyPrice, sell_price: sellPrice, quantity, segment }); | |
| return await this.request(`/api/v1/statutory/costs?${params.toString()}`); | |
| } | |
| async searchStocks(query, exchange = 'ALL', limit = 10) { | |
| const params = new URLSearchParams({ q: query, exchange, limit }); | |
| return await this.request(`/api/v1/market/search?${params.toString()}`); | |
| } | |
| // ββ Operations & Telemetry ββββββββββββββββββββββββββββββββββββββββββββββββ | |
| async getHealth() { | |
| return await this.request('/api/v1/health'); | |
| } | |
| async getVersion() { | |
| return await this.request('/api/v1/version'); | |
| } | |
| async getTenantUsage() { | |
| return await this.request('/api/v1/observability/usage'); | |
| } | |
| async getObservabilityMetrics() { | |
| return await this.request('/api/v1/observability/metrics'); | |
| } | |
| } | |
| // Instantiate global singleton client | |
| window.TradeSenseAPI = new TradeSenseAPIClient(); | |
| // ============================================================================== | |
| // Domain Normalization Adapters (Decouples UI from raw backend JSON formats) | |
| // ============================================================================== | |
| window.TradeSenseAdapters = { | |
| /** | |
| * Normalizes raw backend ResearchRun into a structured client model with first-class human identity | |
| */ | |
| normalizeResearchRun(rawRun) { | |
| if (!rawRun) return null; | |
| const runId = rawRun.run_id || 'run_unknown'; | |
| const config = rawRun.configuration || {}; | |
| const custom = config.custom_parameters || {}; | |
| const prov = rawRun.provenance || {}; | |
| // 1. Determine human-readable research name and sanitize universe to Indian equities | |
| const rawUniverse = (config.universe_tickers && config.universe_tickers.length > 0) | |
| ? config.universe_tickers | |
| : ['RELIANCE', 'TCS']; | |
| // Sanitize any legacy foreign symbols to Indian equities | |
| const indianTickerMap = { | |
| 'AAPL': 'RELIANCE', | |
| 'MSFT': 'TCS', | |
| 'GOOG': 'INFY', | |
| 'AMZN': 'HDFCBANK', | |
| 'NVDA': 'TATAMOTORS', | |
| 'META': 'ICICIBANK', | |
| 'TSLA': 'JSWSTEEL', | |
| }; | |
| const universeList = rawUniverse.map(tk => indianTickerMap[tk.toUpperCase()] || tk.toUpperCase()); | |
| let researchName = custom.research_name || config.research_name || null; | |
| let isFallbackName = false; | |
| if (!researchName || researchName.includes('AAPL') || researchName.includes('MSFT')) { | |
| if (rawRun.experiment_id && rawRun.experiment_id.includes(' ') && !rawRun.experiment_id.includes('AAPL')) { | |
| researchName = rawRun.experiment_id; | |
| } else if (rawRun.experiment_id && !rawRun.experiment_id.startsWith('exp_') && !rawRun.experiment_id.includes('AAPL')) { | |
| researchName = rawRun.experiment_id.replace(/_/g, ' ').toUpperCase(); | |
| } else { | |
| // Deterministic Indian market research name | |
| const universeStr = universeList.join(', '); | |
| const horizon = config.holding_period_sessions || 21; | |
| const strat = (rawRun.strategy_id || 'Quantitative Strategy').replace(/^strat_/, '').replace(/_/g, ' '); | |
| const stratClean = strat.toLowerCase().includes('01') ? 'Alpha Factor Momentum' : (strat.charAt(0).toUpperCase() + strat.slice(1)); | |
| researchName = `${universeStr} ${stratClean} β ${horizon} Sessions`; | |
| isFallbackName = true; | |
| } | |
| } | |
| // 2. Extract hypothesis & methodology | |
| const hypothesis = custom.hypothesis || 'Investigate empirical predictability and factor contamination under Newey-West HAC covariance correction on NSE Indian equities.'; | |
| const methodology = custom.methodology || 'Conformal Prediction & Purged K-Fold Cross-Validation'; | |
| return { | |
| runId, | |
| researchName, | |
| isFallbackName, | |
| experimentId: rawRun.experiment_id || 'exp_general', | |
| strategyId: rawRun.strategy_id || 'strat_general', | |
| tenantId: rawRun.tenant_id || 'default_tenant', | |
| workflowState: rawRun.workflow_state || 'PRE_REGISTERED', | |
| status: rawRun.status || 'QUEUED', | |
| universe: universeList, | |
| horizonSessions: config.holding_period_sessions || 21, | |
| randomSeed: config.random_seed || 42, | |
| hypothesis, | |
| methodology, | |
| configuration: config, | |
| provenance: prov, | |
| createdAt: rawRun.created_at || '2026-09-02T09:00:00Z', | |
| }; | |
| }, | |
| /** | |
| * Normalizes raw backend stock research response into an authoritative DecisionEvidenceModel | |
| */ | |
| normalizeDecisionBrief(raw) { | |
| if (!raw) return null; | |
| if (window.TradeSenseDecisionEngine && window.TradeSenseDecisionEngine.buildDecisionEvidenceModel) { | |
| return window.TradeSenseDecisionEngine.buildDecisionEvidenceModel(raw); | |
| } | |
| return null; | |
| }, | |
| /** | |
| * Clipboard helper with toast feedback | |
| */ | |
| copyToClipboard(text, label = 'Identifier') { | |
| if (navigator.clipboard && navigator.clipboard.writeText) { | |
| navigator.clipboard.writeText(text).then(() => { | |
| if (window.AppState && window.AppState.showToast) { | |
| window.AppState.showToast(`β Copied ${label} to clipboard!`); | |
| } | |
| }).catch(() => this.fallbackCopy(text, label)); | |
| } else { | |
| this.fallbackCopy(text, label); | |
| } | |
| }, | |
| fallbackCopy(text, label) { | |
| const input = document.createElement('input'); | |
| input.value = text; | |
| document.body.appendChild(input); | |
| input.select(); | |
| document.execCommand('copy'); | |
| document.body.removeChild(input); | |
| if (window.AppState && window.AppState.showToast) { | |
| window.AppState.showToast(`β Copied ${label} to clipboard!`); | |
| } | |
| } | |
| }; | |