state handlers
This commit is contained in:
parent
fb1433a13d
commit
af089554ce
137
main.py
137
main.py
@ -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())
|
||||
|
Loading…
Reference in New Issue
Block a user