diff --git a/bfxbot/models.py b/bfxbot/models.py index 4521407..c74d410 100644 --- a/bfxbot/models.py +++ b/bfxbot/models.py @@ -58,13 +58,17 @@ class Ticker: class Event: - def __init__(self, kind: EventKind, tick: int) -> None: + def __init__(self, kind: EventKind, tick: int, metadata: Dict = None) -> None: self.kind: EventKind = kind self.tick: int = tick + self.metadata = metadata def __repr__(self) -> str: return f"{self.kind.name} @ Tick {self.tick}" + def has_metadata(self) -> bool: + return self.metadata is not None + class PositionWrapper: def __init__(self, position: Position): @@ -99,17 +103,10 @@ class SymbolStatus: self.current_tick: int = 1 self.strategy: Strategy = strategy - def last_events(self, n) -> List[Event]: - return self.events[-n:] - - def last_positions(self) -> List[PositionWrapper]: - return self.positions[self.current_tick] - - def all_ticks(self) -> List[int]: - return [x for x in range(1, self.current_tick + 1)] - - def all_prices(self) -> List[float]: - return list(map(lambda x: self.prices[x], range(1, self.current_tick + 1))) + def add_order(self, order: Order): + if self.strategy: + self.strategy.order_on_new_tick(order, self) + __add_to_dict_list__(self.orders, self.current_tick, order) # Applies strategy and adds position to list async def add_position(self, position: Position): @@ -119,10 +116,17 @@ class SymbolStatus: await self.__apply_strategy_to_position__(pw) __add_to_dict_list__(self.positions, self.current_tick, pw) - def add_order(self, order: Order): - if self.strategy: - self.strategy.order_on_tick(order, self) - __add_to_dict_list__(self.orders, self.current_tick, order) + def all_prices(self) -> List[float]: + return list(map(lambda x: self.prices[x], range(1, self.current_tick + 1))) + + def all_ticks(self) -> List[int]: + return [x for x in range(1, self.current_tick + 1)] + + def last_events(self, n) -> List[Event]: + return self.events[-n:] + + def last_positions(self) -> List[PositionWrapper]: + return self.positions[self.current_tick] def previous_position_w(self, pid: int) -> PositionWrapper: if self.current_tick == 1: @@ -130,12 +134,18 @@ class SymbolStatus: return next(filter(lambda x: x.position.id == pid, self.positions[self.current_tick - 1])) + def set_tick_price(self, tick, price): + self.prices[tick] = price + async def __add_event__(self, event: Event): self.events.append(event) await self.eh.call_event(self, event) async def __apply_strategy_to_position__(self, pw: PositionWrapper): - (new_state, events) = self.strategy.position_on_tick(pw.position, self) + if not self.strategy: + return + + new_state, events = self.strategy.position_on_new_tick(pw.position, self) if isinstance(new_state, PositionState): await self.__update_position_state__(pw, new_state) @@ -147,10 +157,7 @@ class SymbolStatus: async def __update_position_state__(self, pw: PositionWrapper, state: PositionState): pw.set_state(state) - await self.eh.call_state(self, pw) - - def set_tick_price(self, tick, price): - self.prices[tick] = price + await self.eh.call_position_state(self, pw) class Strategy: @@ -158,14 +165,14 @@ class Strategy: Defines new position state and events after tick. """ - def position_on_tick(self, position: Position, ss: SymbolStatus) -> (PositionState, List[Event]): + def position_on_new_tick(self, position: Position, ss: SymbolStatus) -> (PositionState, List[Event]): pass """ Defines new order state and events after tick. """ - def order_on_tick(self, order: Order, ss: SymbolStatus): + def order_on_new_tick(self, order: Order, ss: SymbolStatus): pass @@ -193,7 +200,7 @@ class EventHandler: else: h(event, status) - async def call_state(self, status: SymbolStatus, pw: PositionWrapper): + async def call_position_state(self, status: SymbolStatus, pw: PositionWrapper): state = pw.state if state in self.state_handlers: @@ -221,7 +228,7 @@ class EventHandler: return registerhandler - def on_state(self, state: PositionState): + def on_position_state(self, state: PositionState): def registerhandler(handler): if state in self.state_handlers: self.state_handlers[state].append(handler) @@ -238,7 +245,7 @@ class EventHandler: return registerhandle - def on_any_state(self): + def on_any_position_state(self): def registerhandle(handler): self.any_state.append(handler) return handler diff --git a/strategy.py b/strategy.py index 32dce20..586c614 100644 --- a/strategy.py +++ b/strategy.py @@ -14,6 +14,7 @@ class Position(object): pass + class SquaredTrailingStop: def __init__(self, p_min: Point, p_max: Point): a = sympy.abc.a @@ -24,8 +25,8 @@ class SquaredTrailingStop: self.p_max = p_max e1 = 2 * a * (p_max.x + b) - e2 = a * (p_min.x + b)**2 + c - p_min.y - e3 = a * (p_max.x + b)**2 + c - p_max.y + e2 = a * (p_min.x + b) ** 2 + c - p_min.y + e3 = a * (p_max.x + b) ** 2 + c - p_max.y s = solve([e1, e2, e3])[0] @@ -33,7 +34,7 @@ class SquaredTrailingStop: def y(self, x): def inter_y(x): - return self.a * (x + self.b)**2 + self.c + return self.a * (x + self.b) ** 2 + self.c if x < self.p_min.x: return self.p_min.y @@ -47,6 +48,7 @@ class SquaredTrailingStop: return 0 return x - self.y(x) + class TrailingStopStrategy(Strategy): BREAK_EVEN_PERC = TAKER_FEE MIN_PROFIT_PERC = BREAK_EVEN_PERC + 0.3 @@ -54,9 +56,9 @@ class TrailingStopStrategy(Strategy): MAX_LOSS_PERC = -3.75 OFFER_PERC = 0.005 - TRAILING_STOP = SquaredTrailingStop(Point(MIN_PROFIT_PERC, MIN_PROFIT_PERC/3*2), Point(GOOD_PROFIT_PERC, 0.1)) + TRAILING_STOP = SquaredTrailingStop(Point(MIN_PROFIT_PERC, MIN_PROFIT_PERC / 3 * 2), Point(GOOD_PROFIT_PERC, 0.1)) - def position_on_tick(self, position: Position, ss: SymbolStatus) -> (PositionState, List[Event]): + def position_on_new_tick(self, position: Position, ss: SymbolStatus) -> (PositionState, List[Event]): events = [] pl_perc = net_pl_percentage(position.profit_loss_percentage, TAKER_FEE) @@ -77,17 +79,15 @@ class TrailingStopStrategy(Strategy): return state, events if state == PositionState.PROFIT: - events.append(Event(EventKind.REACHED_GOOD_PROFIT, position.id, ss.current_tick)) + events.append(Event(EventKind.REACHED_GOOD_PROFIT, ss.current_tick)) elif state == PositionState.MINIMUM_PROFIT: - events.append(Event(EventKind.REACHED_MIN_PROFIT, position.id, ss.current_tick)) + events.append(Event(EventKind.REACHED_MIN_PROFIT, ss.current_tick)) elif state == PositionState.BREAK_EVEN: - events.append(Event(EventKind.REACHED_BREAK_EVEN, position.id, ss.current_tick)) + events.append(Event(EventKind.REACHED_BREAK_EVEN, ss.current_tick)) elif state == PositionState.LOSS: - events.append(Event(EventKind.REACHED_LOSS, position.id, ss.current_tick)) + events.append(Event(EventKind.REACHED_LOSS, ss.current_tick)) else: - events.append(Event(EventKind.REACHED_MAX_LOSS, position.id, ss.current_tick)) - events.append(Event(EventKind.CLOSE_POSITION, position.id, ss.current_tick)) + events.append(Event(EventKind.REACHED_MAX_LOSS, ss.current_tick)) + events.append(Event(EventKind.CLOSE_POSITION, ss.current_tick)) return state, events - -