tailwind #9
0
bfxbot/__init__.py
Normal file
0
bfxbot/__init__.py
Normal file
87
bfxbot/bfxbot.py
Normal file
87
bfxbot/bfxbot.py
Normal file
@ -0,0 +1,87 @@
|
||||
import inspect
|
||||
from typing import Dict
|
||||
|
||||
from bfxapi import Position
|
||||
|
||||
from bfxbot.bfxwrapper import BfxWrapper
|
||||
from bfxbot.event import Event, EventKind
|
||||
from bfxbot.status import Status, PositionState, Ticker
|
||||
|
||||
|
||||
class BfxBot:
|
||||
class EventHandler:
|
||||
def __init__(self):
|
||||
self.event_handlers = {}
|
||||
self.state_handlers = {}
|
||||
|
||||
async def call_event(self, event: Event, status: Status):
|
||||
value = event.kind.value
|
||||
if value in self.event_handlers:
|
||||
for h in self.event_handlers[value]:
|
||||
if inspect.iscoroutinefunction(h):
|
||||
await h(event, status)
|
||||
else:
|
||||
h(event, status)
|
||||
|
||||
async def call_state(self, state: PositionState, status: Status):
|
||||
if state in self.state_handlers:
|
||||
for h in self.state_handlers[state]:
|
||||
if inspect.iscoroutinefunction(h):
|
||||
await h(status)
|
||||
else:
|
||||
h(status)
|
||||
|
||||
def on_event(self, kind: EventKind):
|
||||
value = kind.value
|
||||
|
||||
def registerhandler(handler):
|
||||
if value in self.event_handlers:
|
||||
self.event_handlers[value].append(handler)
|
||||
else:
|
||||
self.event_handlers[value] = [handler]
|
||||
return handler
|
||||
|
||||
return registerhandler
|
||||
|
||||
def on_state(self, state: PositionState):
|
||||
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
|
||||
|
||||
def __init__(self, api_key: str, api_secret: str, tick_duration: int = 60):
|
||||
self.bfx: BfxWrapper = BfxWrapper(api_key, api_secret)
|
||||
self.status: Dict[str, Status] = {}
|
||||
self.eh: BfxBot.EventHandler = BfxBot.EventHandler()
|
||||
self.ticker: Ticker = Ticker(tick_duration)
|
||||
|
||||
await self.__update_status__()
|
||||
|
||||
async def __update_status__(self):
|
||||
active_positions = await self.bfx.get_active_position()
|
||||
|
||||
for p in active_positions:
|
||||
if p.symbol not in self.status:
|
||||
self.status[p.symbol] = Status(p.symbol)
|
||||
|
||||
self.status[p.symbol].positions[self.ticker.current_tick].append(p)
|
||||
|
||||
for symbol in self.status.keys():
|
||||
active_orders = await self.bfx.get_active_orders(symbol)
|
||||
|
||||
for o in active_orders:
|
||||
if symbol not in self.status:
|
||||
self.status[symbol] = Status(symbol)
|
||||
self.status[symbol].orders[self.ticker.current_tick].append(o)
|
||||
|
||||
def __trigger__events(self):
|
||||
return
|
||||
|
||||
async def update(self):
|
||||
self.ticker.inc()
|
||||
await self.__update_status__()
|
||||
|
29
bfxbot/bfxwrapper.py
Normal file
29
bfxbot/bfxwrapper.py
Normal file
@ -0,0 +1,29 @@
|
||||
from bfxapi.rest.bfx_rest import BfxRest
|
||||
|
||||
|
||||
class BfxWrapper(BfxRest):
|
||||
def __init__(self, api_key: str, api_secret: str):
|
||||
super().__init__(API_KEY=api_key, API_SECRET=api_secret)
|
||||
|
||||
async def get_current_prices(self, symbol) -> (float, float, float):
|
||||
tickers = await self.get_public_ticker(symbol)
|
||||
|
||||
bid_price = tickers[0]
|
||||
ask_price = tickers[2]
|
||||
ticker_price = tickers[6]
|
||||
|
||||
return bid_price, ask_price, ticker_price
|
||||
|
||||
async def get_usd_balance(self):
|
||||
balance = 0.0
|
||||
|
||||
wallets = await self.get_wallets()
|
||||
|
||||
for w in wallets:
|
||||
if w.currency == "USD":
|
||||
balance += w.balance
|
||||
else:
|
||||
current_price = await self.get_current_prices(f"t{w.currency}USD")
|
||||
balance += current_price * w.balance
|
||||
|
||||
return balance
|
24
bfxbot/event.py
Normal file
24
bfxbot/event.py
Normal file
@ -0,0 +1,24 @@
|
||||
from enum import Enum
|
||||
|
||||
|
||||
class EventKind(Enum):
|
||||
NEW_MINIMUM = 1,
|
||||
NEW_MAXIMUM = 2,
|
||||
REACHED_LOSS = 3,
|
||||
REACHED_BREAK_EVEN = 4,
|
||||
REACHED_MIN_PROFIT = 5,
|
||||
REACHED_GOOD_PROFIT = 6,
|
||||
REACHED_MAX_LOSS = 7,
|
||||
CLOSE_POSITION = 8,
|
||||
TRAILING_STOP_SET = 9,
|
||||
TRAILING_STOP_MOVED = 10,
|
||||
ORDER_SUBMITTED = 11,
|
||||
|
||||
|
||||
class Event:
|
||||
def __init__(self, kind: EventKind, tick: int) -> None:
|
||||
self.kind: EventKind = kind
|
||||
self.tick: int = tick
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f"{self.kind.name} @ Tick {self.tick}"
|
78
bfxbot/status.py
Normal file
78
bfxbot/status.py
Normal file
@ -0,0 +1,78 @@
|
||||
import time
|
||||
from enum import Enum
|
||||
from typing import List, Dict
|
||||
|
||||
from bfxapi import Position, Order
|
||||
|
||||
from bfxbot.bfxbot import BfxBot
|
||||
from bfxbot.event import Event, EventKind
|
||||
|
||||
|
||||
class Ticker:
|
||||
def __init__(self, sec) -> None:
|
||||
self.seconds: int = sec
|
||||
self.start_time = time.time()
|
||||
self.current_tick: int = 1
|
||||
|
||||
def inc(self):
|
||||
self.current_tick += 1
|
||||
|
||||
|
||||
class PositionState(Enum):
|
||||
CRITICAL = -1,
|
||||
LOSS = 0,
|
||||
BREAK_EVEN = 1,
|
||||
MINIMUM_PROFIT = 2,
|
||||
PROFIT = 3
|
||||
|
||||
def color(self) -> str:
|
||||
if self == self.LOSS or self == self.CRITICAL:
|
||||
return "red"
|
||||
elif self == self.BREAK_EVEN:
|
||||
return "yellow"
|
||||
else:
|
||||
return "green"
|
||||
|
||||
|
||||
class Status:
|
||||
def __init__(self, symbol):
|
||||
self.events: List[Event] = []
|
||||
self.symbol = symbol
|
||||
self.current_state: PositionState = PositionState.LOSS
|
||||
self.stop_percentage: float = None
|
||||
self.orders: Dict[int, List[Order]] = {}
|
||||
self.positions: Dict[int, List[Position]] = {}
|
||||
|
||||
def last_events(self, n) -> List[Event]:
|
||||
return self.events[-n:]
|
||||
|
||||
def last_position(self) -> Position:
|
||||
return [self.ticker.current_tick][1]
|
||||
|
||||
async def add_event(self, event: Event, event_handler: BfxBot.EventHandler):
|
||||
self.events.append(event)
|
||||
await event_handler.call_event(event, self)
|
||||
|
||||
async def set_state(self, state: PositionState, event_handler: BfxBot.EventHandler, tick: int):
|
||||
if self.current_state != state:
|
||||
event: Event = None
|
||||
|
||||
if state == PositionState.CRITICAL:
|
||||
event = Event(EventKind.REACHED_MAX_LOSS, tick)
|
||||
elif state == PositionState.LOSS:
|
||||
event = Event(EventKind.REACHED_LOSS, tick)
|
||||
elif state == PositionState.BREAK_EVEN:
|
||||
event = Event(EventKind.REACHED_BREAK_EVEN, tick)
|
||||
elif state == PositionState.MINIMUM_PROFIT:
|
||||
event = Event(EventKind.REACHED_MIN_PROFIT, tick)
|
||||
elif state == PositionState.PROFIT:
|
||||
event = Event(EventKind.REACHED_GOOD_PROFIT, tick)
|
||||
|
||||
self.events.append(event)
|
||||
await event_handler.call_event(event, self)
|
||||
self.current_state = state
|
||||
|
||||
await event_handler.call_state(self.current_state, self)
|
||||
|
||||
def get_current_state(self) -> PositionState:
|
||||
return self.current_state
|
Loading…
Reference in New Issue
Block a user