JC321 commited on
Commit
a5f04f7
·
verified ·
1 Parent(s): cd1f91e

Upload 3 files

Browse files
Files changed (3) hide show
  1. app.py +270 -64
  2. edgar_client.py +19 -7
  3. financial_analyzer.py +15 -14
app.py CHANGED
@@ -261,8 +261,24 @@ HTML_CONTENT = """
261
  h2 { color: #1e40af; margin-top: 30px; }
262
  h3 { color: #1e3a8a; }
263
  .endpoint { background: #e0e7ff; padding: 15px; border-radius: 5px; margin: 15px 0; font-family: monospace; }
264
- .tool { background: #f0f9ff; border-left: 4px solid #2563eb; padding: 15px; margin: 10px 0; }
265
- .tool-name { font-weight: bold; color: #1e40af; font-size: 18px; }
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
266
  .code { background: #1e293b; color: #e2e8f0; padding: 15px; border-radius: 5px; overflow-x: auto; margin: 10px 0; }
267
  .code pre { margin: 0; }
268
  .badge { display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: bold; }
@@ -272,7 +288,7 @@ HTML_CONTENT = """
272
  table { width: 100%; border-collapse: collapse; margin: 15px 0; }
273
  th, td { padding: 12px; text-align: left; border-bottom: 1px solid #e5e7eb; }
274
  th { background: #f3f4f6; font-weight: 600; }
275
- .status { position: fixed; top: 20px; right: 20px; background: #22c55e; color: white; padding: 10px 20px; border-radius: 20px; }
276
  </style>
277
  </head>
278
  <body>
@@ -293,57 +309,101 @@ HTML_CONTENT = """
293
  <h2>🛠️ Available Tools (7)</h2>
294
 
295
  <div class="tool">
296
- <div class="tool-name">1. search_company</div>
297
- <p>Search for a company by name</p>
298
- <strong>Parameters:</strong>
299
- <ul>
300
- <li><code>company_name</code> (string): Company name (e.g., "Tesla", "Apple")</li>
301
- </ul>
302
- <strong>Example:</strong>
303
- <div class="code"><pre>{
 
 
 
 
304
  "company_name": "Tesla"
305
  }</pre></div>
306
- <strong>Returns:</strong> Company CIK, name, tickers, SIC code
 
 
 
 
 
 
 
 
 
307
  </div>
308
 
309
  <div class="tool">
310
- <div class="tool-name">2. get_company_info</div>
311
- <p>Get detailed company information</p>
312
- <strong>Parameters:</strong>
313
- <ul>
314
- <li><code>cik</code> (string): Company CIK code (10-digit format)</li>
315
- </ul>
316
- <strong>Example:</strong>
317
- <div class="code"><pre>{
 
 
 
 
318
  "cik": "0001318605"
319
  }</pre></div>
 
 
 
 
 
 
 
 
 
320
  </div>
321
 
322
  <div class="tool">
323
- <div class="tool-name">3. get_company_filings</div>
324
- <p>Get list of SEC filings (10-K, 10-Q, etc.)</p>
325
- <strong>Parameters:</strong>
326
- <ul>
327
- <li><code>cik</code> (string): Company CIK code</li>
328
- <li><code>form_types</code> (array, optional): Filter by form types</li>
329
- </ul>
330
- <strong>Example:</strong>
331
- <div class="code"><pre>{
 
 
 
 
332
  "cik": "0001318605",
333
  "form_types": ["10-K", "10-Q"]
334
  }</pre></div>
 
 
 
 
 
 
 
 
 
 
335
  </div>
336
 
337
  <div class="tool">
338
- <div class="tool-name">4. get_financial_data</div>
339
- <p>Get financial data for a specific period</p>
340
- <strong>Parameters:</strong>
341
- <ul>
342
- <li><code>cik</code> (string): Company CIK code</li>
343
- <li><code>period</code> (string): Period format "YYYY" or "YYYYQX"</li>
344
- </ul>
345
- <strong>Examples:</strong>
346
- <div class="code"><pre>// Annual data
 
 
 
 
347
  {
348
  "cik": "0001318605",
349
  "period": "2024"
@@ -354,46 +414,87 @@ HTML_CONTENT = """
354
  "cik": "0001318605",
355
  "period": "2024Q3"
356
  }</pre></div>
 
 
 
 
 
 
 
 
 
 
357
  </div>
358
 
359
  <div class="tool">
360
- <div class="tool-name">5. extract_financial_metrics ⭐</div>
361
- <p>Extract comprehensive financial metrics for multiple years (annual + quarterly)</p>
362
- <strong>Parameters:</strong>
363
- <ul>
364
- <li><code>cik</code> (string): Company CIK code</li>
365
- <li><code>years</code> (integer): Number of years (1-10, default: 3)</li>
366
- </ul>
367
- <strong>Example:</strong>
368
- <div class="code"><pre>{
 
 
 
 
369
  "cik": "0001318605",
370
  "years": 3
371
  }</pre></div>
372
- <strong>Returns:</strong> Multi-year data sorted newest first (FY → Q4 → Q3 → Q2 → Q1)
 
 
 
 
 
 
 
 
 
 
373
  </div>
374
 
375
  <div class="tool">
376
- <div class="tool-name">6. get_latest_financial_data</div>
377
- <p>Get the most recent financial data available</p>
378
- <strong>Parameters:</strong>
379
- <ul>
380
- <li><code>cik</code> (string): Company CIK code</li>
381
- </ul>
382
- <strong>Example:</strong>
383
- <div class="code"><pre>{
 
 
 
 
384
  "cik": "0001318605"
385
  }</pre></div>
 
 
 
 
 
 
 
 
 
386
  </div>
387
 
388
  <div class="tool">
389
- <div class="tool-name">7. advanced_search_company</div>
390
- <p>Advanced search supporting both company name and CIK code</p>
391
- <strong>Parameters:</strong>
392
- <ul>
393
- <li><code>company_input</code> (string): Company name, ticker, or CIK</li>
394
- </ul>
395
- <strong>Examples:</strong>
396
- <div class="code"><pre>// By company name
 
 
 
 
397
  {
398
  "company_input": "Tesla"
399
  }
@@ -402,6 +503,15 @@ HTML_CONTENT = """
402
  {
403
  "company_input": "0001318605"
404
  }</pre></div>
 
 
 
 
 
 
 
 
 
405
  </div>
406
 
407
  <h2>📝 How to Use</h2>
@@ -493,6 +603,102 @@ HTML_CONTENT = """
493
  <strong>Uptime:</strong> Always-On
494
  </p>
495
  </div>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
496
  </body>
497
  </html>
498
  """
 
261
  h2 { color: #1e40af; margin-top: 30px; }
262
  h3 { color: #1e3a8a; }
263
  .endpoint { background: #e0e7ff; padding: 15px; border-radius: 5px; margin: 15px 0; font-family: monospace; }
264
+ .tool { background: #f0f9ff; border-left: 4px solid #2563eb; padding: 15px; margin: 10px 0; border-radius: 4px; }
265
+ .tool-header { display: flex; align-items: center; cursor: pointer; user-select: none; }
266
+ .tool-header:hover { background: #e0f2fe; margin: -5px -10px; padding: 5px 10px; border-radius: 4px; }
267
+ .tool-toggle { font-size: 14px; margin-right: 10px; transition: transform 0.3s; display: inline-block; }
268
+ .tool-toggle.expanded { transform: rotate(90deg); }
269
+ .tool-name { font-weight: bold; color: #1e40af; font-size: 18px; flex: 1; }
270
+ .tool-content { display: none; margin-top: 15px; padding-top: 15px; border-top: 1px solid #e0e7ff; }
271
+ .tool-content.expanded { display: block; }
272
+ .test-panel { background: #f8fafc; padding: 15px; border-radius: 5px; margin-top: 15px; border: 1px solid #e2e8f0; }
273
+ .test-input { width: 100%; padding: 8px; margin: 5px 0; border: 1px solid #cbd5e1; border-radius: 4px; font-family: monospace; font-size: 13px; }
274
+ .test-button { background: #2563eb; color: white; border: none; padding: 10px 20px; border-radius: 5px; cursor: pointer; font-weight: bold; margin-top: 10px; }
275
+ .test-button:hover { background: #1e40af; }
276
+ .test-button:disabled { background: #94a3b8; cursor: not-allowed; }
277
+ .test-result { background: #1e293b; color: #e2e8f0; padding: 15px; border-radius: 5px; margin-top: 10px; white-space: pre-wrap; word-wrap: break-word; font-family: monospace; font-size: 12px; max-height: 400px; overflow-y: auto; }
278
+ .test-status { padding: 8px 12px; border-radius: 4px; margin-top: 10px; font-size: 14px; }
279
+ .status-loading { background: #dbeafe; color: #1e40af; }
280
+ .status-success { background: #dcfce7; color: #166534; }
281
+ .status-error { background: #fee2e2; color: #991b1b; }
282
  .code { background: #1e293b; color: #e2e8f0; padding: 15px; border-radius: 5px; overflow-x: auto; margin: 10px 0; }
283
  .code pre { margin: 0; }
284
  .badge { display: inline-block; padding: 4px 12px; border-radius: 12px; font-size: 12px; font-weight: bold; }
 
288
  table { width: 100%; border-collapse: collapse; margin: 15px 0; }
289
  th, td { padding: 12px; text-align: left; border-bottom: 1px solid #e5e7eb; }
290
  th { background: #f3f4f6; font-weight: 600; }
291
+ .status { position: fixed; top: 20px; right: 20px; background: #22c55e; color: white; padding: 10px 20px; border-radius: 20px; z-index: 1000; }
292
  </style>
293
  </head>
294
  <body>
 
309
  <h2>🛠️ Available Tools (7)</h2>
310
 
311
  <div class="tool">
312
+ <div class="tool-header" onclick="toggleTool('tool1')">
313
+ <span class="tool-toggle" id="toggle-tool1">▶</span>
314
+ <div class="tool-name">1. search_company</div>
315
+ </div>
316
+ <div class="tool-content" id="content-tool1">
317
+ <p>Search for a company by name</p>
318
+ <strong>Parameters:</strong>
319
+ <ul>
320
+ <li><code>company_name</code> (string): Company name (e.g., "Tesla", "Apple")</li>
321
+ </ul>
322
+ <strong>Example:</strong>
323
+ <div class="code"><pre>{
324
  "company_name": "Tesla"
325
  }</pre></div>
326
+ <strong>Returns:</strong> Company CIK, name, tickers, SIC code
327
+
328
+ <div class="test-panel">
329
+ <strong>🧪 Test this tool:</strong>
330
+ <input type="text" id="input-tool1-company_name" class="test-input" placeholder="Company name (e.g., Tesla)" value="Tesla">
331
+ <button class="test-button" onclick="testTool1()">Run Test</button>
332
+ <div id="status-tool1"></div>
333
+ <div id="result-tool1"></div>
334
+ </div>
335
+ </div>
336
  </div>
337
 
338
  <div class="tool">
339
+ <div class="tool-header" onclick="toggleTool('tool2')">
340
+ <span class="tool-toggle" id="toggle-tool2">▶</span>
341
+ <div class="tool-name">2. get_company_info</div>
342
+ </div>
343
+ <div class="tool-content" id="content-tool2">
344
+ <p>Get detailed company information</p>
345
+ <strong>Parameters:</strong>
346
+ <ul>
347
+ <li><code>cik</code> (string): Company CIK code (10-digit format)</li>
348
+ </ul>
349
+ <strong>Example:</strong>
350
+ <div class="code"><pre>{
351
  "cik": "0001318605"
352
  }</pre></div>
353
+
354
+ <div class="test-panel">
355
+ <strong>🧪 Test this tool:</strong>
356
+ <input type="text" id="input-tool2-cik" class="test-input" placeholder="CIK (e.g., 0001318605)" value="0001318605">
357
+ <button class="test-button" onclick="testTool2()">Run Test</button>
358
+ <div id="status-tool2"></div>
359
+ <div id="result-tool2"></div>
360
+ </div>
361
+ </div>
362
  </div>
363
 
364
  <div class="tool">
365
+ <div class="tool-header" onclick="toggleTool('tool3')">
366
+ <span class="tool-toggle" id="toggle-tool3">▶</span>
367
+ <div class="tool-name">3. get_company_filings</div>
368
+ </div>
369
+ <div class="tool-content" id="content-tool3">
370
+ <p>Get list of SEC filings (10-K, 10-Q, etc.)</p>
371
+ <strong>Parameters:</strong>
372
+ <ul>
373
+ <li><code>cik</code> (string): Company CIK code</li>
374
+ <li><code>form_types</code> (array, optional): Filter by form types</li>
375
+ </ul>
376
+ <strong>Example:</strong>
377
+ <div class="code"><pre>{
378
  "cik": "0001318605",
379
  "form_types": ["10-K", "10-Q"]
380
  }</pre></div>
381
+
382
+ <div class="test-panel">
383
+ <strong>🧪 Test this tool:</strong>
384
+ <input type="text" id="input-tool3-cik" class="test-input" placeholder="CIK" value="0001318605">
385
+ <input type="text" id="input-tool3-form_types" class="test-input" placeholder="Form types (JSON array, e.g., [\"10-K\",\"10-Q\"])" value='["10-K","10-Q"]'>
386
+ <button class="test-button" onclick="testTool3()">Run Test</button>
387
+ <div id="status-tool3"></div>
388
+ <div id="result-tool3"></div>
389
+ </div>
390
+ </div>
391
  </div>
392
 
393
  <div class="tool">
394
+ <div class="tool-header" onclick="toggleTool('tool4')">
395
+ <span class="tool-toggle" id="toggle-tool4">▶</span>
396
+ <div class="tool-name">4. get_financial_data</div>
397
+ </div>
398
+ <div class="tool-content" id="content-tool4">
399
+ <p>Get financial data for a specific period</p>
400
+ <strong>Parameters:</strong>
401
+ <ul>
402
+ <li><code>cik</code> (string): Company CIK code</li>
403
+ <li><code>period</code> (string): Period format "YYYY" or "YYYYQX"</li>
404
+ </ul>
405
+ <strong>Examples:</strong>
406
+ <div class="code"><pre>// Annual data
407
  {
408
  "cik": "0001318605",
409
  "period": "2024"
 
414
  "cik": "0001318605",
415
  "period": "2024Q3"
416
  }</pre></div>
417
+
418
+ <div class="test-panel">
419
+ <strong>🧪 Test this tool:</strong>
420
+ <input type="text" id="input-tool4-cik" class="test-input" placeholder="CIK" value="0001318605">
421
+ <input type="text" id="input-tool4-period" class="test-input" placeholder="Period (e.g., 2024 or 2024Q3)" value="2024">
422
+ <button class="test-button" onclick="testTool4()">Run Test</button>
423
+ <div id="status-tool4"></div>
424
+ <div id="result-tool4"></div>
425
+ </div>
426
+ </div>
427
  </div>
428
 
429
  <div class="tool">
430
+ <div class="tool-header" onclick="toggleTool('tool5')">
431
+ <span class="tool-toggle" id="toggle-tool5">▶</span>
432
+ <div class="tool-name">5. extract_financial_metrics ⭐</div>
433
+ </div>
434
+ <div class="tool-content" id="content-tool5">
435
+ <p>Extract comprehensive financial metrics for multiple years (annual + quarterly)</p>
436
+ <strong>Parameters:</strong>
437
+ <ul>
438
+ <li><code>cik</code> (string): Company CIK code</li>
439
+ <li><code>years</code> (integer): Number of years (1-10, default: 3)</li>
440
+ </ul>
441
+ <strong>Example:</strong>
442
+ <div class="code"><pre>{
443
  "cik": "0001318605",
444
  "years": 3
445
  }</pre></div>
446
+ <strong>Returns:</strong> Multi-year data sorted newest first (FY → Q4 → Q3 → Q2 → Q1)
447
+
448
+ <div class="test-panel">
449
+ <strong>🧪 Test this tool:</strong>
450
+ <input type="text" id="input-tool5-cik" class="test-input" placeholder="CIK" value="0001318605">
451
+ <input type="number" id="input-tool5-years" class="test-input" placeholder="Years (1-10)" value="3" min="1" max="10">
452
+ <button class="test-button" onclick="testTool5()">Run Test</button>
453
+ <div id="status-tool5"></div>
454
+ <div id="result-tool5"></div>
455
+ </div>
456
+ </div>
457
  </div>
458
 
459
  <div class="tool">
460
+ <div class="tool-header" onclick="toggleTool('tool6')">
461
+ <span class="tool-toggle" id="toggle-tool6">▶</span>
462
+ <div class="tool-name">6. get_latest_financial_data</div>
463
+ </div>
464
+ <div class="tool-content" id="content-tool6">
465
+ <p>Get the most recent financial data available</p>
466
+ <strong>Parameters:</strong>
467
+ <ul>
468
+ <li><code>cik</code> (string): Company CIK code</li>
469
+ </ul>
470
+ <strong>Example:</strong>
471
+ <div class="code"><pre>{
472
  "cik": "0001318605"
473
  }</pre></div>
474
+
475
+ <div class="test-panel">
476
+ <strong>🧪 Test this tool:</strong>
477
+ <input type="text" id="input-tool6-cik" class="test-input" placeholder="CIK" value="0001318605">
478
+ <button class="test-button" onclick="testTool6()">Run Test</button>
479
+ <div id="status-tool6"></div>
480
+ <div id="result-tool6"></div>
481
+ </div>
482
+ </div>
483
  </div>
484
 
485
  <div class="tool">
486
+ <div class="tool-header" onclick="toggleTool('tool7')">
487
+ <span class="tool-toggle" id="toggle-tool7">▶</span>
488
+ <div class="tool-name">7. advanced_search_company</div>
489
+ </div>
490
+ <div class="tool-content" id="content-tool7">
491
+ <p>Advanced search supporting both company name and CIK code</p>
492
+ <strong>Parameters:</strong>
493
+ <ul>
494
+ <li><code>company_input</code> (string): Company name, ticker, or CIK</li>
495
+ </ul>
496
+ <strong>Examples:</strong>
497
+ <div class="code"><pre>// By company name
498
  {
499
  "company_input": "Tesla"
500
  }
 
503
  {
504
  "company_input": "0001318605"
505
  }</pre></div>
506
+
507
+ <div class="test-panel">
508
+ <strong>🧪 Test this tool:</strong>
509
+ <input type="text" id="input-tool7-company_input" class="test-input" placeholder="Company name or CIK" value="Tesla">
510
+ <button class="test-button" onclick="testTool7()">Run Test</button>
511
+ <div id="status-tool7"></div>
512
+ <div id="result-tool7"></div>
513
+ </div>
514
+ </div>
515
  </div>
516
 
517
  <h2>📝 How to Use</h2>
 
603
  <strong>Uptime:</strong> Always-On
604
  </p>
605
  </div>
606
+
607
+ <script>
608
+ // Toggle tool content
609
+ function toggleTool(toolId) {
610
+ const content = document.getElementById('content-' + toolId);
611
+ const toggle = document.getElementById('toggle-' + toolId);
612
+
613
+ if (content.classList.contains('expanded')) {
614
+ content.classList.remove('expanded');
615
+ toggle.classList.remove('expanded');
616
+ } else {
617
+ content.classList.add('expanded');
618
+ toggle.classList.add('expanded');
619
+ }
620
+ }
621
+
622
+ // Common function to call MCP API
623
+ async function callMCP(toolName, arguments, statusId, resultId) {
624
+ const statusDiv = document.getElementById(statusId);
625
+ const resultDiv = document.getElementById(resultId);
626
+ const button = event.target;
627
+
628
+ statusDiv.innerHTML = '<div class="test-status status-loading">⏳ Sending request...</div>';
629
+ resultDiv.innerHTML = '';
630
+ button.disabled = true;
631
+
632
+ try {
633
+ const response = await fetch('/sse', {
634
+ method: 'POST',
635
+ headers: { 'Content-Type': 'application/json' },
636
+ body: JSON.stringify({
637
+ jsonrpc: '2.0',
638
+ method: 'tools/call',
639
+ params: {
640
+ name: toolName,
641
+ arguments: arguments
642
+ },
643
+ id: Date.now()
644
+ })
645
+ });
646
+
647
+ const data = await response.json();
648
+ statusDiv.innerHTML = '<div class="test-status status-success">✅ Success!</div>';
649
+ resultDiv.innerHTML = '<div class="test-result">' + JSON.stringify(data, null, 2) + '</div>';
650
+ } catch (error) {
651
+ statusDiv.innerHTML = '<div class="test-status status-error">❌ Error: ' + error.message + '</div>';
652
+ resultDiv.innerHTML = '<div class="test-result">' + JSON.stringify({error: error.toString()}, null, 2) + '</div>';
653
+ } finally {
654
+ button.disabled = false;
655
+ }
656
+ }
657
+
658
+ // Tool-specific test functions
659
+ function testTool1() {
660
+ const companyName = document.getElementById('input-tool1-company_name').value;
661
+ callMCP('search_company', { company_name: companyName }, 'status-tool1', 'result-tool1');
662
+ }
663
+
664
+ function testTool2() {
665
+ const cik = document.getElementById('input-tool2-cik').value;
666
+ callMCP('get_company_info', { cik: cik }, 'status-tool2', 'result-tool2');
667
+ }
668
+
669
+ function testTool3() {
670
+ const cik = document.getElementById('input-tool3-cik').value;
671
+ const formTypesStr = document.getElementById('input-tool3-form_types').value;
672
+ try {
673
+ const formTypes = formTypesStr ? JSON.parse(formTypesStr) : null;
674
+ callMCP('get_company_filings', { cik: cik, form_types: formTypes }, 'status-tool3', 'result-tool3');
675
+ } catch (e) {
676
+ document.getElementById('status-tool3').innerHTML = '<div class="test-status status-error">❌ Invalid JSON for form_types</div>';
677
+ }
678
+ }
679
+
680
+ function testTool4() {
681
+ const cik = document.getElementById('input-tool4-cik').value;
682
+ const period = document.getElementById('input-tool4-period').value;
683
+ callMCP('get_financial_data', { cik: cik, period: period }, 'status-tool4', 'result-tool4');
684
+ }
685
+
686
+ function testTool5() {
687
+ const cik = document.getElementById('input-tool5-cik').value;
688
+ const years = parseInt(document.getElementById('input-tool5-years').value);
689
+ callMCP('extract_financial_metrics', { cik: cik, years: years }, 'status-tool5', 'result-tool5');
690
+ }
691
+
692
+ function testTool6() {
693
+ const cik = document.getElementById('input-tool6-cik').value;
694
+ callMCP('get_latest_financial_data', { cik: cik }, 'status-tool6', 'result-tool6');
695
+ }
696
+
697
+ function testTool7() {
698
+ const companyInput = document.getElementById('input-tool7-company_input').value;
699
+ callMCP('advanced_search_company', { company_input: companyInput }, 'status-tool7', 'result-tool7');
700
+ }
701
+ </script>
702
  </body>
703
  </html>
704
  """
edgar_client.py CHANGED
@@ -158,7 +158,7 @@ class EdgarDataClient:
158
  EdgarDataClient._last_request_time = time.time()
159
 
160
  def search_company_by_name(self, company_name):
161
- """Search company CIK by company name with caching"""
162
  try:
163
  # Check cache first
164
  with self._cache_lock:
@@ -184,23 +184,35 @@ class EdgarDataClient:
184
  EdgarDataClient._company_tickers_cache = companies
185
  EdgarDataClient._company_tickers_cache_time = current_time
186
 
187
- # Search for matching company names
 
 
 
 
 
 
 
 
 
 
 
188
  matches = []
189
  exact_matches = []
 
 
190
  for _, company in companies.items():
191
  company_title = company["title"].lower()
192
- search_name = company_name.lower()
193
 
194
  # Exact match
195
- if search_name == company_title:
196
  exact_matches.append({
197
  "cik": str(company["cik_str"]).zfill(10),
198
  "name": company["title"],
199
  "ticker": company["ticker"]
200
  })
201
- # Partial match
202
- elif search_name in company_title or \
203
- search_name in company["ticker"].lower():
204
  matches.append({
205
  "cik": str(company["cik_str"]).zfill(10),
206
  "name": company["title"],
 
158
  EdgarDataClient._last_request_time = time.time()
159
 
160
  def search_company_by_name(self, company_name):
161
+ """Search company CIK by company name with caching and optimized ticker matching"""
162
  try:
163
  # Check cache first
164
  with self._cache_lock:
 
184
  EdgarDataClient._company_tickers_cache = companies
185
  EdgarDataClient._company_tickers_cache_time = current_time
186
 
187
+ # OPTIMIZATION 1: Prioritize exact ticker match (fastest path)
188
+ search_name_upper = company_name.upper().strip()
189
+ for _, company in companies.items():
190
+ if company["ticker"].upper() == search_name_upper:
191
+ # Exact ticker match - return immediately
192
+ return {
193
+ "cik": str(company["cik_str"]).zfill(10),
194
+ "name": company["title"],
195
+ "ticker": company["ticker"]
196
+ }
197
+
198
+ # ✅ OPTIMIZATION 2: Search for matching company names
199
  matches = []
200
  exact_matches = []
201
+ search_name_lower = company_name.lower()
202
+
203
  for _, company in companies.items():
204
  company_title = company["title"].lower()
205
+ ticker_lower = company["ticker"].lower()
206
 
207
  # Exact match
208
+ if search_name_lower == company_title:
209
  exact_matches.append({
210
  "cik": str(company["cik_str"]).zfill(10),
211
  "name": company["title"],
212
  "ticker": company["ticker"]
213
  })
214
+ # Partial match (name or ticker contains search term)
215
+ elif search_name_lower in company_title or search_name_lower in ticker_lower:
 
216
  matches.append({
217
  "cik": str(company["cik_str"]).zfill(10),
218
  "name": company["title"],
financial_analyzer.py CHANGED
@@ -17,7 +17,7 @@ class FinancialAnalyzer:
17
 
18
  def search_company(self, company_input):
19
  """
20
- Search company information (by name or CIK)
21
 
22
  Args:
23
  company_input (str): Company name or CIK
@@ -27,27 +27,28 @@ class FinancialAnalyzer:
27
  """
28
  # If input is numeric, assume it's a CIK
29
  if company_input.isdigit() and len(company_input) >= 8:
30
- # Get company information
31
  company_info = self.edgar_client.get_company_info(company_input)
32
  if company_info:
33
  return company_info
34
  else:
35
  return {"error": "Company not found for specified CIK"}
36
  else:
37
- # Search company by name
38
  company = self.edgar_client.search_company_by_name(company_input)
39
  if company:
40
- # Get detailed information
41
- company_info = self.edgar_client.get_company_info(company['cik'])
42
- if company_info:
43
- return company_info
44
- else:
45
- # If detailed info unavailable, return basic info
46
- return {
47
- "cik": company['cik'],
48
- "name": company['name'],
49
- "tickers": [company['ticker']] if company['ticker'] else []
50
- }
 
51
  else:
52
  return {"error": "No matching company found"}
53
 
 
17
 
18
  def search_company(self, company_input):
19
  """
20
+ Search company information (by name or CIK) - Optimized version
21
 
22
  Args:
23
  company_input (str): Company name or CIK
 
27
  """
28
  # If input is numeric, assume it's a CIK
29
  if company_input.isdigit() and len(company_input) >= 8:
30
+ # Get company information from cache (will use @lru_cache)
31
  company_info = self.edgar_client.get_company_info(company_input)
32
  if company_info:
33
  return company_info
34
  else:
35
  return {"error": "Company not found for specified CIK"}
36
  else:
37
+ # Search company by name/ticker (uses cached company_tickers.json)
38
  company = self.edgar_client.search_company_by_name(company_input)
39
  if company:
40
+ # OPTIMIZATION: Return basic info directly without calling get_company_info
41
+ # search_company_by_name already returns: cik, name, ticker
42
+ # Only call get_company_info if we need SIC code or description
43
+
44
+ # For basic searches, the ticker data is sufficient
45
+ # This eliminates the 3-5 second delay from get_company_info
46
+ return {
47
+ "cik": company['cik'],
48
+ "name": company['name'],
49
+ "tickers": [company['ticker']] if company.get('ticker') else [],
50
+ "_source": "company_tickers_cache" # Debug info
51
+ }
52
  else:
53
  return {"error": "No matching company found"}
54