diff --git a/bots/live_signal_trader.py b/bots/live_signal_trader.py index 0949f1b8..d255ccd2 100644 --- a/bots/live_signal_trader.py +++ b/bots/live_signal_trader.py @@ -844,12 +844,35 @@ class LiveSignalTrader: n_tranches = min(n_desired_tranches, max_tranches) return total_lot, n_tranches + MAX_ENTRY_DEVIATION = 0.02 # 2 % vom Live-Kurs → Parse-Fehler + def _resolve_missing_fields(self, signal): """Same default-fill logic as the backtester's _resolve_informal_signals, just resolved against a live price - instead of historical bars.""" + instead of historical bars. + + Zusätzlich: ein geparster Einstieg, der weit vom Live-Kurs entfernt + ist (z.B. 2077.75 statt 4127 durch "4126.5-29"), wird verworfen und + durch den Live-Kurs ersetzt. SL/TP, die dann auf der falschen Seite + oder unplausibel weit weg liegen, werden neu aus dem Default-Plan + gebildet bzw. das Signal wird übersprungen.""" + live = self._get_live_price(signal['pair'], signal['action']) + if signal['entry'] is not None and live: + dev = abs(signal['entry'] - live) / live + if dev > self.MAX_ENTRY_DEVIATION: + logger.warning(f"[{self.channel_name}] Entry {signal['entry']} weicht {dev:.0%} vom " + f"Live-Kurs {live} ab – Parse-Fehler, verwende Live-Kurs") + signal['entry'] = live + signal['tp_levels'] = [t for t in signal['tp_levels'] + if abs(t - live) / live <= self.MAX_ENTRY_DEVIATION] + if signal['entry'] is not None and signal['sl'] is not None: + e, sl = signal['entry'], signal['sl'] + wrong_side = (signal['action'] == 'BUY' and sl >= e) or (signal['action'] == 'SELL' and sl <= e) + if wrong_side or abs(sl - e) / e > self.MAX_ENTRY_DEVIATION: + logger.warning(f"[{self.channel_name}] SL {sl} unplausibel zu Entry {e} – Signal übersprungen") + return False if signal['entry'] is None: - price = self._get_live_price(signal['pair'], signal['action']) + price = live or self._get_live_price(signal['pair'], signal['action']) if price is None: return False signal['entry'] = price @@ -858,6 +881,9 @@ class LiveSignalTrader: if signal['sl'] is None: dist = self.analyzer.default_sl_pips * pip signal['sl'] = signal['entry'] - dist if signal['action'] == 'BUY' else signal['entry'] + dist + signal['tp_levels'] = [t for t in signal['tp_levels'] + if (signal['action'] == 'BUY' and t > signal['entry']) + or (signal['action'] == 'SELL' and t < signal['entry'])] if not signal['tp_levels']: levels = [signal['entry'] + (d * pip if signal['action'] == 'BUY' else -d * pip) for d in self.analyzer.default_tp_ladder_pips] @@ -968,8 +994,10 @@ class LiveSignalTrader: resp.raise_for_status() positions = resp.json().get('positions', []) ticket_set = set(int(t) for t in tickets) - fills = [float(p['price_open']) for p in positions - if int(p.get('ticket', -1)) in ticket_set and p.get('price_open')] + fills = [float(p.get('open_price') or p.get('price_open')) + for p in positions + if int(p.get('ticket', -1)) in ticket_set + and (p.get('open_price') or p.get('price_open'))] if fills: avg = sum(fills) / len(fills) logger.info(f"[LIVE] fill prices from /positions: {fills} → avg {avg:.2f}") @@ -1149,7 +1177,7 @@ def main(): "Eliminates auto-generated SL/TP guesses but may miss fast-moving entries.") args = parser.parse_args() - if args.channel_id is None: + if args.channel_id is None and args.channels in ('ch1', 'both') and not args.channel2_id: logger.error("No channel ID given - pass --channel-id or set CHANNEL_ID in .env") sys.exit(1) if not (API_ID and API_HASH and PHONE_NUMBER): @@ -1172,7 +1200,7 @@ def main(): logger.info("LIVE mode auto-confirmed (non-interactive / nohup)") # Resolve which channels to actually activate - run_ch1 = args.channels in ('ch1', 'both') + run_ch1 = args.channels in ('ch1', 'both') and args.channel_id is not None run_ch2 = args.channels in ('ch2', 'both') and bool(args.channel2_id) if args.channels in ('ch2', 'both') and not args.channel2_id: @@ -1230,7 +1258,7 @@ def main(): f"using seed capital {args.initial_capital:.2f} USDT for sizing until the " f"first signal fires (will retry before each trade)") - client = TelegramClient('mysession', API_ID, API_HASH) + client = TelegramClient(os.getenv('TG_SESSION', 'mysession'), API_ID, API_HASH) # --- Event handlers -------------------------------------------------------- if trader is not None: