diff --git a/bot_new.py b/bot_new.py index c8606d0..a8dc1c3 100644 --- a/bot_new.py +++ b/bot_new.py @@ -124,6 +124,18 @@ class StateManager: self.last_strategy_time = 0 self.strategy_interval = 0.1 + self.ema_fast_period = 5 + self.ema_slow_period = 20 + self.ema_prices = {sym: [] for sym in ["VALBZ", "VALE", "XLF"]} + self.ema_fast = {sym: None for sym in ["VALBZ", "VALE", "XLF"]} + self.ema_slow = {sym: None for sym in ["VALBZ", "VALE", "XLF"]} + self.prev_ema_fast = {sym: None for sym in ["VALBZ", "VALE", "XLF"]} + self.position_signal = {sym: 0 for sym in ["VALBZ", "VALE", "XLF"]} + self.bid_prices = {sym: None for sym in ["BOND", "VALBZ", "VALE", "GS", "MS", "WFC", "XLF"]} + self.ask_prices = {sym: None for sym in ["BOND", "VALBZ", "VALE", "GS", "MS", "WFC", "XLF"]} + self.bid_depths = {sym: 0 for sym in ["BOND", "VALBZ", "VALE", "GS", "MS", "WFC", "XLF"]} + self.ask_depths = {sym: 0 for sym in ["BOND", "VALBZ", "VALE", "GS", "MS", "WFC", "XLF"]} + def position_for_symbol(self, symbol): return self.positions_by_symbol.get(symbol, 0) @@ -213,30 +225,76 @@ class StateManager: order_id = self.next_order_id() self.exchange.send_convert_message(order_id, symbol, dir, size) - def update_avg_prices(self, trade_message): + def calculate_ema(self, prices, period): + if len(prices) < period: + return None + alpha = 2 / (period + 1) + ema = prices[0] + for price in prices[1:]: + ema = alpha * price + (1 - alpha) * ema + return ema + + def update_ema_prices(self, trade_message): symbol = trade_message["symbol"] price = trade_message["price"] - if symbol == "VALBZ": - if len(self.average_valbz_price) < 5: - self.average_valbz_price.append(price) - else: - self.average_valbz_price.pop(0) - self.average_valbz_price.append(price) + if symbol not in ["VALBZ", "VALE", "XLF"]: + return - elif symbol == "VALE": - if len(self.average_vale_price) < 5: - self.average_vale_price.append(price) - else: - self.average_vale_price.pop(0) - self.average_vale_price.append(price) + self.ema_prices[symbol].append(price) + max_len = self.ema_slow_period + if len(self.ema_prices[symbol]) > max_len: + self.ema_prices[symbol].pop(0) - elif symbol == "XLF": - if len(self.average_xlf_price) < 25: - self.average_xlf_price.append(price) - else: - self.average_xlf_price.pop(0) - self.average_xlf_price.append(price) + self.prev_ema_fast[symbol] = self.ema_fast[symbol] + self.ema_fast[symbol] = self.calculate_ema(self.ema_prices[symbol], self.ema_fast_period) + self.ema_slow[symbol] = self.calculate_ema(self.ema_prices[symbol], self.ema_slow_period) + + def get_cross_signal(self, symbol): + if self.ema_fast[symbol] is None or self.ema_slow[symbol] is None: + return 0 + if self.prev_ema_fast[symbol] is None: + return 0 + + prev_fast = self.prev_ema_fast[symbol] + prev_slow = self.ema_slow[symbol] + curr_fast = self.ema_fast[symbol] + curr_slow = self.ema_slow[symbol] + + if prev_fast <= prev_slow and curr_fast > curr_slow: + return 1 + elif prev_fast >= prev_slow and curr_fast < curr_slow: + return -1 + return 0 + + def update_book(self, book_message): + symbol = book_message["symbol"] + if symbol not in self.bid_prices: + return + + buy = book_message.get("buy", []) + sell = book_message.get("sell", []) + + if buy: + self.bid_prices[symbol] = buy[0][0] + self.bid_depths[symbol] = sum(depth for _, depth in buy[:3]) + if sell: + self.ask_prices[symbol] = sell[0][0] + self.ask_depths[symbol] = sum(depth for _, depth in sell[:3]) + + def get_book_imbalance(self, symbol): + bid = self.bid_depths.get(symbol, 0) + ask = self.ask_depths.get(symbol, 0) + if bid + ask == 0: + return 0 + return (bid - ask) / (bid + ask) + + def get_fair_value_from_book(self, symbol): + bid = self.bid_prices.get(symbol) + ask = self.ask_prices.get(symbol) + if bid is not None and ask is not None: + return (bid + ask) // 2 + return None def can_add_position(self, symbol, size, is_buy): current = self.position_for_symbol(symbol) @@ -251,27 +309,30 @@ class StateManager: return self.last_strategy_time = now - avg_valbz = self.get_average_price(self.average_valbz_price) - avg_vale = self.get_average_price(self.average_vale_price) - avg_xlf = self.get_average_price(self.average_xlf_price) + for sym in ["VALBZ", "VALE", "XLF"]: + ema_signal = self.get_cross_signal(sym) + imbalance = self.get_book_imbalance(sym) + fair = self.get_fair_value_from_book(sym) - if len(self.average_valbz_price) >= 5: - sell_size = 1 if self.can_add_position("VALBZ", 1, False) else 0 - buy_size = 1 if self.can_add_position("VALBZ", 1, True) else 0 - self.set_orders_in_symbol_for_direction("VALBZ", "SELL", {avg_valbz + 1: sell_size}) - self.set_orders_in_symbol_for_direction("VALBZ", "BUY", {avg_valbz - 1: buy_size}) + if fair is None: + continue - if len(self.average_vale_price) >= 5: - sell_size = 1 if self.can_add_position("VALE", 1, False) else 0 - buy_size = 1 if self.can_add_position("VALE", 1, True) else 0 - self.set_orders_in_symbol_for_direction("VALE", "SELL", {avg_vale + 1: sell_size}) - self.set_orders_in_symbol_for_direction("VALE", "BUY", {avg_vale - 1: buy_size}) + signal_strength = abs(ema_signal) + (1 if abs(imbalance) > 0.3 else 0) + if signal_strength == 0: + continue - if len(self.average_xlf_price) >= 25: - sell_size = 1 if self.can_add_position("XLF", 1, False) else 0 - buy_size = 1 if self.can_add_position("XLF", 1, True) else 0 - self.set_orders_in_symbol_for_direction("XLF", "SELL", {avg_xlf + 1: sell_size}) - self.set_orders_in_symbol_for_direction("XLF", "BUY", {avg_xlf - 1: buy_size}) + if ema_signal == 1 and self.position_signal[sym] <= 0: + size = 1 if self.can_add_position(sym, 1, True) else 0 + if size > 0: + price = fair + 1 + self.set_orders_in_symbol_for_direction(sym, "BUY", {price: size}) + self.position_signal[sym] = 1 + elif ema_signal == -1 and self.position_signal[sym] >= 0: + size = 1 if self.can_add_position(sym, 1, False) else 0 + if size > 0: + price = fair - 1 + self.set_orders_in_symbol_for_direction(sym, "SELL", {price: size}) + self.position_signal[sym] = -1 self.execute_arb() @@ -357,12 +418,14 @@ def main(): print(message) state_manager.on_fill(message) elif message["type"] == "trade": - state_manager.update_avg_prices(message) + state_manager.update_ema_prices(message) state_manager.execute_strategies() elif message["type"] == "ack": state_manager.on_ack(message) elif message["type"] == "out": state_manager.on_out(message) + elif message["type"] == "book": + state_manager.update_book(message) if __name__ == "__main__":