Add AI scoring results: 121 stocks scored with GPT-4.1-mini

- MemelyAlphaStockRanking_scored.xlsx: Excel with AI scores (yellow highlighted)
- research_scores.json: Raw scoring data
- score_stocks.py: AI scoring script
- write_scores_to_excel.py: Excel writing script
- Updated README with methodology and top 10 rankings
这个提交包含在:
hao
2026-03-14 07:55:14 -04:00
父节点 a985d71b76
当前提交 c579681989
修改 6 个文件,包含 2768 行新增2 行删除

二进制
MemelyAlphaStockRanking_scored.xlsx 普通文件

二进制文件未显示。

查看文件

@@ -1,3 +1,65 @@
# memely-alpha-stock-ranking
# Memely Alpha Stock Ranking - AI Scoring Analysis
Memely Alpha Stock Ranking - Deep Research & AI Scoring Analysis
## Overview
This repository contains the **Memely Alpha Stock Ranking** data with AI-powered scoring analysis for 121 unique stocks/assets across multiple thematic sectors.
## Files
| File | Description |
|------|-------------|
| `MemelyAlphaStockRanking_original.xlsx` | Original Excel file with stock master list, weekly/YTD rankings |
| `MemelyAlphaStockRanking_scored.xlsx` | Updated Excel with AI scores (yellow-highlighted cells) |
| `research_scores.json` | Raw AI scoring data in JSON format |
| `scoring_progress.json` | Scoring progress tracking file |
| `score_stocks.py` | Python script used for AI model scoring |
| `write_scores_to_excel.py` | Python script to write scores into Excel |
## AI Scoring Methodology
Each stock was evaluated by an AI model (GPT-4.1-mini) based on:
1. **Overall Score (1-100)**: Comprehensive investment attractiveness
2. **Momentum Score (1-100)**: Based on 7-day and YTD price performance
3. **Theme Score (1-100)**: Relevance of thematic play (AI, Defense, Aerospace, Crypto, etc.)
4. **Risk Score (1-100)**: Volatility and risk level (100 = highest risk)
5. **Social Buzz Score (1-100)**: KOL backing and social media attention
## Top 10 Stocks by Overall Score
| Rank | Symbol | Theme | Overall | Momentum | Theme Score |
|------|--------|-------|---------|----------|-------------|
| 1 | UMAC | Drone | 88 | 95 | 80 |
| 2 | EQRLF | - | 85 | 95 | 20 |
| 3 | OSS | Defense | 82 | 80 | 70 |
| 4 | AXTI | Photonics | 80 | 90 | 75 |
| 5 | LASR | Defense | 80 | 85 | 75 |
| 6 | LFMD | GLP-1 | 80 | 90 | 75 |
| 7 | AMPX | Battery | 78 | 60 | 80 |
| 8 | CRCL | Stablecoin | 78 | 75 | 70 |
| 9 | FSLY | - | 75 | 80 | 25 |
| 10 | PSRHF | Helium 3 | 75 | 85 | 70 |
## Sector Distribution
Key sectors covered include:
- **Defense & Drone**: UMAC, OSS, LASR, RCAT, KTOS, MRCY, AIRO, etc.
- **Aerospace**: ASTS, RDW, PL, IRDM, RKLB, VELO, etc.
- **AI & Technology**: ACMR, NBIS, ZETA, PATH, GTLB, etc.
- **Photonics**: AXTI, AAOI, POET, LPTH, ALMU
- **Crypto & Fintech**: CRCL, GLXY, BKKT, FIGR, ETOR, GEMI
- **Biotech & Pharma**: LFMD, NVO, RXRX, CERT, HIMS
- **Quantum Computing**: IONQ, RGTI, QUBT, QBTS, INFQ
- **Nuclear/Energy**: UUUU, OKLO, SMR, NEE, PPSI
## Excel Modifications
All AI-generated scores are written into the Excel file with **yellow background highlighting** to clearly distinguish new data from original data. The modifications include:
1. **"stock master list" sheet**: 6 new columns added (AI Overall Score, Momentum, Theme, Risk, Social Buzz, Brief Analysis)
2. **"Sheet4"**: Same 6 columns added
3. **"AI Score Summary" (new sheet)**: Complete ranking sorted by Overall Score with color-coded cells
## Date
Analysis performed on: **March 14, 2026**

1091
research_scores.json 普通文件

文件差异内容过多而无法显示 加载差异

196
score_stocks.py 普通文件
查看文件

