Spaces:
Sleeping
Sleeping
Commit ·
73d6dae
1
Parent(s): bfab9f9
fix: Kelly per-verdict caps, more noise words, higher token limit, Pydantic warning
Browse files- Cap Kelly per verdict: STRONG BUY 25%, BUY 15%, WATCH 5% (was flat 25%)
- Cap raw Kelly fraction at 0.5 to prevent extreme values from small avg_loss
- Add THING, TXTW, MRC, HERE and 20+ more common words to noise filter
- Increase structured output max_tokens from 8192 to 16384
- Fix Pydantic warning filter to match by module instead of message string
Made-with: Cursor
- src/agent.py +1 -1
- src/core/ticker_utils.py +4 -0
- src/llm.py +1 -1
- src/models/kelly.py +9 -2
- src/whale_hunter.py +1 -1
src/agent.py
CHANGED
|
@@ -375,7 +375,7 @@ def analyst_node(state):
|
|
| 375 |
|
| 376 |
structured_llm = get_structured_llm().with_structured_output(InvestmentVerdict)
|
| 377 |
with warnings.catch_warnings():
|
| 378 |
-
warnings.filterwarnings("ignore",
|
| 379 |
result = structured_llm.invoke(prompt)
|
| 380 |
|
| 381 |
stats = get_kelly_stats()
|
|
|
|
| 375 |
|
| 376 |
structured_llm = get_structured_llm().with_structured_output(InvestmentVerdict)
|
| 377 |
with warnings.catch_warnings():
|
| 378 |
+
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic")
|
| 379 |
result = structured_llm.invoke(prompt)
|
| 380 |
|
| 381 |
stats = get_kelly_stats()
|
src/core/ticker_utils.py
CHANGED
|
@@ -42,6 +42,10 @@ NOISE_WORDS = frozenset({
|
|
| 42 |
"SPAC", "NBER", "OPEC", "MSCI", "EMEA", "APAC", "OECD", "FIFO",
|
| 43 |
"FINRA", "SIPC", "FDIC", "LISA", "ISA", "ATM", "AMA", "FDA",
|
| 44 |
"PHNX", "IPG", "GAAP", "IFRS", "FASB", "IASB", "PCAOB",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 45 |
})
|
| 46 |
|
| 47 |
_MAX_TICKER_LEN = 8 # longest valid ticker with suffix: e.g. CHE.UN.TO
|
|
|
|
| 42 |
"SPAC", "NBER", "OPEC", "MSCI", "EMEA", "APAC", "OECD", "FIFO",
|
| 43 |
"FINRA", "SIPC", "FDIC", "LISA", "ISA", "ATM", "AMA", "FDA",
|
| 44 |
"PHNX", "IPG", "GAAP", "IFRS", "FASB", "IASB", "PCAOB",
|
| 45 |
+
"THING", "TXTW", "MRC", "HERE", "ELSE", "SURE", "WORK",
|
| 46 |
+
"SAFE", "IDEA", "PLAN", "RULE", "STEP", "PLAY", "OPEN",
|
| 47 |
+
"PART", "NOTE", "LINE", "READ", "FILL", "SIZE", "WIDE",
|
| 48 |
+
"SIGN", "RISE", "LEAD", "PUSH", "PULL", "DROP", "JUMP",
|
| 49 |
})
|
| 50 |
|
| 51 |
_MAX_TICKER_LEN = 8 # longest valid ticker with suffix: e.g. CHE.UN.TO
|
src/llm.py
CHANGED
|
@@ -48,7 +48,7 @@ def get_llm() -> ChatOpenAI:
|
|
| 48 |
return _llm_instance
|
| 49 |
|
| 50 |
|
| 51 |
-
def get_structured_llm(max_tokens: int =
|
| 52 |
"""Return an LLM instance configured for structured output.
|
| 53 |
|
| 54 |
Uses a capped ``max_tokens`` to prevent reasoning models from
|
|
|
|
| 48 |
return _llm_instance
|
| 49 |
|
| 50 |
|
| 51 |
+
def get_structured_llm(max_tokens: int = 16384) -> ChatOpenAI:
|
| 52 |
"""Return an LLM instance configured for structured output.
|
| 53 |
|
| 54 |
Uses a capped ``max_tokens`` to prevent reasoning models from
|
src/models/kelly.py
CHANGED
|
@@ -34,6 +34,12 @@ _VERDICT_SCALE = {
|
|
| 34 |
}
|
| 35 |
_POS_FLOOR = 1.0
|
| 36 |
_POS_CAP = 25.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
|
| 39 |
@dataclass
|
|
@@ -160,7 +166,7 @@ def get_kelly_stats() -> KellyStats:
|
|
| 160 |
else:
|
| 161 |
kelly = 0.0
|
| 162 |
|
| 163 |
-
kelly = max(kelly, 0.0)
|
| 164 |
|
| 165 |
result = KellyStats(
|
| 166 |
total_trades=total,
|
|
@@ -201,5 +207,6 @@ def calculate_position_size(stats: KellyStats, verdict: str) -> float:
|
|
| 201 |
return 0.0
|
| 202 |
|
| 203 |
raw = stats.half_kelly * scale * 100
|
|
|
|
| 204 |
|
| 205 |
-
return round(max(_POS_FLOOR, min(raw,
|
|
|
|
| 34 |
}
|
| 35 |
_POS_FLOOR = 1.0
|
| 36 |
_POS_CAP = 25.0
|
| 37 |
+
_VERDICT_CAPS = {
|
| 38 |
+
"STRONG BUY": 25.0,
|
| 39 |
+
"BUY": 15.0,
|
| 40 |
+
"WATCH": 5.0,
|
| 41 |
+
}
|
| 42 |
+
_MAX_KELLY_FRACTION = 0.5 # cap raw Kelly to avoid extreme values
|
| 43 |
|
| 44 |
|
| 45 |
@dataclass
|
|
|
|
| 166 |
else:
|
| 167 |
kelly = 0.0
|
| 168 |
|
| 169 |
+
kelly = max(min(kelly, _MAX_KELLY_FRACTION), 0.0)
|
| 170 |
|
| 171 |
result = KellyStats(
|
| 172 |
total_trades=total,
|
|
|
|
| 207 |
return 0.0
|
| 208 |
|
| 209 |
raw = stats.half_kelly * scale * 100
|
| 210 |
+
cap = _VERDICT_CAPS.get(verdict, _POS_CAP)
|
| 211 |
|
| 212 |
+
return round(max(_POS_FLOOR, min(raw, cap)), 1)
|
src/whale_hunter.py
CHANGED
|
@@ -313,7 +313,7 @@ def analyst_node(state):
|
|
| 313 |
|
| 314 |
structured_llm = get_structured_llm().with_structured_output(InvestmentVerdict)
|
| 315 |
with warnings.catch_warnings():
|
| 316 |
-
warnings.filterwarnings("ignore",
|
| 317 |
result = structured_llm.invoke(prompt)
|
| 318 |
|
| 319 |
stats = get_kelly_stats()
|
|
|
|
| 313 |
|
| 314 |
structured_llm = get_structured_llm().with_structured_output(InvestmentVerdict)
|
| 315 |
with warnings.catch_warnings():
|
| 316 |
+
warnings.filterwarnings("ignore", category=UserWarning, module="pydantic")
|
| 317 |
result = structured_llm.invoke(prompt)
|
| 318 |
|
| 319 |
stats = get_kelly_stats()
|