Commit ·
99a1635
1
Parent(s): 7c0742a
Also replace stop/target when they are >20 percent from entry or degenerate
Browse filesBTC-USD rendered with stop_loss=$79 (real: $80k, LLM dropped the 'k')
and target=entry. The drift check only looked at entry, so those
fields passed through untouched.
Now we also back-fill stop/target if either is more than 20 percent
from entry or within 0.5 percent of entry (the degenerate HOLD case
where the model conflates HOLD with 'no stop, no target').
- crew/crew.py +41 -2
crew/crew.py
CHANGED
|
@@ -291,8 +291,47 @@ class FinAgentCrew:
|
|
| 291 |
)
|
| 292 |
|
| 293 |
# Parsed entry is close to live price — keep the LLM signal and
|
| 294 |
-
#
|
| 295 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 296 |
|
| 297 |
@staticmethod
|
| 298 |
def _backfill_missing_prices(signal: TradingSignal) -> TradingSignal:
|
|
|
|
| 291 |
)
|
| 292 |
|
| 293 |
# Parsed entry is close to live price — keep the LLM signal and
|
| 294 |
+
# back-fill any missing stop/target fields, plus swap out any
|
| 295 |
+
# stop/target that are wildly off (more than 20 % from entry).
|
| 296 |
+
# Small LLMs occasionally emit numbers that are credible-looking
|
| 297 |
+
# on their own but nonsensical for the ticker (e.g. BTC-USD
|
| 298 |
+
# at \$80 782 with a \$79 stop-loss — the model dropped the "k").
|
| 299 |
+
entry = signal.entry_price
|
| 300 |
+
stop = signal.stop_loss
|
| 301 |
+
target = signal.target_price
|
| 302 |
+
|
| 303 |
+
def _far_from_entry(v: Optional[float]) -> bool:
|
| 304 |
+
if v is None or v <= 0:
|
| 305 |
+
return True
|
| 306 |
+
return abs(v - entry) / entry > 0.20
|
| 307 |
+
|
| 308 |
+
def _too_close_to_entry(v: Optional[float]) -> bool:
|
| 309 |
+
"""True if a stop/target is within 0.5 % of entry — effectively a
|
| 310 |
+
degenerate zero-risk / zero-reward number that the LLM sometimes
|
| 311 |
+
emits when it conflates HOLD with 'no action'."""
|
| 312 |
+
if v is None or v <= 0:
|
| 313 |
+
return True
|
| 314 |
+
return abs(v - entry) / entry < 0.005
|
| 315 |
+
|
| 316 |
+
if _far_from_entry(stop) or _too_close_to_entry(stop):
|
| 317 |
+
stop = None # trigger back-fill below
|
| 318 |
+
if _far_from_entry(target) or _too_close_to_entry(target):
|
| 319 |
+
target = None
|
| 320 |
+
|
| 321 |
+
if stop is None or target is None:
|
| 322 |
+
return self._backfill_missing_prices(
|
| 323 |
+
TradingSignal(
|
| 324 |
+
ticker=signal.ticker,
|
| 325 |
+
action=signal.action,
|
| 326 |
+
confidence=signal.confidence,
|
| 327 |
+
entry_price=entry,
|
| 328 |
+
stop_loss=stop,
|
| 329 |
+
target_price=target,
|
| 330 |
+
reasoning=signal.reasoning,
|
| 331 |
+
)
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
return signal
|
| 335 |
|
| 336 |
@staticmethod
|
| 337 |
def _backfill_missing_prices(signal: TradingSignal) -> TradingSignal:
|