@@ -0,0 +1,196 @@
#!/usr/bin/env python3
"""
Score all stocks in the MemelyAlpha ranking using AI model.
Processes in batches to handle rate limits and save progress.
"""
import pandas as pd
import json
import os
import time
from openai import OpenAI
client = OpenAI()
EXCEL_PATH = "/home/ubuntu/upload/MemelyAlphaStockRanking副本副本.xlsx"
OUTPUT_JSON = "/home/ubuntu/memely-alpha-stock-ranking/research_scores.json"
PROGRESS_FILE = "/home/ubuntu/memely-alpha-stock-ranking/scoring_progress.json"
# Read the master list
df = pd.read_excel(EXCEL_PATH, sheet_name='stock master list')
# Also read Sheet4 for sector/theme info
df4 = pd.read_excel(EXCEL_PATH, sheet_name='Sheet4')
sector_map = {}
for _, row in df4.iterrows():
sym = row.get('Symbol')
if pd.notna(sym):
sector_map[sym] = {
'sector': row.get('Sector', ''),
'theme': row.get('Theme', ''),
'cap_category': row.get('Cap Category', '')
}
# Read Sheet6 for additional YTD data
df6 = pd.read_excel(EXCEL_PATH, sheet_name='Sheet6')
ytd_extra = {}
for _, row in df6.iterrows():
sym = row.get('Symbol')
if pd.notna(sym):
ytd_extra[sym] = row.get('performance Year to Date', None)
# Build stock info for each symbol
stocks_info = []
seen = set()
for _, row in df.iterrows():
sym = row['Symbol']
if pd.isna(sym) or sym in seen:
continue
seen.add(sym)
info = {
'symbol': sym,
'theme': str(row.get('Theme', '')),
'perf_7d': row.get('performance past 7 days', None),
'perf_ytd': row.get('performance Year to Date', None),
'perf_specific': row.get('performance on specific dates', None),
'first_call_kol': str(row.get('first call X kol', '')),
'top_contributor': str(row.get('top contributor', '')),
}
# Add sector info from Sheet4
if sym in sector_map:
info['sector'] = sector_map[sym].get('sector', '')
info['theme_s4'] = sector_map[sym].get('theme', '')
# Add extra YTD from Sheet6
if sym in ytd_extra and pd.notna(ytd_extra[sym]):
info['ytd_extra'] = ytd_extra[sym]
stocks_info.append(info)
print(f"Total unique stocks to score: {len(stocks_info)}")
# Load progress if exists
scored = {}
if os.path.exists(PROGRESS_FILE):
with open(PROGRESS_FILE, 'r') as f:
scored = json.load(f)
print(f"Loaded {len(scored)} previously scored stocks")
def score_batch(batch_stocks):
"""Score a batch of stocks using AI model."""
stocks_text = ""
for s in batch_stocks:
perf_7d = f"{s['perf_7d']:.2%}" if pd.notna(s.get('perf_7d')) else "N/A"
perf_ytd = f"{s['perf_ytd']:.2%}" if pd.notna(s.get('perf_ytd')) else "N/A"
perf_spec = f"{s['perf_specific']:.2%}" if pd.notna(s.get('perf_specific')) else "N/A"
sector = s.get('sector', s.get('theme', 'N/A'))
theme = s.get('theme_s4', s.get('theme', 'N/A'))
stocks_text += f"""
---
Symbol: {s['symbol']}
Sector: {sector}
Theme: {theme}
Performance (Past 7 Days): {perf_7d}
Performance (Year to Date): {perf_ytd}
Performance (Specific Date): {perf_spec}
First Call KOL: {s.get('first_call_kol', 'N/A')}
Top Contributor: {s.get('top_contributor', 'N/A')}
"""
prompt = f"""You are a professional stock/crypto analyst. Evaluate each of the following stocks/assets and provide a comprehensive score.
For EACH stock, provide:
1. **overall_score** (1-100): Overall investment attractiveness score
2. **momentum_score** (1-100): Based on recent price performance and momentum
3. **theme_score** (1-100): How strong/relevant is the thematic play (e.g., AI, Defense, Aerospace, Crypto, etc.)
4. **risk_score** (1-100): Risk level (100 = highest risk)
5. **social_buzz_score** (1-100): Social media attention and KOL backing strength
6. **brief_analysis**: 2-3 sentence analysis explaining the scores
Consider these factors:
- YTD performance indicates momentum strength
- Thematic relevance to current market trends (AI, Defense, Aerospace, Crypto, Quantum Computing are hot in 2025-2026)
- Small/micro cap stocks with strong themes get higher theme scores
- Stocks with notable KOL backing get higher social buzz scores
- Higher volatility = higher risk score
Here are the stocks to evaluate:
{stocks_text}
Return ONLY a valid JSON array with objects for each stock. Each object must have: symbol, overall_score, momentum_score, theme_score, risk_score, social_buzz_score, brief_analysis.
Do not include any text outside the JSON array."""
try:
response = client.chat.completions.create(
model="gpt-4.1-mini",
messages=[
{"role": "system", "content": "You are a professional financial analyst. Always respond with valid JSON only."},
{"role": "user", "content": prompt}
],
temperature=0.3,
max_tokens=8000
)
content = response.choices[0].message.content.strip()
# Try to extract JSON from the response
if content.startswith("```"):
content = content.split("```")[1]
if content.startswith("json"):
content = content[4:]
results = json.loads(content)
return results
except Exception as e:
print(f"Error in batch scoring: {e}")
return None
# Process in batches of 15
batch_size = 15
remaining = [s for s in stocks_info if s['symbol'] not in scored]
print(f"Remaining to score: {len(remaining)}")
total_batches = (len(remaining) + batch_size - 1) // batch_size
for i in range(0, len(remaining), batch_size):
batch = remaining[i:i+batch_size]
batch_num = i // batch_size + 1
symbols_in_batch = [s['symbol'] for s in batch]
print(f"\nBatch {batch_num}/{total_batches}: Scoring {symbols_in_batch}")
results = score_batch(batch)
if results:
for r in results:
sym = r.get('symbol', '')
if sym:
scored[sym] = r
print(f"{sym}: Overall={r.get('overall_score')}, Momentum={r.get('momentum_score')}, Theme={r.get('theme_score')}")
# Save progress after each batch
with open(PROGRESS_FILE, 'w') as f:
json.dump(scored, f, indent=2, ensure_ascii=False)
else:
print(f" ✗ Batch {batch_num} failed, will retry...")
# Retry once
time.sleep(2)
results = score_batch(batch)
if results:
for r in results:
sym = r.get('symbol', '')
if sym:
scored[sym] = r
with open(PROGRESS_FILE, 'w') as f:
json.dump(scored, f, indent=2, ensure_ascii=False)
time.sleep(1) # Rate limit
# Save final results
with open(OUTPUT_JSON, 'w') as f:
json.dump(scored, f, indent=2, ensure_ascii=False)
print(f"\n=== SCORING COMPLETE ===")
print(f"Total scored: {len(scored)}/{len(stocks_info)}")
print(f"Results saved to: {OUTPUT_JSON}")

