state handlers

This commit is contained in:
Giulio De Pasquale 2020-11-28 16:10:27 +00:00
parent fb1433a13d
commit af089554ce

137
main.py
View File

@ -33,14 +33,14 @@ class EventKind(Enum):
REACHED_BREAK_EVEN = 4,
REACHED_MIN_PROFIT = 5,
REACHED_GOOD_PROFIT = 6,
REACHED_MAX_LOSS = 7
REACHED_MAX_LOSS = 7,
CLOSE_POSITION = 8,
class Event():
def __init__(self, kind: EventKind, tick: int, position: Position) -> None:
def __init__(self, kind: EventKind, tick: int) -> None:
self.kind: EventKind = kind
self.tick: int = tick
self.position: Position = position
def __repr__(self) -> str:
return f"{self.kind.name} @ Tick {self.tick}"
@ -100,15 +100,16 @@ class Status():
self.ticker: Ticker = Ticker(tick_duration)
self.events: list[Event] = []
self.symbol = symbol
self.ticks: dict[int, float] = {}
self.ticks: dict[int, (float, Position)] = {}
self.current_state: State = State.LOSS
self.printer: Printer = printer
self.stop_percentage = 999.0
self.stop_percentage: float = None
async def wait(self) -> None:
async def update(self, position: Position):
self.ticks[self.get_current_tick()] = (await get_current_price(self.symbol), position)
def wait(self):
sleep(self.ticker.seconds)
self.ticks[self.ticker.current_tick] = await get_current_price(self.symbol)
self.ticker.current_tick += 1
def get_current_tick(self) -> int:
@ -117,6 +118,9 @@ class Status():
def last_events(self, n):
return self.events[-n:]
def last_position(self) -> Position:
return self.ticks[self.ticker.current_tick][1]
def add_event(self, event: Event):
self.events.append(event)
@ -125,52 +129,69 @@ class Status():
return await get_current_price(self.symbol)
self.ticks[self.get_current_tick()]
def set_state(self, state: State, position: Position):
def set_state(self, state: State):
if self.current_state != state:
event: EventKind = None
if state == State.CRITICAL:
event = Event(EventKind.REACHED_MAX_LOSS,
self.get_current_tick(), position)
self.get_current_tick())
elif state == State.LOSS:
event = Event(EventKind.REACHED_LOSS,
self.get_current_tick(), position)
self.get_current_tick())
elif state == State.BREAK_EVEN:
event = Event(EventKind.REACHED_BREAK_EVEN,
self.get_current_tick(), position)
self.get_current_tick())
elif state == State.MINIMUM_PROFIT:
event = Event(EventKind.REACHED_MIN_PROFIT,
self.get_current_tick(), position)
self.get_current_tick())
elif state == State.PROFIT:
event = Event(EventKind.REACHED_GOOD_PROFIT,
self.get_current_tick(), position)
self.get_current_tick())
self.events.append(event)
eh.call(event, self)
eh.call_event(event, self)
self.current_state = state
eh.call_state(self.current_state, self)
def get_current_state(self) -> State:
return self.current_state
class EventHandler:
def __init__(self):
self.handlers = {}
self.event_handlers = {}
self.state_handlers = {}
def call(self, event: Event, status: Status):
def call_event(self, event: Event, status: Status):
value = event.kind.value
if value in self.handlers:
for h in self.handlers[value]:
if value in self.event_handlers:
for h in self.event_handlers[value]:
h(event, status)
def call_state(self, state: State, status: Status):
if state in self.state_handlers:
for h in self.state_handlers[state]:
h(status)
def on_event(self, kind: EventKind):
value = kind.value
def registerhandler(handler):
if value in self.handlers:
self.handlers[value].append(handler)
if value in self.event_handlers:
self.event_handlers[value].append(handler)
else:
self.handlers[value] = [handler]
self.event_handlers[value] = [handler]
return handler
return registerhandler
def on_state(self, state: State):
def registerhandler(handler):
if state in self.state_handlers:
self.state_handlers[state].append(handler)
else:
self.state_handlers[state] = [handler]
return handler
return registerhandler
@ -194,8 +215,8 @@ GOOD_PROFIT_PERC = MIN_PROFIT_PERC * 2.1
MAX_LOSS_PERC = -4.0
TRAIL_STOP_PERCENTAGES = {
EventKind.REACHED_MIN_PROFIT: 0.2,
EventKind.REACHED_GOOD_PROFIT: 0.1
State.MINIMUM_PROFIT: 0.2,
State.PROFIT: 0.1
}
@ -226,6 +247,49 @@ def on_critical(event: Event, status: Status):
playsound("sounds/gameover.mp3")
@eh.on_state(State.MINIMUM_PROFIT)
def on_state_min_profit(status: Status):
update_stop_percentage(State.MINIMUM_PROFIT, status)
current_pl_perc = net_pl_percentage(
status.last_position().profit_loss_percentage, TAKER_FEE)
if current_pl_perc < status.stop_percentage:
status.add_event(Event(EventKind.CLOSE_POSITION,
status.get_current_tick()))
@eh.on_state(State.CRITICAL)
def on_state_critical(status: Status):
status.add_event(Event(EventKind.CLOSE_POSITION,
status.get_current_tick()))
@eh.on_state(State.PROFIT)
def on_state_min_profit(status: Status):
update_stop_percentage(State.PROFIT, status)
current_pl_perc = net_pl_percentage(
status.last_position().profit_loss_percentage, TAKER_FEE)
if current_pl_perc < status.stop_percentage:
status.add_event(Event(EventKind.CLOSE_POSITION,
status.get_current_tick()))
def update_stop_percentage(state: State, status: Status):
last_position = status.last_position()
last_pl_net_perc = net_pl_percentage(
last_position.profit_loss_percentage, TAKER_FEE)
# set stop percentage for first time
if not status.stop_percentage or last_pl_net_perc - TRAIL_STOP_PERCENTAGES[state] > status.stop_percentage:
status.stop_percentage = last_pl_net_perc - \
TRAIL_STOP_PERCENTAGES[state]
return
def net_pl_percentage(perc: float, reference_fee_perc: float):
return perc - reference_fee_perc
@ -243,13 +307,13 @@ async def main(screen: Screen):
positions = [p for p in await bfx.get_active_position() if p.symbol == status.symbol]
orders = await bfx.get_active_orders(symbol)
last_price = await status.last_price()
current_price = await status.last_price()
screen.clear()
printer.print_next_line("Balance: ${} | Current {} price: {} | Current tick ({} sec): {}".format(colored_float(balance, "white"),
symbol,
colored_float(
last_price, "white", attrs=["bold"]),
current_price, "white", attrs=["bold"]),
status.ticker.seconds,
status.get_current_tick(),
))
@ -260,19 +324,21 @@ async def main(screen: Screen):
colored("POSITIONS", attrs=["underline"])))
for p in [p for p in positions if p.symbol == status.symbol]:
await status.update(p)
plfees_percentage = net_pl_percentage(
p.profit_loss_percentage, TAKER_FEE)
if plfees_percentage > GOOD_PROFIT_PERC:
status.set_state(State.PROFIT, p)
status.set_state(State.PROFIT)
elif plfees_percentage >= MIN_PROFIT_PERC and plfees_percentage < GOOD_PROFIT_PERC:
status.set_state(State.MINIMUM_PROFIT, p)
status.set_state(State.MINIMUM_PROFIT)
elif plfees_percentage >= 0.0 and plfees_percentage < MIN_PROFIT_PERC:
status.set_state(State.BREAK_EVEN, p)
status.set_state(State.BREAK_EVEN)
elif plfees_percentage < 0.0 and plfees_percentage > MAX_LOSS_PERC:
status.set_state(State.LOSS, p)
status.set_state(State.LOSS)
else:
status.set_state(State.CRITICAL, p)
status.set_state(State.CRITICAL)
status_color = status.get_current_state().color()
@ -282,11 +348,11 @@ async def main(screen: Screen):
if plfees_percentage > max_perc:
max_perc = plfees_percentage
status.add_event(Event(EventKind.NEW_MAXIMUM,
status.get_current_tick(), p))
status.get_current_tick()))
if plfees_percentage < min_perc:
min_perc = plfees_percentage
status.add_event(Event(EventKind.NEW_MINIMUM,
status.get_current_tick(), p))
status.get_current_tick()))
min_perc_colored = colored_percentage(
min_perc, "red") if min_perc < 0.0 else colored_percentage(min_perc, "green")
@ -321,7 +387,7 @@ async def main(screen: Screen):
plot(status, printer)
printer.reset_current_line()
await status.wait()
status.wait()
return
@ -344,8 +410,9 @@ def print_last_events(status: Status, n: int, printer: Printer):
def plot(status: Status, printer: Printer):
if status.ticks:
figure = termplotlib.figure()
x = range(1, status.get_current_tick() + 1)
y = [x for x in status.ticks.values()]
y = [x[0] for x in status.ticks.values()]
figure.plot(x, y, width=printer.screen.width,
height=printer.screen.height - printer.get_current_line())