Fix critical bugs: RSI division by zero, API price validation, order timeout, and crossover race condition
This commit is contained in:
@@ -64,7 +64,7 @@ class AlpacaClient:
|
|||||||
price_source = limit_price
|
price_source = limit_price
|
||||||
else:
|
else:
|
||||||
price_source = bid_price if side == "buy" else ask_price
|
price_source = bid_price if side == "buy" else ask_price
|
||||||
if price_source is None:
|
if price_source is None or price_source <= 0:
|
||||||
return None
|
return None
|
||||||
shares = int(notional / price_source)
|
shares = int(notional / price_source)
|
||||||
if shares == 0:
|
if shares == 0:
|
||||||
@@ -83,7 +83,11 @@ class AlpacaClient:
|
|||||||
return None
|
return None
|
||||||
order = self.submit_order(symbol=symbol, qty=shares, side=side, type="market", time_in_force="day")
|
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)
|
||||||
|
timeout = 30
|
||||||
|
start_time = time.time()
|
||||||
while status.status not in {"filled", "cancelled", "expired", "rejected"}:
|
while status.status not in {"filled", "cancelled", "expired", "rejected"}:
|
||||||
|
if time.time() - start_time > timeout:
|
||||||
|
return None
|
||||||
time.sleep(0.5)
|
time.sleep(0.5)
|
||||||
status = self.get_order(order.id)
|
status = self.get_order(order.id)
|
||||||
if status.status == "filled":
|
if status.status == "filled":
|
||||||
|
|||||||
+16
-13
@@ -206,6 +206,17 @@ api = AlpacaClient(
|
|||||||
api_version='v2'
|
api_version='v2'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
class SignalState:
|
||||||
|
def __init__(self):
|
||||||
|
self.last_bullish_crossover_bar = -999
|
||||||
|
self.last_bearish_crossover_bar = -999
|
||||||
|
|
||||||
|
def reset(self):
|
||||||
|
self.last_bullish_crossover_bar = -999
|
||||||
|
self.last_bearish_crossover_bar = -999
|
||||||
|
|
||||||
|
signal_state = SignalState()
|
||||||
|
|
||||||
def debug_print(message):
|
def debug_print(message):
|
||||||
if DEBUG_MODE:
|
if DEBUG_MODE:
|
||||||
debug_logger.debug(f"🔎 {message}")
|
debug_logger.debug(f"🔎 {message}")
|
||||||
@@ -426,28 +437,23 @@ def advanced_signal_generator(symbol):
|
|||||||
bearish_crossover = False
|
bearish_crossover = False
|
||||||
|
|
||||||
if REQUIRE_MA_CROSSOVER and len(bars) >= LONG_WINDOW + CROSSOVER_LOOKBACK:
|
if REQUIRE_MA_CROSSOVER and len(bars) >= LONG_WINDOW + CROSSOVER_LOOKBACK:
|
||||||
if not hasattr(advanced_signal_generator, 'last_bullish_crossover_bar'):
|
|
||||||
advanced_signal_generator.last_bullish_crossover_bar = -999
|
|
||||||
if not hasattr(advanced_signal_generator, 'last_bearish_crossover_bar'):
|
|
||||||
advanced_signal_generator.last_bearish_crossover_bar = -999
|
|
||||||
|
|
||||||
current_bar_index = len(bars) - 1
|
current_bar_index = len(bars) - 1
|
||||||
|
|
||||||
for i in range(1, CROSSOVER_LOOKBACK + 1):
|
for i in range(1, CROSSOVER_LOOKBACK + 1):
|
||||||
bar_index = current_bar_index - i
|
bar_index = current_bar_index - i
|
||||||
if short_ma_series.iloc[-i-1] <= long_ma_series.iloc[-i-1] and short_ma_series.iloc[-i] > long_ma_series.iloc[-i]:
|
if short_ma_series.iloc[-i-1] <= long_ma_series.iloc[-i-1] and short_ma_series.iloc[-i] > long_ma_series.iloc[-i]:
|
||||||
if bar_index > advanced_signal_generator.last_bullish_crossover_bar:
|
if bar_index > signal_state.last_bullish_crossover_bar:
|
||||||
bullish_crossover = True
|
bullish_crossover = True
|
||||||
advanced_signal_generator.last_bullish_crossover_bar = bar_index
|
signal_state.last_bullish_crossover_bar = bar_index
|
||||||
debug_print(f"Bullish crossover detected {i} bars ago")
|
debug_print(f"Bullish crossover detected {i} bars ago")
|
||||||
break
|
break
|
||||||
|
|
||||||
for i in range(1, CROSSOVER_LOOKBACK + 1):
|
for i in range(1, CROSSOVER_LOOKBACK + 1):
|
||||||
bar_index = current_bar_index - i
|
bar_index = current_bar_index - i
|
||||||
if short_ma_series.iloc[-i-1] >= long_ma_series.iloc[-i-1] and short_ma_series.iloc[-i] < long_ma_series.iloc[-i]:
|
if short_ma_series.iloc[-i-1] >= long_ma_series.iloc[-i-1] and short_ma_series.iloc[-i] < long_ma_series.iloc[-i]:
|
||||||
if bar_index > advanced_signal_generator.last_bearish_crossover_bar:
|
if bar_index > signal_state.last_bearish_crossover_bar:
|
||||||
bearish_crossover = True
|
bearish_crossover = True
|
||||||
advanced_signal_generator.last_bearish_crossover_bar = bar_index
|
signal_state.last_bearish_crossover_bar = bar_index
|
||||||
debug_print(f"Bearish crossover detected {i} bars ago")
|
debug_print(f"Bearish crossover detected {i} bars ago")
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -647,10 +653,7 @@ def main():
|
|||||||
trades_today = 0
|
trades_today = 0
|
||||||
total_pnl = 0
|
total_pnl = 0
|
||||||
|
|
||||||
if hasattr(advanced_signal_generator, 'last_bullish_crossover_bar'):
|
signal_state.reset()
|
||||||
delattr(advanced_signal_generator, 'last_bullish_crossover_bar')
|
|
||||||
if hasattr(advanced_signal_generator, 'last_bearish_crossover_bar'):
|
|
||||||
delattr(advanced_signal_generator, 'last_bearish_crossover_bar')
|
|
||||||
|
|
||||||
try:
|
try:
|
||||||
existing_position = api.get_position(SYMBOL)
|
existing_position = api.get_position(SYMBOL)
|
||||||
|
|||||||
@@ -10,7 +10,8 @@ def rsi(data, window=14):
|
|||||||
delta = data.diff()
|
delta = data.diff()
|
||||||
gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
|
gain = (delta.where(delta > 0, 0)).rolling(window=window).mean()
|
||||||
loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
|
loss = (-delta.where(delta < 0, 0)).rolling(window=window).mean()
|
||||||
loss = loss.replace(0, 0.0001)
|
loss = loss.replace(0, 1e-10)
|
||||||
|
loss = loss.clip(lower=1e-10)
|
||||||
rs = gain / loss
|
rs = gain / loss
|
||||||
rsi_val = 100 - (100 / (1 + rs))
|
rsi_val = 100 - (100 / (1 + rs))
|
||||||
return rsi_val
|
return rsi_val
|
||||||
|
|||||||
Reference in New Issue
Block a user