File size: 29,838 Bytes
1ccf652
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""

🦈πŸ”ͺ ORCA KILL EXECUTION - HUNT β†’ KILL β†’ PROFIT πŸ”ͺ🦈

═══════════════════════════════════════════════════════════════════════════════



"The Orca doesn't just find prey - it KILLS and FEEDS."



This module handles the actual execution:

  1. HUNT  β†’ Find the target (done by global scanner)

  2. KILL  β†’ Execute the trade (BUY or SELL)

  3. WATCH β†’ Monitor for profit target

  4. FEAST β†’ Close position and realize profit



EXCHANGES SUPPORTED:

  πŸ™ KRAKEN: Full trading (TUSD/USD available)

  πŸ¦™ ALPACA: Full trading (USD available)



Gary Leckey | Orca Kills Now | January 2026

═══════════════════════════════════════════════════════════════════════════════

"""

from aureon.core.aureon_baton_link import link_system as _baton_link; _baton_link(__name__)
import sys
import os
import time
import json
import logging
from dataclasses import dataclass, field, asdict
from typing import Dict, List, Optional, Any, Tuple
from datetime import datetime, timezone

logger = logging.getLogger(__name__)

# Position tracking file
POSITION_FILE = "orca_active_positions.json"


@dataclass
class OrcaPosition:
    """An active position held by the Orca."""
    id: str
    symbol: str
    exchange: str
    side: str  # 'long' or 'short'
    
    # Entry details
    entry_price: float
    entry_qty: float
    entry_cost: float  # Total cost including fees
    entry_time: float
    entry_order_id: str
    
    # Target/Stop
    take_profit_pct: float = 5.0   # Default 5% profit target
    stop_loss_pct: float = -3.0    # Default -3% stop loss
    
    # Current state
    current_price: float = 0.0
    unrealized_pnl: float = 0.0
    unrealized_pnl_pct: float = 0.0
    
    # Exit details (when closed)
    exit_price: float = 0.0
    exit_qty: float = 0.0
    exit_cost: float = 0.0
    exit_time: float = 0.0
    exit_order_id: str = ""
    exit_reason: str = ""
    
    # Final P&L
    realized_pnl: float = 0.0
    status: str = "open"  # 'open', 'closed', 'stopped'
    
    def update_pnl(self, current_price: float):
        """Update unrealized P&L."""
        self.current_price = current_price
        if self.side == 'long':
            self.unrealized_pnl = (current_price - self.entry_price) * self.entry_qty
            self.unrealized_pnl_pct = ((current_price / self.entry_price) - 1) * 100
        else:  # short
            self.unrealized_pnl = (self.entry_price - current_price) * self.entry_qty
            self.unrealized_pnl_pct = ((self.entry_price / current_price) - 1) * 100
    
    def should_take_profit(self) -> bool:
        """Check if we hit profit target."""
        return self.unrealized_pnl_pct >= self.take_profit_pct
    
    def should_stop_loss(self) -> bool:
        """Check if we hit stop loss."""
        return self.unrealized_pnl_pct <= self.stop_loss_pct
    
    def to_dict(self) -> Dict:
        return asdict(self)


class OrcaKillExecutor:
    """

    🦈πŸ”ͺ THE ORCA KILL EXECUTOR

    

    Takes hunt results and executes actual trades:

    - BUY to open long positions

    - SELL to close positions

    - Monitors for profit/stop

    """
    
    def __init__(self, dry_run: bool = False):
        self.dry_run = dry_run
        self.positions: Dict[str, OrcaPosition] = {}
        self.kraken = None
        self.alpaca = None
        
        self._init_exchanges()
        self._load_positions()
        
    def _init_exchanges(self):
        """Initialize exchange connections."""
        # Kraken - ALWAYS get real balance, only use dry_run for orders
        try:
            from aureon.exchanges.kraken_client import get_kraken_client
            self.kraken = get_kraken_client()
            if self.kraken:
                # Store original dry_run state
                original_dry_run = getattr(self.kraken, 'dry_run', False)
                # Temporarily disable dry_run to get REAL balance
                self.kraken.dry_run = False
                balance = self.kraken.get_account_balance()
                # Restore dry_run for order execution
                self.kraken.dry_run = self.dry_run
                
                # Check multiple stablecoins
                tusd = float(balance.get('TUSD', 0))
                usd = float(balance.get('USD', 0))
                usdc = float(balance.get('USDC', 0))
                usdt = float(balance.get('USDT', 0))
                total_stable = tusd + usd + usdc + usdt
                
                self.kraken_balance = balance  # Store full balance
                logger.info(f"πŸ™ Kraken connected: ${tusd:.2f} TUSD, ${usd:.2f} USD, ${usdc:.2f} USDC, ${usdt:.2f} USDT")
                logger.info(f"   Total stable: ${total_stable:.2f} (dry_run orders: {self.dry_run})")
        except Exception as e:
            logger.error(f"Kraken init error: {e}")
            self.kraken_balance = {}
            
        # Alpaca
        try:
            from aureon.exchanges.alpaca_client import AlpacaClient
            self.alpaca = AlpacaClient()
            acct = self.alpaca.get_account()
            cash = float(acct.get('cash', 0))
            logger.info(f"πŸ¦™ Alpaca connected: ${cash:.2f} USD")
        except Exception as e:
            logger.error(f"Alpaca init error: {e}")
    
    def _load_positions(self):
        """Load active positions from file."""
        try:
            if os.path.exists(POSITION_FILE):
                with open(POSITION_FILE, 'r') as f:
                    data = json.load(f)
                    for pos_id, pos_data in data.items():
                        self.positions[pos_id] = OrcaPosition(**pos_data)
                logger.info(f"πŸ“‚ Loaded {len(self.positions)} active positions")
        except Exception as e:
            logger.error(f"Failed to load positions: {e}")
    
    def _save_positions(self):
        """Save active positions to file."""
        try:
            data = {pos_id: pos.to_dict() for pos_id, pos in self.positions.items()}
            with open(POSITION_FILE, 'w') as f:
                json.dump(data, f, indent=2)
        except Exception as e:
            logger.error(f"Failed to save positions: {e}")
    
    def get_balance(self, exchange: str) -> Tuple[str, float]:
        """Get available balance on exchange."""
        if exchange == 'kraken' and self.kraken:
            # Use cached balance if available, otherwise fetch fresh (with dry_run disabled)
            if hasattr(self, 'kraken_balance') and self.kraken_balance:
                balance = self.kraken_balance
            else:
                # Temporarily disable dry_run to get REAL balance
                original_dry_run = getattr(self.kraken, 'dry_run', False)
                self.kraken.dry_run = False
                balance = self.kraken.get_account_balance()
                self.kraken.dry_run = original_dry_run
                self.kraken_balance = balance
            
            # Check for TUSD (primary), then USD, then USDC, then USDT
            for asset in ['TUSD', 'USD', 'ZUSD', 'USDC', 'USDT']:
                amt = float(balance.get(asset, 0))
                if amt > 0.5:  # At least $0.50
                    return asset, amt
            return 'TUSD', 0.0
        elif exchange == 'alpaca' and self.alpaca:
            acct = self.alpaca.get_account()
            return 'USD', float(acct.get('cash', 0))
        return 'USD', 0.0
    
    def get_price(self, symbol: str, exchange: str) -> float:
        """Get current price for symbol."""
        try:
            if exchange == 'kraken' and self.kraken:
                # Normalize symbol (ME/USD -> MEUSD)
                kraken_sym = symbol.replace('/', '')
                price_info = self.kraken.best_price(kraken_sym)
                return float(price_info.get('price', 0))
            elif exchange == 'alpaca' and self.alpaca:
                quote = self.alpaca.get_crypto_quote(symbol.replace('/', ''))
                return float(quote.get('ap', 0) or quote.get('ask', 0))
        except Exception as e:
            logger.error(f"Price fetch error for {symbol}: {e}")
        return 0.0

    def _normalize_symbol(self, symbol: str) -> str:
        """Normalize symbol formats into BASE/QUOTE convention."""
        if not isinstance(symbol, str):
            return ""
        cleaned = symbol.strip().upper()
        if "/" in cleaned:
            return cleaned
        # Common fallback: BTCUSD -> BTC/USD
        if len(cleaned) > 3:
            if cleaned.endswith("USD"):
                return f"{cleaned[:-3]}/USD"
            if cleaned.endswith("USDT"):
                return f"{cleaned[:-4]}/USDT"
            if cleaned.endswith("USDC"):
                return f"{cleaned[:-4]}/USDC"
        return cleaned

    def validate_ticker(self, symbol: str, exchange: str) -> Dict[str, Any]:
        """Validate ticker syntax and market availability on the selected exchange."""
        normalized = self._normalize_symbol(symbol)
        if "/" not in normalized:
            return {
                "valid": False,
                "symbol": normalized,
                "reason": "Ticker must be in BASE/QUOTE format (example: ETH/USD)",
            }

        price = self.get_price(normalized, exchange)
        if price <= 0:
            return {
                "valid": False,
                "symbol": normalized,
                "reason": f"Ticker unavailable or not priced on {exchange}",
            }

        return {
            "valid": True,
            "symbol": normalized,
            "exchange": exchange,
            "price": price,
        }

    def select_trade_candidate(self, tickers: List[str], exchange: str) -> Dict[str, Any]:
        """

        Validate a list of tickers and select the highest-priced valid candidate.

        Selection policy is intentionally deterministic for auditability.

        """
        if not tickers:
            return {"selected": None, "validations": [], "reason": "No tickers provided"}

        validations = [self.validate_ticker(ticker, exchange) for ticker in tickers]
        valid = [v for v in validations if v.get("valid")]
        if not valid:
            return {"selected": None, "validations": validations, "reason": "No valid tickers"}

        selected = sorted(valid, key=lambda item: item.get("price", 0.0), reverse=True)[0]
        return {"selected": selected, "validations": validations}

    def run_orca_kill_cycle(

        self,

        tickers: List[str],

        exchange: str,

        side: str = "buy",

        amount_usd: Optional[float] = None,

        take_profit_pct: float = 5.0,

        stop_loss_pct: float = -3.0,

        monitor_cycles: int = 30,

        poll_seconds: float = 1.0,

    ) -> Dict[str, Any]:
        """

        Execute the complete Orca cycle:

        selection/validation -> trade -> monitor -> profitable close.

        """
        selection = self.select_trade_candidate(tickers=tickers, exchange=exchange)
        selected = selection.get("selected")
        if not selected:
            return {
                "success": False,
                "phase": "selection",
                "reason": selection.get("reason", "No valid selection"),
                "selection": selection,
            }

        position = self.execute_kill(
            symbol=selected["symbol"],
            exchange=exchange,
            side=side,
            amount_usd=amount_usd,
            take_profit_pct=take_profit_pct,
            stop_loss_pct=stop_loss_pct,
        )
        if not position:
            return {
                "success": False,
                "phase": "execution",
                "reason": "Failed to execute kill",
                "selection": selection,
            }

        closed_positions: List[OrcaPosition] = []
        for _ in range(max(1, monitor_cycles)):
            closed_now = self.monitor_positions()
            if closed_now:
                closed_positions.extend(closed_now)
                break
            if poll_seconds > 0:
                time.sleep(poll_seconds)

        closed_position = next((p for p in closed_positions if p.id == position.id), None)
        if not closed_position:
            return {
                "success": False,
                "phase": "monitor",
                "reason": "Position did not close within monitoring window",
                "selection": selection,
                "position_id": position.id,
                "closed_positions": [p.id for p in closed_positions],
            }

        profitable = closed_position.realized_pnl > 0
        return {
            "success": profitable,
            "phase": "closed",
            "selection": selection,
            "position_id": closed_position.id,
            "symbol": closed_position.symbol,
            "exit_reason": closed_position.exit_reason,
            "realized_pnl": closed_position.realized_pnl,
            "profitable_close": profitable,
        }
    
    def execute_kill(self, symbol: str, exchange: str, side: str, 

                     amount_usd: Optional[float] = None,

                     take_profit_pct: float = 5.0,

                     stop_loss_pct: float = -3.0) -> Optional[OrcaPosition]:
        """

        πŸ”ͺ EXECUTE THE KILL

        

        Args:

            symbol: Trading pair (e.g., "ME/USD")

            exchange: Exchange to trade on ('kraken' or 'alpaca')

            side: 'buy' for long, 'sell' for short

            amount_usd: Amount in USD to trade (default: 90% of balance)

            take_profit_pct: Profit target %

            stop_loss_pct: Stop loss %

        

        Returns:

            OrcaPosition if successful, None if failed

        """
        print(f"\n🦈πŸ”ͺ ORCA KILL EXECUTION")
        print("=" * 60)
        print(f"   Symbol:   {symbol}")
        print(f"   Exchange: {exchange}")
        print(f"   Side:     {side.upper()}")
        print(f"   TP:       +{take_profit_pct}%")
        print(f"   SL:       {stop_loss_pct}%")
        print(f"   DRY-RUN:  {self.dry_run}")
        print("=" * 60)
        
        # Get balance
        quote_asset, balance = self.get_balance(exchange)
        print(f"\nπŸ’° Available: ${balance:.2f} {quote_asset}")
        
        if balance < 1.0:
            print(f"❌ Insufficient balance on {exchange}")
            return None
        
        # Calculate trade size (90% of balance or specified amount)
        trade_amount = amount_usd or (balance * 0.9)
        trade_amount = min(trade_amount, balance * 0.95)  # Keep 5% buffer
        print(f"πŸ“Š Trade amount: ${trade_amount:.2f}")
        
        # Get current price
        price = self.get_price(symbol, exchange)
        if price <= 0:
            print(f"❌ Could not get price for {symbol}")
            return None
        print(f"πŸ’΅ Current price: ${price:.6f}")
        
        # Calculate quantity
        qty = trade_amount / price
        print(f"πŸ“¦ Quantity: {qty:.6f} {symbol.split('/')[0]}")
        
        # Execute order
        print(f"\nπŸ”ͺ EXECUTING {side.upper()} ORDER...")
        
        order_result = None
        if exchange == 'kraken' and self.kraken:
            try:
                # Kraken needs the pair without slash
                kraken_pair = symbol.replace('/', '')
                
                # If we have TUSD, we need to trade METUSD pair or convert first
                if quote_asset == 'TUSD':
                    # Check if direct TUSD pair exists
                    tusd_pair = symbol.split('/')[0] + 'TUSD'
                    try:
                        self.kraken.best_price(tusd_pair)
                        kraken_pair = tusd_pair
                        print(f"   Using TUSD pair: {kraken_pair}")
                    except:
                        # No TUSD pair, need to convert TUSD->USD first
                        print(f"   ⚠️ No {tusd_pair} pair, using USD pair")
                
                order_result = self.kraken.place_market_order(
                    symbol=kraken_pair,
                    side=side,
                    quote_qty=trade_amount
                )
            except Exception as e:
                print(f"❌ Kraken order failed: {e}")
                return None
                
        elif exchange == 'alpaca' and self.alpaca:
            try:
                # Alpaca crypto trading
                order_result = self.alpaca.submit_order(
                    symbol=symbol.replace('/', ''),
                    qty=None,  # Use notional
                    side=side,
                    type='market',
                    time_in_force='gtc',
                    notional=trade_amount
                )
            except Exception as e:
                print(f"❌ Alpaca order failed: {e}")
                return None
        
        if not order_result:
            print(f"❌ Order execution failed")
            return None
        
        # Check for errors
        if 'error' in order_result:
            print(f"❌ Order error: {order_result}")
            return None
        
        # Parse order result
        order_id = order_result.get('orderId', order_result.get('id', str(time.time())))
        exec_price = float(
            order_result.get('price')
            or order_result.get('avg_price')
            or order_result.get('avgPrice')
            or price
        )
        exec_qty = float(
            order_result.get('executedQty')
            or order_result.get('vol_exec')
            or order_result.get('filled_qty')
            or qty
        )
        exec_cost = float(
            order_result.get('cummulativeQuoteQty')
            or order_result.get('cost')
            or order_result.get('filled_notional')
            or (exec_price * exec_qty)
        )
        
        print(f"\nβœ… ORDER FILLED!")
        print(f"   Order ID: {order_id}")
        print(f"   Price:    ${exec_price:.6f}")
        print(f"   Quantity: {exec_qty:.6f}")
        print(f"   Cost:     ${exec_cost:.2f}")
        
        # Create position
        pos_id = f"{exchange}_{symbol.replace('/', '')}_{int(time.time())}"
        position = OrcaPosition(
            id=pos_id,
            symbol=symbol,
            exchange=exchange,
            side='long' if side == 'buy' else 'short',
            entry_price=exec_price,
            entry_qty=exec_qty,
            entry_cost=exec_cost,
            entry_time=time.time(),
            entry_order_id=order_id,
            take_profit_pct=take_profit_pct,
            stop_loss_pct=stop_loss_pct,
            current_price=exec_price,
            status='open'
        )
        
        # Save position
        self.positions[pos_id] = position
        self._save_positions()
        
        print(f"\nπŸ“ Position opened: {pos_id}")
        print(f"   Target: +{take_profit_pct}% (${exec_price * (1 + take_profit_pct/100):.6f})")
        print(f"   Stop:   {stop_loss_pct}% (${exec_price * (1 + stop_loss_pct/100):.6f})")
        
        return position
    
    def close_position(self, position: OrcaPosition, reason: str = "manual") -> bool:
        """

        Close an open position.

        """
        if position.status != 'open':
            print(f"⚠️ Position {position.id} already {position.status}")
            return False
        
        print(f"\nπŸ”ͺ CLOSING POSITION: {position.symbol}")
        print(f"   Side: {position.side}")
        print(f"   Entry: ${position.entry_price:.6f}")
        print(f"   Current PnL: {position.unrealized_pnl_pct:+.2f}%")
        print(f"   Reason: {reason}")
        
        # Determine close side
        close_side = 'sell' if position.side == 'long' else 'buy'
        
        # Execute close order
        order_result = None
        if position.exchange == 'kraken' and self.kraken:
            try:
                kraken_pair = position.symbol.replace('/', '')
                order_result = self.kraken.place_market_order(
                    symbol=kraken_pair,
                    side=close_side,
                    quantity=position.entry_qty
                )
            except Exception as e:
                print(f"❌ Close order failed: {e}")
                return False
                
        elif position.exchange == 'alpaca' and self.alpaca:
            try:
                order_result = self.alpaca.submit_order(
                    symbol=position.symbol.replace('/', ''),
                    qty=position.entry_qty,
                    side=close_side,
                    type='market',
                    time_in_force='gtc'
                )
            except Exception as e:
                print(f"❌ Close order failed: {e}")
                return False
        
        if not order_result or 'error' in order_result:
            print(f"❌ Close failed: {order_result}")
            return False
        
        # Update position with exit details
        exit_price = float(
            order_result.get('price')
            or order_result.get('avg_price')
            or order_result.get('avgPrice')
            or position.current_price
        )
        exit_qty = float(
            order_result.get('executedQty')
            or order_result.get('vol_exec')
            or order_result.get('filled_qty')
            or position.entry_qty
        )
        exit_cost = float(
            order_result.get('cummulativeQuoteQty')
            or order_result.get('cost')
            or order_result.get('filled_notional')
            or (exit_price * exit_qty)
        )
        
        position.exit_price = exit_price
        position.exit_qty = exit_qty
        position.exit_cost = exit_cost
        position.exit_time = time.time()
        position.exit_order_id = order_result.get('orderId', '')
        position.exit_reason = reason
        
        # Calculate realized P&L
        if position.side == 'long':
            position.realized_pnl = exit_cost - position.entry_cost
        else:
            position.realized_pnl = position.entry_cost - exit_cost
        
        position.status = 'closed'
        self._save_positions()
        
        pnl_emoji = "🟒" if position.realized_pnl > 0 else "πŸ”΄"
        print(f"\n{pnl_emoji} POSITION CLOSED!")
        print(f"   Exit Price: ${exit_price:.6f}")
        print(f"   Realized P&L: ${position.realized_pnl:.4f}")
        
        return True
    
    def monitor_positions(self) -> List[OrcaPosition]:
        """

        Monitor all open positions and close if hit TP/SL.

        Returns list of positions that were closed.

        """
        closed = []
        
        for pos_id, position in list(self.positions.items()):
            if position.status != 'open':
                continue
            
            # Update current price
            price = self.get_price(position.symbol, position.exchange)
            if price <= 0:
                continue
            
            position.update_pnl(price)
            
            # Check take profit
            if position.should_take_profit():
                print(f"\n🎯 TAKE PROFIT HIT: {position.symbol} +{position.unrealized_pnl_pct:.2f}%")
                if self.close_position(position, reason="take_profit"):
                    closed.append(position)
                continue
            
            # Check stop loss
            if position.should_stop_loss():
                print(f"\nπŸ›‘ STOP LOSS HIT: {position.symbol} {position.unrealized_pnl_pct:.2f}%")
                if self.close_position(position, reason="stop_loss"):
                    closed.append(position)
                continue
            
            # Print status
            pnl_emoji = "🟒" if position.unrealized_pnl >= 0 else "πŸ”΄"
            print(f"{pnl_emoji} {position.symbol}: ${price:.6f} ({position.unrealized_pnl_pct:+.2f}%)")
        
        return closed
    
    def get_open_positions(self) -> List[OrcaPosition]:
        """Get all open positions."""
        return [p for p in self.positions.values() if p.status == 'open']
    
    def status(self):
        """Print current status."""
        print("\n" + "=" * 60)
        print("🦈 ORCA KILL EXECUTOR STATUS")
        print("=" * 60)
        
        # Exchange balances
        kraken_asset, kraken_bal = self.get_balance('kraken')
        alpaca_asset, alpaca_bal = self.get_balance('alpaca')
        print(f"\nπŸ’° BALANCES:")
        print(f"   πŸ™ Kraken: ${kraken_bal:.2f} {kraken_asset}")
        print(f"   πŸ¦™ Alpaca: ${alpaca_bal:.2f} {alpaca_asset}")
        
        # Open positions
        open_pos = self.get_open_positions()
        print(f"\nπŸ“ OPEN POSITIONS: {len(open_pos)}")
        
        total_pnl = 0.0
        for pos in open_pos:
            price = self.get_price(pos.symbol, pos.exchange)
            if price > 0:
                pos.update_pnl(price)
            pnl_emoji = "🟒" if pos.unrealized_pnl >= 0 else "πŸ”΄"
            print(f"   {pnl_emoji} {pos.symbol} ({pos.exchange})")
            print(f"      Entry: ${pos.entry_price:.6f}")
            print(f"      Current: ${pos.current_price:.6f}")
            print(f"      P&L: {pos.unrealized_pnl_pct:+.2f}% (${pos.unrealized_pnl:+.4f})")
            total_pnl += pos.unrealized_pnl
        
        if open_pos:
            print(f"\n   πŸ’΅ Total Unrealized: ${total_pnl:+.4f}")
        
        # Closed positions (recent)
        closed_pos = [p for p in self.positions.values() if p.status == 'closed']
        if closed_pos:
            print(f"\nπŸ“Š RECENT CLOSED: {len(closed_pos)}")
            for pos in closed_pos[-5:]:  # Last 5
                pnl_emoji = "🟒" if pos.realized_pnl > 0 else "πŸ”΄"
                print(f"   {pnl_emoji} {pos.symbol}: ${pos.realized_pnl:+.4f} ({pos.exit_reason})")
        
        print("=" * 60)


# ═══════════════════════════════════════════════════════════════════════════════
# 🦈 QUICK KILL FUNCTIONS
# ═══════════════════════════════════════════════════════════════════════════════

def orca_execute_kill(symbol: str, exchange: str, side: str, 

                      amount_usd: float = None, dry_run: bool = False):
    """πŸ”ͺ Execute a kill (trade)."""
    executor = OrcaKillExecutor(dry_run=dry_run)
    return executor.execute_kill(symbol, exchange, side, amount_usd)

def orca_hunt_and_kill(dry_run: bool = False):
    """🦈 Run full hunt cycle and execute best kill."""
    from aureon.scanners.aureon_live_momentum_hunter import LiveMomentumHunter
    
    # Hunt
    hunter = LiveMomentumHunter(dry_run=dry_run)
    results = hunter.hunt()
    
    if not results:
        print("\n⏳ No kills found - Orca is patient")
        return None
    
    # Execute best kill
    best = results[0]
    executor = OrcaKillExecutor(dry_run=dry_run)
    
    # Determine exchange - check which one has the symbol
    # For now, check if it came from global scan (likely Kraken)
    exchange = 'alpaca'
    if best.nexus_factors.get('extreme_momentum'):
        # Came from global scanner, likely Kraken
        exchange = 'kraken'
    
    position = executor.execute_kill(
        symbol=best.symbol,
        exchange=exchange,
        side=best.side,
        take_profit_pct=5.0,
        stop_loss_pct=-3.0
    )
    
    return position

def orca_monitor():
    """πŸ‘€ Monitor open positions."""
    executor = OrcaKillExecutor(dry_run=False)  # Always real for monitoring
    return executor.monitor_positions()

def orca_status():
    """πŸ“Š Show executor status."""
    executor = OrcaKillExecutor(dry_run=False)
    executor.status()


if __name__ == "__main__":
    import sys
    
    if len(sys.argv) > 1:
        cmd = sys.argv[1]
        if cmd == 'status':
            orca_status()
        elif cmd == 'monitor':
            orca_monitor()
        elif cmd == 'hunt':
            # Dry run hunt and kill
            orca_hunt_and_kill(dry_run=False)
        elif cmd == 'hunt-live':
            # LIVE hunt and kill
            print("⚠️ LIVE MODE - Real trades will be executed!")
            confirm = input("Type 'KILL' to confirm: ")
            if confirm == 'KILL':
                orca_hunt_and_kill(dry_run=False)
        else:
            print(f"Unknown command: {cmd}")
            print("Usage: python orca_kill_executor.py [status|monitor|hunt|hunt-live]")
    else:
        # Default: show status
        orca_status()