diff --git a/alpaca_trader/config.json b/alpaca_trader/config.json index f2584b4..dc7715c 100644 --- a/alpaca_trader/config.json +++ b/alpaca_trader/config.json @@ -17,12 +17,12 @@ "ENABLE_SLIPPAGE": true, "SLIPPAGE_PCT": 0.0005, "COMMISSION_PCT": 0.0005, - "MIN_SIGNAL_STRENGTH": 0.5, + "MIN_SIGNAL_STRENGTH": 0.4, "BACKTEST_DAYS": 90, "USE_LIMIT_ORDERS": false, "LIMIT_ORDER_TIMEOUT": 60, "ADX_THRESHOLD": 30, - "VOLUME_MULTIPLIER": 1.0, + "VOLUME_MULTIPLIER": 0.7, "ATR_STOP_MULTIPLIER": 2.5, "MAX_HOLD_TIME": 3600, "REGIME_DETECTION": true, @@ -35,19 +35,19 @@ "VIX_THRESHOLD": 30, "USE_VIX_FILTER": false, "USE_FIBONACCI": false, - "MAX_TRADES_PER_DAY": 1, + "MAX_TRADES_PER_DAY": 3, "SKIP_MONDAYS_FRIDAYS": false, "USE_200_SMA_FILTER": false, - "REQUIRE_MACD_CONFIRMATION": true, + "REQUIRE_MACD_CONFIRMATION": false, "MIN_RISK_REWARD": 2.0, "PULLBACK_PERCENTAGE": 0.382, "ENABLE_SHORT_SELLING": true, - "RSI_BUY_MAX": 55, - "RSI_SELL_MIN": 45, + "RSI_BUY_MAX": 65, + "RSI_SELL_MIN": 35, "RSI_SELL_MAX": 70, "RSI_RANGE_OVERSOLD": 30, "RSI_RANGE_OVERBOUGHT": 70, - "REQUIRE_MA_CROSSOVER": true, + "REQUIRE_MA_CROSSOVER": false, "CROSSOVER_LOOKBACK": 3, "REQUIRE_CASH_ACCOUNT": false, "T1_SETTLEMENT_ENABLED": false, diff --git a/alpaca_trader/engine.py b/alpaca_trader/engine.py index 4e54ae4..1063b1f 100644 --- a/alpaca_trader/engine.py +++ b/alpaca_trader/engine.py @@ -969,10 +969,11 @@ def scale_out_profit_taking(symbol, entry_price, current_price, stop_loss, posit half_qty = int(qty / 2) if half_qty > 0: debug_print(f"Target 1 hit ({target_1_pct:.2f}%), scaling out {half_qty} shares") + exit_price = None if position_type == 'long': - submit_market_sell(symbol, half_qty) + exit_price = submit_market_sell(symbol, half_qty) else: - submit_buy_to_cover(symbol, half_qty) + exit_price = submit_buy_to_cover(symbol, half_qty) position_state.target_1_hit = True logger.info(f"💰 Partial profit @ {profit_pct:.2f}% ({half_qty} shares)") debug_print(f"Partial profit taken: {half_qty} shares @ {profit_pct:.2f}%") @@ -985,15 +986,16 @@ def scale_out_profit_taking(symbol, entry_price, current_price, stop_loss, posit qty = current_position_qty(symbol) if qty != 0: debug_print(f"Target 2 hit ({target_2_pct:.2f}%), closing remaining {qty} shares") + exit_price = None if position_type == 'long': - submit_market_sell(symbol, qty) + exit_price = submit_market_sell(symbol, qty) else: - submit_buy_to_cover(symbol, qty) + exit_price = submit_buy_to_cover(symbol, qty) logger.info(f"💰💰 Full profit @ {profit_pct:.2f}%") debug_print(f"Full profit target hit: closed @ {profit_pct:.2f}%") - return True + return True, exit_price if exit_price else current_price - return False + return False, None def atr_based_trailing_stop(symbol, entry_price, current_price, initial_stop, position_type): debug_print(f"Checking trailing stop: entry=${entry_price:.2f}, current=${current_price:.2f}") @@ -1181,10 +1183,51 @@ def main(): debug_print(f"Max hold time exceeded, closing position") qty = current_position_qty(SYMBOL) if qty != 0: + exit_time = datetime.now(EASTERN) + hold_minutes = time_in_trade / 60 + if position_type == 'long': - submit_market_sell(SYMBOL, qty) + exit_price = submit_market_sell(SYMBOL, qty) + pnl_dollars = (exit_price - entry_price) * qty if exit_price else 0 else: - submit_buy_to_cover(SYMBOL, abs(qty)) + exit_price = submit_buy_to_cover(SYMBOL, abs(qty)) + pnl_dollars = (entry_price - exit_price) * abs(qty) if exit_price else 0 + + pnl_percent = (pnl_dollars / (entry_price * abs(qty)) * 100) if entry_price > 0 and qty != 0 else 0 + + if pnl_dollars > 0: + winners += 1 + elif pnl_dollars < 0: + losers += 1 + + risk_pct = abs((entry_price - stop_loss) / entry_price) if entry_price > 0 else 0 + target_1 = entry_price + (entry_price - stop_loss) * PROFIT_TARGET_1 if position_type == 'long' else entry_price - (stop_loss - entry_price) * PROFIT_TARGET_1 + target_2 = entry_price + (entry_price - stop_loss) * PROFIT_TARGET_2 if position_type == 'long' else entry_price - (stop_loss - entry_price) * PROFIT_TARGET_2 + + log_trade( + entry_time, + exit_time, + SYMBOL, + position_type, + entry_price, + exit_price if exit_price else current_price, + abs(qty), + entry_price * abs(qty), + stop_loss, + target_1, + target_2, + pnl_dollars, + pnl_percent, + hold_minutes, + 'max_hold_time', + entry_regime, + entry_strength, + entry_rsi, + entry_adx, + entry_ma_spread, + 0 + ) + position_active = False trade_count += 1 position_state.reset() @@ -1192,9 +1235,59 @@ def main(): time.sleep(POLL_INTERVAL) continue - if scale_out_profit_taking(SYMBOL, entry_price, current_price, stop_loss, position_type): + target_hit, exit_price_target = scale_out_profit_taking(SYMBOL, entry_price, current_price, stop_loss, position_type) + if target_hit: remaining_qty = current_position_qty(SYMBOL) if remaining_qty == 0: + exit_time = datetime.now(EASTERN) + hold_minutes = (exit_time - entry_time).total_seconds() / 60 if entry_time else 0 + + if position_type == 'long': + pnl_dollars = (exit_price_target - entry_price) * abs(qty) if exit_price_target and qty != 0 else 0 + else: + pnl_dollars = (entry_price - exit_price_target) * abs(qty) if exit_price_target and qty != 0 else 0 + + pnl_percent = (pnl_dollars / (entry_price * abs(qty)) * 100) if entry_price > 0 and qty != 0 else 0 + + if pnl_dollars > 0: + winners += 1 + elif pnl_dollars < 0: + losers += 1 + + risk_pct = abs((entry_price - stop_loss) / entry_price) if entry_price > 0 else 0 + target_1 = entry_price + (entry_price - stop_loss) * PROFIT_TARGET_1 if position_type == 'long' else entry_price - (stop_loss - entry_price) * PROFIT_TARGET_1 + target_2 = entry_price + (entry_price - stop_loss) * PROFIT_TARGET_2 if position_type == 'long' else entry_price - (stop_loss - entry_price) * PROFIT_TARGET_2 + + try: + existing_position = api.get_position(SYMBOL) + original_qty = float(existing_position.qty) + except: + original_qty = qty + + log_trade( + entry_time, + exit_time, + SYMBOL, + position_type, + entry_price, + exit_price_target if exit_price_target else current_price, + abs(original_qty), + entry_price * abs(original_qty), + stop_loss, + target_1, + target_2, + pnl_dollars, + pnl_percent, + hold_minutes, + 'target_2_hit', + entry_regime, + entry_strength, + entry_rsi, + entry_adx, + entry_ma_spread, + 0 + ) + position_active = False position_state.reset() debug_print(f"Sleeping {seconds_to_human_readable(POLL_INTERVAL)} after exit") @@ -1258,12 +1351,6 @@ def main(): time.sleep(POLL_INTERVAL) continue - if trades_today >= MAX_TRADES_PER_DAY: - logger.info(f"📊 Daily limit ({MAX_TRADES_PER_DAY}) - monitoring only") - debug_print(f"Daily trade limit reached ({trades_today}/{MAX_TRADES_PER_DAY})") - time.sleep(POLL_INTERVAL) - continue - signal, strength, signal_stop_loss, signal_position_type = advanced_signal_generator(SYMBOL) bars_for_signal = get_recent_bars(SYMBOL, 50) @@ -1284,6 +1371,14 @@ def main(): long_ma = sma(closes, LONG_WINDOW).iloc[-1] signal_ma_spread = short_ma - long_ma + if trades_today >= MAX_TRADES_PER_DAY: + if signal in ['buy', 'sell'] and strength > 0: + log_missed_signal(datetime.now(EASTERN), signal, 'max_trades_per_day', current_price, SYMBOL, strength, signal_rsi, signal_adx, regime) + logger.info(f"📊 Daily limit ({MAX_TRADES_PER_DAY}) - monitoring only") + debug_print(f"Daily trade limit reached ({trades_today}/{MAX_TRADES_PER_DAY})") + time.sleep(POLL_INTERVAL) + continue + if signal == 'sell' and not ENABLE_SHORT_SELLING: debug_print("Short selling disabled, ignoring sell signal") if signal and strength > 0: @@ -1427,6 +1522,56 @@ def main(): logger.info("🔚 Session ending...") debug_print("Session ending, closing all positions...") + + if position_active and entry_time: + exit_time = datetime.now(EASTERN) + hold_minutes = (exit_time - entry_time).total_seconds() / 60 + qty = current_position_qty(SYMBOL) + + if qty != 0: + bars_eod = get_recent_bars(SYMBOL, 10) + exit_price = bars_eod['close'].iloc[-1] if bars_eod is not None and len(bars_eod) > 0 else current_price + + if position_type == 'long': + pnl_dollars = (exit_price - entry_price) * qty + else: + pnl_dollars = (entry_price - exit_price) * abs(qty) + + pnl_percent = (pnl_dollars / (entry_price * abs(qty)) * 100) if entry_price > 0 and qty != 0 else 0 + + if pnl_dollars > 0: + winners += 1 + elif pnl_dollars < 0: + losers += 1 + + risk_pct = abs((entry_price - stop_loss) / entry_price) if entry_price > 0 else 0 + target_1 = entry_price + (entry_price - stop_loss) * PROFIT_TARGET_1 if position_type == 'long' else entry_price - (stop_loss - entry_price) * PROFIT_TARGET_1 + target_2 = entry_price + (entry_price - stop_loss) * PROFIT_TARGET_2 if position_type == 'long' else entry_price - (stop_loss - entry_price) * PROFIT_TARGET_2 + + log_trade( + entry_time, + exit_time, + SYMBOL, + position_type, + entry_price, + exit_price, + abs(qty), + entry_price * abs(qty), + stop_loss, + target_1, + target_2, + pnl_dollars, + pnl_percent, + hold_minutes, + 'eod_close', + entry_regime, + entry_strength, + entry_rsi, + entry_adx, + entry_ma_spread, + 0 + ) + close_all_positions() final_equity = fetch_equity()