add cross + imb

This commit is contained in:
2026-05-09 16:25:02 +09:00
parent 5bd1588e09
commit 7a8704c1d4

View File

@@ -124,6 +124,18 @@ class StateManager:
self.last_strategy_time = 0 self.last_strategy_time = 0
self.strategy_interval = 0.1 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): def position_for_symbol(self, symbol):
return self.positions_by_symbol.get(symbol, 0) return self.positions_by_symbol.get(symbol, 0)
@@ -213,30 +225,76 @@ class StateManager:
order_id = self.next_order_id() order_id = self.next_order_id()
self.exchange.send_convert_message(order_id, symbol, dir, size) 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"] symbol = trade_message["symbol"]
price = trade_message["price"] price = trade_message["price"]
if symbol == "VALBZ": if symbol not in ["VALBZ", "VALE", "XLF"]:
if len(self.average_valbz_price) < 5: return
self.average_valbz_price.append(price)
else:
self.average_valbz_price.pop(0)
self.average_valbz_price.append(price)
elif symbol == "VALE": self.ema_prices[symbol].append(price)
if len(self.average_vale_price) < 5: max_len = self.ema_slow_period
self.average_vale_price.append(price) if len(self.ema_prices[symbol]) > max_len:
else: self.ema_prices[symbol].pop(0)
self.average_vale_price.pop(0)
self.average_vale_price.append(price)
elif symbol == "XLF": self.prev_ema_fast[symbol] = self.ema_fast[symbol]
if len(self.average_xlf_price) < 25: self.ema_fast[symbol] = self.calculate_ema(self.ema_prices[symbol], self.ema_fast_period)
self.average_xlf_price.append(price) self.ema_slow[symbol] = self.calculate_ema(self.ema_prices[symbol], self.ema_slow_period)
else:
self.average_xlf_price.pop(0) def get_cross_signal(self, symbol):
self.average_xlf_price.append(price) 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): def can_add_position(self, symbol, size, is_buy):
current = self.position_for_symbol(symbol) current = self.position_for_symbol(symbol)
@@ -251,27 +309,30 @@ class StateManager:
return return
self.last_strategy_time = now self.last_strategy_time = now
avg_valbz = self.get_average_price(self.average_valbz_price) for sym in ["VALBZ", "VALE", "XLF"]:
avg_vale = self.get_average_price(self.average_vale_price) ema_signal = self.get_cross_signal(sym)
avg_xlf = self.get_average_price(self.average_xlf_price) imbalance = self.get_book_imbalance(sym)
fair = self.get_fair_value_from_book(sym)
if len(self.average_valbz_price) >= 5: if fair is None:
sell_size = 1 if self.can_add_position("VALBZ", 1, False) else 0 continue
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 len(self.average_vale_price) >= 5: signal_strength = abs(ema_signal) + (1 if abs(imbalance) > 0.3 else 0)
sell_size = 1 if self.can_add_position("VALE", 1, False) else 0 if signal_strength == 0:
buy_size = 1 if self.can_add_position("VALE", 1, True) else 0 continue
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})
if len(self.average_xlf_price) >= 25: if ema_signal == 1 and self.position_signal[sym] <= 0:
sell_size = 1 if self.can_add_position("XLF", 1, False) else 0 size = 1 if self.can_add_position(sym, 1, True) else 0
buy_size = 1 if self.can_add_position("XLF", 1, True) else 0 if size > 0:
self.set_orders_in_symbol_for_direction("XLF", "SELL", {avg_xlf + 1: sell_size}) price = fair + 1
self.set_orders_in_symbol_for_direction("XLF", "BUY", {avg_xlf - 1: buy_size}) 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() self.execute_arb()
@@ -357,12 +418,14 @@ def main():
print(message) print(message)
state_manager.on_fill(message) state_manager.on_fill(message)
elif message["type"] == "trade": elif message["type"] == "trade":
state_manager.update_avg_prices(message) state_manager.update_ema_prices(message)
state_manager.execute_strategies() state_manager.execute_strategies()
elif message["type"] == "ack": elif message["type"] == "ack":
state_manager.on_ack(message) state_manager.on_ack(message)
elif message["type"] == "out": elif message["type"] == "out":
state_manager.on_out(message) state_manager.on_out(message)
elif message["type"] == "book":
state_manager.update_book(message)
if __name__ == "__main__": if __name__ == "__main__":