Fix scale out for small positions and add exception handling to order placement
This commit is contained in:
+40
-36
@@ -55,41 +55,45 @@ class AlpacaClient:
|
|||||||
return self.api.get_position(symbol)
|
return self.api.get_position(symbol)
|
||||||
|
|
||||||
def place_order(self, symbol, side, notional, limit_price, limit_order_timeout):
|
def place_order(self, symbol, side, notional, limit_price, limit_order_timeout):
|
||||||
quote = self.get_latest_quote(symbol)
|
try:
|
||||||
if quote is None:
|
quote = self.get_latest_quote(symbol)
|
||||||
return None
|
if quote is None:
|
||||||
bid_price = getattr(quote, 'bid_price', None)
|
|
||||||
ask_price = getattr(quote, 'ask_price', None)
|
|
||||||
if limit_price:
|
|
||||||
price_source = limit_price
|
|
||||||
else:
|
|
||||||
price_source = bid_price if side == "buy" else ask_price
|
|
||||||
if price_source is None or price_source <= 0:
|
|
||||||
return None
|
|
||||||
shares = int(notional / price_source)
|
|
||||||
if shares == 0:
|
|
||||||
return None
|
|
||||||
if limit_price:
|
|
||||||
order = self.submit_order(symbol=symbol, qty=shares, side=side, type="limit", limit_price=round(limit_price, 2), time_in_force="day")
|
|
||||||
start = time.time()
|
|
||||||
while time.time() - start < limit_order_timeout:
|
|
||||||
status = self.get_order(order.id)
|
|
||||||
if status.status == "filled":
|
|
||||||
return float(status.filled_avg_price)
|
|
||||||
if status.status in {"cancelled", "expired", "rejected"}:
|
|
||||||
return None
|
|
||||||
time.sleep(2)
|
|
||||||
self.cancel_order(order.id)
|
|
||||||
return None
|
|
||||||
order = self.submit_order(symbol=symbol, qty=shares, side=side, type="market", time_in_force="day")
|
|
||||||
status = self.get_order(order.id)
|
|
||||||
timeout = 30
|
|
||||||
start_time = time.time()
|
|
||||||
while status.status not in {"filled", "cancelled", "expired", "rejected"}:
|
|
||||||
if time.time() - start_time > timeout:
|
|
||||||
return None
|
return None
|
||||||
time.sleep(0.5)
|
bid_price = getattr(quote, 'bid_price', None)
|
||||||
|
ask_price = getattr(quote, 'ask_price', None)
|
||||||
|
if limit_price:
|
||||||
|
price_source = limit_price
|
||||||
|
else:
|
||||||
|
price_source = bid_price if side == "buy" else ask_price
|
||||||
|
if price_source is None or price_source <= 0:
|
||||||
|
return None
|
||||||
|
shares = int(notional / price_source)
|
||||||
|
if shares == 0:
|
||||||
|
return None
|
||||||
|
if limit_price:
|
||||||
|
order = self.submit_order(symbol=symbol, qty=shares, side=side, type="limit", limit_price=round(limit_price, 2), time_in_force="day")
|
||||||
|
start = time.time()
|
||||||
|
while time.time() - start < limit_order_timeout:
|
||||||
|
status = self.get_order(order.id)
|
||||||
|
if status.status == "filled":
|
||||||
|
return float(status.filled_avg_price)
|
||||||
|
if status.status in {"cancelled", "expired", "rejected"}:
|
||||||
|
return None
|
||||||
|
time.sleep(2)
|
||||||
|
self.cancel_order(order.id)
|
||||||
|
return None
|
||||||
|
order = self.submit_order(symbol=symbol, qty=shares, side=side, type="market", time_in_force="day")
|
||||||
status = self.get_order(order.id)
|
status = self.get_order(order.id)
|
||||||
if status.status == "filled":
|
timeout = 30
|
||||||
return float(status.filled_avg_price)
|
start_time = time.time()
|
||||||
return None
|
while status.status not in {"filled", "cancelled", "expired", "rejected"}:
|
||||||
|
if time.time() - start_time > timeout:
|
||||||
|
return None
|
||||||
|
time.sleep(0.5)
|
||||||
|
status = self.get_order(order.id)
|
||||||
|
if status.status == "filled":
|
||||||
|
return float(status.filled_avg_price)
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Order placement error: {e}")
|
||||||
|
return None
|
||||||
|
|||||||
@@ -763,6 +763,10 @@ def scale_out_profit_taking(symbol, entry_price, current_price, stop_loss, posit
|
|||||||
position_state.target_1_hit = True
|
position_state.target_1_hit = True
|
||||||
logger.info(f"💰 Partial profit @ {profit_pct:.2f}% ({half_qty} shares)")
|
logger.info(f"💰 Partial profit @ {profit_pct:.2f}% ({half_qty} shares)")
|
||||||
debug_print(f"Partial profit taken: {half_qty} shares @ {profit_pct:.2f}%")
|
debug_print(f"Partial profit taken: {half_qty} shares @ {profit_pct:.2f}%")
|
||||||
|
else:
|
||||||
|
position_state.target_1_hit = True
|
||||||
|
logger.info(f"💰 Target 1 reached @ {profit_pct:.2f}% (position too small to scale)")
|
||||||
|
debug_print(f"Position size {qty} too small for partial exit, holding for target 2")
|
||||||
|
|
||||||
if profit_pct >= target_2_pct:
|
if profit_pct >= target_2_pct:
|
||||||
qty = current_position_qty(symbol)
|
qty = current_position_qty(symbol)
|
||||||
|
|||||||
Reference in New Issue
Block a user