1091
scoring_progress.json 普通文件

文件差异内容过多而无法显示 加载差异

326
write_scores_to_excel.py 普通文件
查看文件

@@ -0,0 +1,326 @@
#!/usr/bin/env python3
"""
Write AI scores into the Excel file and highlight modified cells with yellow background.
"""
import json
import pandas as pd
from openpyxl import load_workbook
from openpyxl.styles import PatternFill, Font, Alignment, Border, Side
from copy import copy
EXCEL_SRC = "/home/ubuntu/upload/MemelyAlphaStockRanking副本副本.xlsx"
SCORES_JSON = "/home/ubuntu/memely-alpha-stock-ranking/research_scores.json"
EXCEL_OUT = "/home/ubuntu/memely-alpha-stock-ranking/MemelyAlphaStockRanking_scored.xlsx"
# Load scores
with open(SCORES_JSON, 'r') as f:
scores = json.load(f)
print(f"Loaded scores for {len(scores)} stocks")
# Load workbook
wb = load_workbook(EXCEL_SRC)
# Yellow fill for modified cells
yellow_fill = PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid")
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
header_font = Font(bold=True, color="FFFFFF", size=11)
score_font = Font(size=10)
thin_border = Border(
left=Side(style='thin'),
right=Side(style='thin'),
top=Side(style='thin'),
bottom=Side(style='thin')
)
# ========== Modify "stock master list" sheet ==========
ws = wb['stock master list']
# Find the last column with data
max_col = ws.max_column
# Add new score columns
new_cols = {
max_col + 1: 'AI Overall Score',
max_col + 2: 'AI Momentum Score',
max_col + 3: 'AI Theme Score',
max_col + 4: 'AI Risk Score',
max_col + 5: 'AI Social Buzz Score',
max_col + 6: 'AI Brief Analysis',
}
# Write headers
for col_offset, header_name in new_cols.items():
cell = ws.cell(row=1, column=col_offset, value=header_name)
cell.fill = header_fill
cell.font = header_font
cell.alignment = Alignment(horizontal='center', wrap_text=True)
cell.border = thin_border
# Write scores for each stock
for row in range(2, ws.max_row + 1):
symbol = ws.cell(row=row, column=1).value
if symbol and symbol in scores:
s = scores[symbol]
# Overall Score
cell = ws.cell(row=row, column=max_col + 1, value=s.get('overall_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Momentum Score
cell = ws.cell(row=row, column=max_col + 2, value=s.get('momentum_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Theme Score
cell = ws.cell(row=row, column=max_col + 3, value=s.get('theme_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Risk Score
cell = ws.cell(row=row, column=max_col + 4, value=s.get('risk_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Social Buzz Score
cell = ws.cell(row=row, column=max_col + 5, value=s.get('social_buzz_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Brief Analysis
cell = ws.cell(row=row, column=max_col + 6, value=s.get('brief_analysis', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(wrap_text=True)
cell.border = thin_border
# Set column widths
ws.column_dimensions[ws.cell(row=1, column=max_col + 1).column_letter].width = 15
ws.column_dimensions[ws.cell(row=1, column=max_col + 2).column_letter].width = 15
ws.column_dimensions[ws.cell(row=1, column=max_col + 3).column_letter].width = 15
ws.column_dimensions[ws.cell(row=1, column=max_col + 4).column_letter].width = 15
ws.column_dimensions[ws.cell(row=1, column=max_col + 5).column_letter].width = 18
ws.column_dimensions[ws.cell(row=1, column=max_col + 6).column_letter].width = 60
print(f"Updated 'stock master list' sheet with scores")
# ========== Also modify Sheet4 with scores ==========
ws4 = wb['Sheet4']
max_col4 = ws4.max_column
new_cols4 = {
max_col4 + 1: 'AI Overall Score',
max_col4 + 2: 'AI Momentum Score',
max_col4 + 3: 'AI Theme Score',
max_col4 + 4: 'AI Risk Score',
max_col4 + 5: 'AI Social Buzz Score',
max_col4 + 6: 'AI Brief Analysis',
}
# Write headers for Sheet4
for col_offset, header_name in new_cols4.items():
cell = ws4.cell(row=1, column=col_offset, value=header_name)
cell.fill = header_fill
cell.font = header_font
cell.alignment = Alignment(horizontal='center', wrap_text=True)
cell.border = thin_border
# Write scores for Sheet4
for row in range(2, ws4.max_row + 1):
symbol = ws4.cell(row=row, column=1).value
if symbol and symbol in scores:
s = scores[symbol]
cell = ws4.cell(row=row, column=max_col4 + 1, value=s.get('overall_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
cell = ws4.cell(row=row, column=max_col4 + 2, value=s.get('momentum_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
cell = ws4.cell(row=row, column=max_col4 + 3, value=s.get('theme_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
cell = ws4.cell(row=row, column=max_col4 + 4, value=s.get('risk_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
cell = ws4.cell(row=row, column=max_col4 + 5, value=s.get('social_buzz_score', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
cell = ws4.cell(row=row, column=max_col4 + 6, value=s.get('brief_analysis', ''))
cell.fill = yellow_fill
cell.font = score_font
cell.alignment = Alignment(wrap_text=True)
cell.border = thin_border
ws4.column_dimensions[ws4.cell(row=1, column=max_col4 + 1).column_letter].width = 15
ws4.column_dimensions[ws4.cell(row=1, column=max_col4 + 2).column_letter].width = 15
ws4.column_dimensions[ws4.cell(row=1, column=max_col4 + 3).column_letter].width = 15
ws4.column_dimensions[ws4.cell(row=1, column=max_col4 + 4).column_letter].width = 15
ws4.column_dimensions[ws4.cell(row=1, column=max_col4 + 5).column_letter].width = 18
ws4.column_dimensions[ws4.cell(row=1, column=max_col4 + 6).column_letter].width = 60
print(f"Updated 'Sheet4' with scores")
# ========== Create a new summary sheet ==========
ws_summary = wb.create_sheet("AI Score Summary", 0)
# Headers
summary_headers = ['Rank', 'Symbol', 'Theme', 'Sector', 'Overall Score', 'Momentum Score',
'Theme Score', 'Risk Score', 'Social Buzz Score', 'Perf YTD', 'Brief Analysis']
for col, header in enumerate(summary_headers, 1):
cell = ws_summary.cell(row=1, column=col, value=header)
cell.fill = header_fill
cell.font = header_font
cell.alignment = Alignment(horizontal='center', wrap_text=True)
cell.border = thin_border
# Sort stocks by overall score
sorted_stocks = sorted(scores.items(), key=lambda x: x[1].get('overall_score', 0), reverse=True)
# Read original data for theme/sector info
df = pd.read_excel(EXCEL_SRC, sheet_name='stock master list')
df4 = pd.read_excel(EXCEL_SRC, sheet_name='Sheet4')
symbol_info = {}
for _, row in df.iterrows():
sym = row['Symbol']
if pd.notna(sym):
symbol_info[sym] = {
'theme': row.get('Theme', ''),
'perf_ytd': row.get('performance Year to Date', None)
}
sector_info = {}
for _, row in df4.iterrows():
sym = row.get('Symbol')
if pd.notna(sym):
sector_info[sym] = row.get('Sector', '')
# Color coding for scores
from openpyxl.styles import PatternFill
def get_score_fill(score):
if score >= 70:
return PatternFill(start_color="92D050", end_color="92D050", fill_type="solid") # Green
elif score >= 50:
return PatternFill(start_color="FFFF00", end_color="FFFF00", fill_type="solid") # Yellow
elif score >= 30:
return PatternFill(start_color="FFC000", end_color="FFC000", fill_type="solid") # Orange
else:
return PatternFill(start_color="FF6B6B", end_color="FF6B6B", fill_type="solid") # Red
for rank, (symbol, s) in enumerate(sorted_stocks, 1):
row = rank + 1
info = symbol_info.get(symbol, {})
ws_summary.cell(row=row, column=1, value=rank).border = thin_border
ws_summary.cell(row=row, column=1).alignment = Alignment(horizontal='center')
ws_summary.cell(row=row, column=2, value=symbol).border = thin_border
ws_summary.cell(row=row, column=2).font = Font(bold=True)
theme = info.get('theme', '')
ws_summary.cell(row=row, column=3, value=theme if pd.notna(theme) else '').border = thin_border
sector = sector_info.get(symbol, '')
ws_summary.cell(row=row, column=4, value=sector if pd.notna(sector) else '').border = thin_border
# Overall Score with color
overall = s.get('overall_score', 0)
cell = ws_summary.cell(row=row, column=5, value=overall)
cell.fill = get_score_fill(overall)
cell.font = Font(bold=True, size=11)
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Momentum Score
momentum = s.get('momentum_score', 0)
cell = ws_summary.cell(row=row, column=6, value=momentum)
cell.fill = get_score_fill(momentum)
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Theme Score
theme_sc = s.get('theme_score', 0)
cell = ws_summary.cell(row=row, column=7, value=theme_sc)
cell.fill = get_score_fill(theme_sc)
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Risk Score (higher = more risky, use inverse coloring)
risk = s.get('risk_score', 0)
cell = ws_summary.cell(row=row, column=8, value=risk)
# For risk, high is bad
if risk >= 70:
cell.fill = PatternFill(start_color="FF6B6B", end_color="FF6B6B", fill_type="solid")
elif risk >= 50:
cell.fill = PatternFill(start_color="FFC000", end_color="FFC000", fill_type="solid")
else:
cell.fill = PatternFill(start_color="92D050", end_color="92D050", fill_type="solid")
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Social Buzz Score
buzz = s.get('social_buzz_score', 0)
cell = ws_summary.cell(row=row, column=9, value=buzz)
cell.fill = get_score_fill(buzz)
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# YTD Performance
ytd = info.get('perf_ytd', None)
if pd.notna(ytd):
cell = ws_summary.cell(row=row, column=10, value=ytd)
cell.number_format = '0.00%'
else:
cell = ws_summary.cell(row=row, column=10, value='N/A')
cell.alignment = Alignment(horizontal='center')
cell.border = thin_border
# Brief Analysis
cell = ws_summary.cell(row=row, column=11, value=s.get('brief_analysis', ''))
cell.alignment = Alignment(wrap_text=True)
cell.border = thin_border
# Set column widths for summary
col_widths = [6, 10, 20, 25, 14, 16, 14, 12, 18, 12, 60]
for i, w in enumerate(col_widths, 1):
ws_summary.column_dimensions[ws_summary.cell(row=1, column=i).column_letter].width = w
# Freeze panes
ws_summary.freeze_panes = 'A2'
print(f"Created 'AI Score Summary' sheet with {len(sorted_stocks)} stocks ranked")
# Save
wb.save(EXCEL_OUT)
print(f"\nSaved scored Excel to: {EXCEL_OUT}")
print("All modified/new cells are highlighted with yellow background")