Compare commits

...
10 Commits
+344 -84
View File
@@ -12,6 +12,7 @@ import json
import pandas as pd
import numpy as np
from datetime import datetime, timedelta
import pytz
from pathlib import Path
from dotenv import load_dotenv
import alpaca_trade_api as tradeapi
@@ -28,41 +29,42 @@ DEFAULT_CONFIG = {
"SHORT_WINDOW": 20,
"LONG_WINDOW": 50,
"MIN_NOTIONAL": 1.0,
"POLL_INTERVAL": 1800,
"POLL_INTERVAL": 120,
"MAX_DRAWDOWN": 0.12,
"PDT_RULE": True,
"USE_TRAILING_STOP": True,
"PROFIT_TARGET_1": 1.5,
"PROFIT_TARGET_2": 3.0,
"VOLATILITY_ADJUSTMENT": True,
"MARKET_HOURS_FILTER": True,
"MARKET_HOURS_FILTER": False,
"ENABLE_SLIPPAGE": True,
"SLIPPAGE_PCT": 0.0005,
"COMMISSION_PCT": 0.0005,
"MIN_SIGNAL_STRENGTH": 0.85,
"MIN_SIGNAL_STRENGTH": 0.50,
"BACKTEST_DAYS": 90,
"USE_LIMIT_ORDERS": True,
"LIMIT_ORDER_TIMEOUT": 60,
"ADX_THRESHOLD": 20,
"VOLUME_MULTIPLIER": 1.2,
"VOLUME_MULTIPLIER": 0.5,
"ATR_STOP_MULTIPLIER": 1.5,
"MAX_HOLD_TIME": 7200,
"REGIME_DETECTION": True,
"MULTIFRAME_FILTER": True,
"MULTIFRAME_FILTER": False,
"BB_WINDOW": 20,
"BB_STD": 2.0,
"USE_EMA": True,
"REQUIRE_CANDLE_PATTERN": True,
"USE_PIVOT_POINTS": True,
"REQUIRE_CANDLE_PATTERN": False,
"USE_PIVOT_POINTS": False,
"VIX_THRESHOLD": 20,
"USE_VIX_FILTER": True,
"USE_FIBONACCI": True,
"MAX_TRADES_PER_DAY": 2,
"SKIP_MONDAYS_FRIDAYS": True,
"USE_200_SMA_FILTER": True,
"REQUIRE_MACD_CONFIRMATION": True,
"MIN_RISK_REWARD": 2.0,
"PULLBACK_PERCENTAGE": 0.382
"USE_FIBONACCI": False,
"MAX_TRADES_PER_DAY": 1000,
"SKIP_MONDAYS_FRIDAYS": False,
"USE_200_SMA_FILTER": False,
"REQUIRE_MACD_CONFIRMATION": False,
"MIN_RISK_REWARD": 1.5,
"PULLBACK_PERCENTAGE": 0.382,
"ENABLE_SHORT_SELLING": False
}
if ENV_PATH.exists():
@@ -127,6 +129,9 @@ USE_200_SMA_FILTER = bool(config["USE_200_SMA_FILTER"])
REQUIRE_MACD_CONFIRMATION = bool(config["REQUIRE_MACD_CONFIRMATION"])
MIN_RISK_REWARD = float(config["MIN_RISK_REWARD"])
PULLBACK_PERCENTAGE = float(config["PULLBACK_PERCENTAGE"])
ENABLE_SHORT_SELLING = bool(config.get("ENABLE_SHORT_SELLING", False))
EASTERN = pytz.timezone('US/Eastern')
api = tradeapi.REST(
os.getenv('APCA_API_KEY_ID'),
@@ -406,8 +411,8 @@ def get_vix_level():
debug_print(f"VIX from data: {vix:.2f}")
return vix
else:
debug_print("No VIX data, estimating from SPY volatility...")
spy_bars = api.get_bars("SPY", "1Day", limit=20).df
debug_print(f"No VIX data, estimating from {SYMBOL} volatility...")
spy_bars = api.get_bars(SYMBOL, "1Day", limit=20).df
if len(spy_bars) >= 20:
spy_returns = spy_bars['close'].pct_change()
volatility = spy_returns.std() * np.sqrt(252) * 100
@@ -493,7 +498,7 @@ def should_skip_trading_day():
return False
today = datetime.now().weekday()
day_name = datetime.now().strftime("%A")
day_name = datetime.now(EASTERN).strftime("%A")
if today == 0 or today == 4:
debug_print(f"Skipping {day_name} (skip_mondays_fridays enabled)")
return True
@@ -520,7 +525,17 @@ def seconds_to_human_readable(seconds):
return " ".join(time_parts) if time_parts else "0 seconds"
def format_market_time(dt_obj):
return dt_obj.strftime("%Y-%m-%d %I:%M:%S %p %Z")
if hasattr(dt_obj, 'to_pydatetime'):
dt_obj = dt_obj.to_pydatetime()
if dt_obj.tzinfo is None:
dt_obj = EASTERN.localize(dt_obj)
elif dt_obj.tzinfo != EASTERN:
dt_obj = dt_obj.astimezone(EASTERN)
eastern_time = dt_obj.strftime("%Y-%m-%d %I:%M:%S %p %Z")
local_time = dt_obj.astimezone().strftime("%I:%M%p").lstrip('0')
return f"{eastern_time} ({local_time} local)"
def apply_slippage(price, is_buy=True):
debug_print(f"Applying slippage to price={price:.2f}, is_buy={is_buy}")
@@ -749,7 +764,7 @@ def advanced_signal_generator(symbol):
bars = get_recent_bars(symbol, 100)
if bars is None or len(bars) < 50:
debug_print("Insufficient bars for signal generation")
return None, 0, 0
return None, 0, 0, None
debug_print(f"Received {len(bars)} bars")
@@ -877,7 +892,7 @@ def advanced_signal_generator(symbol):
pullback_ok = True
debug_print(f"Pullback OK: price near short MA")
if pullback_ok and rsi > 45:
if pullback_ok and rsi < 55:
debug_print(f"Pullback and RSI conditions met (RSI={rsi:.2f})")
if hourly_trend in ['bearish', 'neutral']:
debug_print(f"Hourly trend favorable: {hourly_trend}")
@@ -1003,7 +1018,8 @@ def advanced_signal_generator(symbol):
else:
debug_print("=== NO SIGNAL GENERATED ===")
return signal, signal_strength, stop_loss
position_type = 'long' if signal == 'buy' else ('short' if signal == 'sell' else None)
return signal, signal_strength, stop_loss, position_type
def wait_until_market_open():
debug_print("Checking if market is open...")
@@ -1016,11 +1032,21 @@ def wait_until_market_open():
return
now = clock.timestamp
if now.tzinfo is None:
now = EASTERN.localize(now)
else:
now = now.astimezone(EASTERN)
next_open = clock.next_open
if next_open.tzinfo is None:
next_open = EASTERN.localize(next_open)
else:
next_open = next_open.astimezone(EASTERN)
if not clock.is_open:
seconds_until_open = (next_open - now).total_seconds()
debug_print(f"Market closed, {seconds_until_open:.0f} seconds until open")
readable_time = seconds_to_human_readable(seconds_until_open)
debug_print(f"Market closed, {readable_time} until open")
if seconds_until_open > 0:
readable_time = seconds_to_human_readable(seconds_until_open)
logger.info(f"🕒 Market opens at {format_market_time(next_open)}")
@@ -1031,7 +1057,7 @@ def wait_until_market_open():
time.sleep(sleep_time)
seconds_until_open -= sleep_time
if sleep_time >= 60:
if sleep_time >= 60 and (seconds_until_open % 3600 < 60 or seconds_until_open < 3600):
remaining_readable = seconds_to_human_readable(seconds_until_open)
logger.info(f"⏱️ {remaining_readable} remaining...")
debug_print(f"Waiting... {remaining_readable} remaining")
@@ -1169,10 +1195,137 @@ def submit_market_buy(symbol, notional):
debug_print(f"Market buy failed: {e}")
return False
def submit_short_sell(symbol, notional):
"""Open a short position by selling shares we don't own"""
debug_print(f"=== SUBMITTING SHORT SELL (OPENING SHORT POSITION) ===")
debug_print(f"Symbol: {symbol}, Notional: ${notional:.2f}")
try:
current_price = get_current_price(symbol)
if current_price == 0:
debug_print("Short sell failed: could not get current price")
return False
execution_price = apply_slippage(current_price, False)
shares = int(notional / execution_price)
debug_print(f"Shares to short: {shares}, Expected execution: ${execution_price:.2f}")
if shares == 0:
debug_print("Short sell failed: shares = 0")
return False
debug_print("Submitting short sell order to API...")
api.submit_order(
symbol=symbol,
qty=shares,
side="sell",
type="market",
time_in_force="day"
)
logger.info(f"🔴 SHORT SELL: {shares} shares @ ~${execution_price:.2f}")
debug_print(f"Short sell order submitted (opened short position)")
return execution_price
except Exception as e:
logger.error(f"❌ Failed short sell: {e}")
debug_print(f"Short sell failed: {e}")
return False
def submit_limit_short_sell(symbol, notional, limit_price):
debug_print(f"=== SUBMITTING LIMIT SHORT SELL (OPENING SHORT POSITION) ===")
debug_print(f"Symbol: {symbol}, Notional: ${notional:.2f}, Limit: ${limit_price:.2f}")
if notional < MIN_NOTIONAL:
logger.warning(f"⚠️ Notional ${notional:.2f} < minimum ${MIN_NOTIONAL}")
debug_print(f"Order rejected: notional too small")
return False
try:
shares = int(notional / limit_price)
debug_print(f"Calculated shares to short: {shares}")
if shares == 0:
logger.warning(f"⚠️ Cannot short fractional shares with ${notional:.2f}")
debug_print(f"Order rejected: shares = 0")
return False
debug_print(f"Submitting limit short sell order to API...")
order = api.submit_order(
symbol=symbol,
qty=shares,
side="sell",
type="limit",
limit_price=round(limit_price, 2),
time_in_force="gtc"
)
debug_print(f"Order submitted, ID: {order.id}")
logger.info(f"🔴 LIMIT SHORT SELL: {shares} shares @ ${limit_price:.2f}")
start_time = time.time()
debug_print(f"Waiting for fill (timeout: {LIMIT_ORDER_TIMEOUT}s)...")
while (time.time() - start_time) < LIMIT_ORDER_TIMEOUT:
order_status = api.get_order(order.id)
debug_print(f"Order status: {order_status.status}")
if order_status.status == 'filled':
filled_price = float(order_status.filled_avg_price)
stop_distance = abs(filled_price - limit_price) * ATR_STOP_MULTIPLIER
suggested_stop = filled_price + stop_distance
logger.info(f"✅ FILLED @ ${filled_price:.2f}")
debug_print(f"Order filled at ${filled_price:.2f}, suggested stop: ${suggested_stop:.2f}")
return filled_price
elif order_status.status in ['cancelled', 'expired', 'rejected']:
logger.warning(f"⚠️ Limit order {order_status.status}")
debug_print(f"Order {order_status.status}")
return False
time.sleep(2)
logger.warning("⏱️ Timeout - switching to market")
debug_print("Timeout reached, canceling order and switching to market")
api.cancel_order(order.id)
return submit_short_sell(symbol, notional)
except Exception as e:
logger.error(f"❌ Failed limit short sell: {e}")
debug_print(f"Limit short sell failed: {e}")
return False
def submit_buy_to_cover(symbol, qty):
"""Close a short position by buying back shares"""
debug_print(f"=== SUBMITTING BUY TO COVER (CLOSING SHORT POSITION) ===")
debug_print(f"Symbol: {symbol}, Qty: {qty}")
try:
current_price = get_current_price(symbol)
if current_price == 0:
debug_print("Buy to cover failed: could not get current price")
return False
execution_price = apply_slippage(current_price, True)
debug_print(f"Expected execution: ${execution_price:.2f}")
debug_print("Submitting buy to cover order to API...")
api.submit_order(
symbol=symbol,
qty=qty,
side="buy",
type="market",
time_in_force="day"
)
logger.info(f"🟢 BUY TO COVER: {qty} shares @ ~${execution_price:.2f}")
debug_print(f"Buy to cover order submitted (closed short position)")
return execution_price
except Exception as e:
logger.error(f"❌ Failed buy to cover: {e}")
debug_print(f"Buy to cover failed: {e}")
return False
def submit_limit_sell(symbol, qty, limit_price):
debug_print(f"=== SUBMITTING LIMIT SELL ORDER ===")
debug_print(f"Symbol: {symbol}, Qty: {qty}, Limit: ${limit_price:.2f}")
qty = abs(qty)
try:
debug_print("Submitting limit sell order to API...")
order = api.submit_order(
@@ -1213,10 +1366,13 @@ def submit_limit_sell(symbol, qty, limit_price):
debug_print(f"Limit sell failed: {e}")
return False
def submit_market_sell(symbol, qty):
debug_print(f"=== SUBMITTING MARKET SELL ORDER ===")
debug_print(f"Symbol: {symbol}, Qty: {qty}")
qty = abs(qty)
try:
current_price = get_current_price(symbol)
if current_price == 0:
@@ -1242,6 +1398,7 @@ def submit_market_sell(symbol, qty):
debug_print(f"Market sell failed: {e}")
return False
def close_all_positions():
debug_print("Closing all positions...")
try:
@@ -1254,8 +1411,12 @@ def close_all_positions():
debug_print(f"Found {len(positions)} positions to close")
logger.warning("⚠️ Closing all positions...")
for pos in positions:
debug_print(f"Closing position: {pos.symbol}, qty={pos.qty}")
submit_market_sell(pos.symbol, int(float(pos.qty)))
qty = int(float(pos.qty))
debug_print(f"Closing position: {pos.symbol}, qty={qty}")
if qty > 0:
submit_market_sell(pos.symbol, qty)
elif qty < 0:
submit_buy_to_cover(pos.symbol, abs(qty))
logger.info("✅ All positions closed")
debug_print("All positions closed successfully")
except Exception as e:
@@ -1280,7 +1441,7 @@ def current_position_qty(symbol):
for pos in positions:
if pos.symbol == symbol:
qty = int(float(pos.qty))
debug_print(f"Position qty: {qty}")
debug_print(f"Position qty: {qty} ({'SHORT' if qty < 0 else 'LONG'})")
return qty
debug_print("No position found")
return 0
@@ -1335,7 +1496,7 @@ def get_market_status():
"timestamp": datetime.now()
}
def calculate_position_size(equity, stop_loss, entry_price, regime='normal'):
def calculate_position_size(equity, stop_loss, entry_price, regime='normal', max_position_pct=0.95):
debug_print(f"Calculating position size: equity=${equity:.2f}, entry=${entry_price:.2f}, stop=${stop_loss:.2f}, regime={regime}")
risk_amount = equity * RISK_PER_TRADE
@@ -1353,6 +1514,11 @@ def calculate_position_size(equity, stop_loss, entry_price, regime='normal'):
position_size = risk_amount / stop_distance * entry_price
position_size = max(MIN_NOTIONAL, position_size)
max_position = equity * max_position_pct
if position_size > max_position:
position_size = max_position
debug_print(f"Position capped at {max_position_pct:.0%} of equity: ${position_size:.2f}")
logger.info(f"💰 Position: Risk=${risk_amount:.2f}, Stop=${stop_distance:.2f}, Size=${position_size:.2f}")
debug_print(f"Position size: ${position_size:.2f}")
@@ -1364,19 +1530,19 @@ def should_trade_based_on_market_hours():
debug_print("Market hours filter disabled")
return True
now = datetime.now().time()
now_eastern = datetime.now(EASTERN).time()
open_buffer_end = datetime.strptime("10:00", "%H:%M").time()
close_buffer_start = datetime.strptime("15:30", "%H:%M").time()
debug_print(f"Current time: {now}")
debug_print(f"Current time (ET): {now_eastern}")
if now < open_buffer_end:
debug_print("Before 10:00 AM, outside trading hours")
if now_eastern < open_buffer_end:
debug_print("Before 10:00 AM ET, outside trading hours")
return False
if now >= close_buffer_start:
debug_print("After 3:30 PM, outside trading hours")
if now_eastern >= close_buffer_start:
debug_print("After 3:30 PM ET, outside trading hours")
return False
debug_print("Within trading hours")
@@ -1466,12 +1632,15 @@ def scale_out_profit_taking(symbol, entry_price, current_price, stop_loss, posit
debug_print(f"Target 1 ({PROFIT_TARGET_1}R) hit, scaling out {partial_qty} shares")
if partial_qty > 0:
if USE_LIMIT_ORDERS:
limit_price = current_price
submit_limit_sell(symbol, partial_qty, limit_price)
if partial_qty != 0:
if position_type == 'long':
if USE_LIMIT_ORDERS:
limit_price = current_price
submit_limit_sell(symbol, partial_qty, limit_price)
else:
submit_market_sell(symbol, partial_qty)
else:
submit_market_sell(symbol, partial_qty)
submit_buy_to_cover(symbol, partial_qty)
logger.info(f"🎯 Target 1 ({PROFIT_TARGET_1}R) - 50% out @ ${current_price:.2f}")
@@ -1482,13 +1651,16 @@ def scale_out_profit_taking(symbol, entry_price, current_price, stop_loss, posit
if profit_in_r >= PROFIT_TARGET_2:
remaining_qty = current_position_qty(symbol)
debug_print(f"Target 2 ({PROFIT_TARGET_2}R) hit, exiting {remaining_qty} shares")
if remaining_qty > 0:
if USE_LIMIT_ORDERS:
limit_price = current_price
submit_limit_sell(symbol, remaining_qty, limit_price)
debug_print(f"Target 2 ({PROFIT_TARGET_2}R) hit, exiting {abs(remaining_qty)} shares")
if remaining_qty != 0:
if position_type == 'long':
if USE_LIMIT_ORDERS:
limit_price = current_price
submit_limit_sell(symbol, remaining_qty, limit_price)
else:
submit_market_sell(symbol, remaining_qty)
else:
submit_market_sell(symbol, remaining_qty)
submit_buy_to_cover(symbol, abs(remaining_qty))
logger.info(f"🎯🎯 Target 2 ({PROFIT_TARGET_2}R) - Full exit @ ${current_price:.2f}")
return True
@@ -1589,22 +1761,38 @@ def main():
while True:
debug_print("=== NEW MAIN LOOP ITERATION ===")
try:
current_date = datetime.now().date()
current_date = datetime.now(EASTERN).date()
if last_reset_date != current_date:
trades_today = 0
last_reset_date = current_date
logger.info(f"📅 New day: {current_date}")
debug_print(f"New day: {current_date}, resetting counters")
if hasattr(scale_out_profit_taking, 'target_1_hit'):
delattr(scale_out_profit_taking, 'target_1_hit')
try:
delattr(scale_out_profit_taking, "target_1_hit")
except AttributeError:
pass
debug_print("Reset target_1_hit attribute")
if hasattr(atr_based_trailing_stop, 'trailing_stop'):
delattr(atr_based_trailing_stop, 'trailing_stop')
try:
delattr(atr_based_trailing_stop, "trailing_stop")
except AttributeError:
pass
debug_print("Reset trailing_stop attribute")
if hasattr(main, 'peak_equity'):
delattr(main, 'peak_equity')
debug_print("Reset peak_equity attribute")
if should_skip_trading_day():
day_name = datetime.now().strftime("%A")
day_name = datetime.now(EASTERN).strftime("%A")
logger.info(f"📅 Skipping {day_name} - monitoring mode")
debug_print(f"Skipping trading today ({day_name})")
time.sleep(3600)
@@ -1638,8 +1826,9 @@ def main():
sma_200_trend = check_200_sma_filter(SYMBOL)
logger.info(f"📈 200 SMA: {sma_200_trend.upper()}")
logger.info(f"⚙️ Config: {SYMBOL}, Risk={RISK_PER_TRADE:.2%}, Trades={trades_today}/{MAX_TRADES_PER_DAY}")
debug_print(f"Config: SYMBOL={SYMBOL}, RISK={RISK_PER_TRADE:.2%}, TRADES={trades_today}/{MAX_TRADES_PER_DAY}")
short_status = "ON" if ENABLE_SHORT_SELLING else "OFF"
logger.info(f"⚙️ Config: {SYMBOL}, Risk={RISK_PER_TRADE:.2%}, Trades={trades_today}/{MAX_TRADES_PER_DAY}, Shorts={short_status}")
debug_print(f"Config: SYMBOL={SYMBOL}, RISK={RISK_PER_TRADE:.2%}, TRADES={trades_today}/{MAX_TRADES_PER_DAY}, SHORT_SELLING={ENABLE_SHORT_SELLING}")
trade_count = 0
entry_price = 0
@@ -1664,14 +1853,20 @@ def main():
time.sleep(60)
continue
if datetime.now().date() != current_date:
if datetime.now(EASTERN).date() != current_date:
logger.info("📅 Day changed - resetting")
debug_print("Day changed, exiting session loop")
break
current_equity = fetch_equity()
drawdown = (opening_equity - current_equity) / opening_equity
debug_print(f"Drawdown check: opening=${opening_equity:.2f}, current=${current_equity:.2f}, drawdown={drawdown:.2%}")
if not hasattr(main, 'peak_equity'):
main.peak_equity = opening_equity
if current_equity > main.peak_equity:
main.peak_equity = current_equity
drawdown = (main.peak_equity - current_equity) / main.peak_equity
debug_print(f"Drawdown check: peak=${main.peak_equity:.2f}, current=${current_equity:.2f}, drawdown={drawdown:.2%}")
if drawdown > MAX_DRAWDOWN:
logger.error(f"💸 Max drawdown: {drawdown:.2%}")
@@ -1699,20 +1894,33 @@ def main():
debug_print(f"Managing active position: type={position_type}, entry=${entry_price:.2f}")
if entry_time:
time_in_trade = (datetime.now() - entry_time).total_seconds()
time_in_trade = (datetime.now(EASTERN) - entry_time).total_seconds()
debug_print(f"Time in trade: {time_in_trade:.0f}s (max: {MAX_HOLD_TIME}s)")
if time_in_trade > MAX_HOLD_TIME:
logger.info(f"⏰ Max hold time ({MAX_HOLD_TIME//60} min)")
debug_print(f"Max hold time exceeded, closing position")
qty = current_position_qty(SYMBOL)
if qty > 0:
submit_market_sell(SYMBOL, qty)
if qty != 0:
if position_type == 'long':
submit_market_sell(SYMBOL, qty)
else:
submit_buy_to_cover(SYMBOL, abs(qty))
position_active = False
trade_count += 1
if hasattr(scale_out_profit_taking, 'target_1_hit'):
delattr(scale_out_profit_taking, 'target_1_hit')
if hasattr(atr_based_trailing_stop, 'trailing_stop'):
delattr(atr_based_trailing_stop, 'trailing_stop')
try:
delattr(scale_out_profit_taking, "target_1_hit")
except AttributeError:
pass
try:
delattr(atr_based_trailing_stop, "trailing_stop")
except AttributeError:
pass
debug_print(f"Sleeping {seconds_to_human_readable(POLL_INTERVAL)} after exit")
time.sleep(POLL_INTERVAL)
continue
@@ -1721,30 +1929,56 @@ def main():
remaining_qty = current_position_qty(SYMBOL)
if remaining_qty == 0:
position_active = False
trade_pnl = (current_price - entry_price) * 100
if position_type == 'long':
trade_pnl = (current_price - entry_price) * 100
else:
trade_pnl = (entry_price - current_price) * 100
total_pnl += trade_pnl
logger.info(f"✅ Position closed (PnL: ${trade_pnl:.2f})")
debug_print(f"Position fully closed, PnL: ${trade_pnl:.2f}")
if hasattr(scale_out_profit_taking, 'target_1_hit'):
delattr(scale_out_profit_taking, 'target_1_hit')
if hasattr(atr_based_trailing_stop, 'trailing_stop'):
delattr(atr_based_trailing_stop, 'trailing_stop')
try:
delattr(scale_out_profit_taking, "target_1_hit")
except AttributeError:
pass
try:
delattr(atr_based_trailing_stop, "trailing_stop")
except AttributeError:
pass
debug_print(f"Sleeping {seconds_to_human_readable(POLL_INTERVAL)} after exit")
time.sleep(POLL_INTERVAL)
continue
if atr_based_trailing_stop(SYMBOL, entry_price, current_price, stop_loss, position_type):
qty = current_position_qty(SYMBOL)
if qty > 0:
submit_market_sell(SYMBOL, qty)
if qty != 0:
if position_type == 'long':
submit_market_sell(SYMBOL, qty)
else:
submit_buy_to_cover(SYMBOL, abs(qty))
position_active = False
trade_count += 1
logger.info(f"🛑 Stop hit")
debug_print("Stop hit, position closed")
if hasattr(scale_out_profit_taking, 'target_1_hit'):
delattr(scale_out_profit_taking, 'target_1_hit')
if hasattr(atr_based_trailing_stop, 'trailing_stop'):
delattr(atr_based_trailing_stop, 'trailing_stop')
try:
delattr(scale_out_profit_taking, "target_1_hit")
except AttributeError:
pass
try:
delattr(atr_based_trailing_stop, "trailing_stop")
except AttributeError:
pass
debug_print(f"Sleeping {seconds_to_human_readable(POLL_INTERVAL)} after exit")
time.sleep(POLL_INTERVAL)
continue
@@ -1755,7 +1989,11 @@ def main():
time.sleep(POLL_INTERVAL)
continue
signal, strength, signal_stop_loss = advanced_signal_generator(SYMBOL)
signal, strength, signal_stop_loss, signal_position_type = advanced_signal_generator(SYMBOL)
if signal == 'sell' and not ENABLE_SHORT_SELLING:
debug_print("Short selling disabled, ignoring sell signal")
signal = None
bars = get_recent_bars(SYMBOL, 50)
if bars is not None:
@@ -1770,24 +2008,32 @@ def main():
position_size = calculate_position_size(current_equity, signal_stop_loss, current_price, regime)
if buying_power >= position_size:
if USE_LIMIT_ORDERS and signal == 'buy':
bid, ask = get_bid_ask(SYMBOL)
limit_price = bid
execution_price = submit_limit_buy(SYMBOL, position_size, limit_price)
else:
execution_price = submit_market_buy(SYMBOL, position_size)
execution_price = False
if signal == 'buy':
if USE_LIMIT_ORDERS:
bid, ask = get_bid_ask(SYMBOL)
limit_price = bid
execution_price = submit_limit_buy(SYMBOL, position_size, limit_price)
else:
execution_price = submit_market_buy(SYMBOL, position_size)
elif signal == 'sell':
if USE_LIMIT_ORDERS:
bid, ask = get_bid_ask(SYMBOL)
limit_price = ask
execution_price = submit_limit_short_sell(SYMBOL, position_size, limit_price)
else:
execution_price = submit_short_sell(SYMBOL, position_size)
if execution_price:
trade_count += 1
trades_today += 1
entry_price = execution_price
entry_time = datetime.now()
entry_time = datetime.now(EASTERN)
stop_loss = signal_stop_loss
position_active = True
position_type = 'long' if signal == 'buy' else 'short'
risk_amount = abs(entry_price - stop_loss) / entry_price
logger.info(f"✅ {signal.upper()} executed")
logger.info(f" Entry=${entry_price:.2f}, Stop=${stop_loss:.2f}, Risk={risk_amount:.2%}")
logger.info(f" Regime={regime}, Strength={strength:.2f}, Trade #{trade_count} ({trades_today}/{MAX_TRADES_PER_DAY})")
@@ -1801,9 +2047,22 @@ def main():
position_status = f"{position_type.upper()}" if position_active else "FLAT"
try:
current_time = clock.timestamp.strftime("%I:%M:%S %p")
ts = clock.timestamp
if ts.tzinfo is None:
ts = EASTERN.localize(ts)
else:
ts = ts.astimezone(EASTERN)
current_time = ts.strftime("%I:%M:%S %p ET")
except:
current_time = datetime.now().strftime("%I:%M:%S %p")
current_time = datetime.now(EASTERN).strftime("%I:%M:%S %p ET")
hourly_trend = check_multiframe_confluence(SYMBOL)
@@ -1853,3 +2112,4 @@ def main():
if __name__ == "__main__":
main()