emmanuelakbi commited on
Commit
99a1635
·
1 Parent(s): 7c0742a

Also replace stop/target when they are >20 percent from entry or degenerate

Browse files

BTC-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').

Files changed (1) hide show
  1. 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
- # just back-fill any missing stop/target fields.
295
- return self._backfill_missing_prices(signal)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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: