diff --git a/bfxbot/bfxbot.py b/bfxbot/bfxbot.py index 10ff142..13a32f8 100644 --- a/bfxbot/bfxbot.py +++ b/bfxbot/bfxbot.py @@ -8,6 +8,14 @@ from bfxbot.models import SymbolStatus, Ticker, EventHandler, Strategy class BfxBot: def __init__(self, api_key: str, api_secret: str, tick_duration: int = 1): + if api_key is None: + print("API_KEY is not set!") + raise ValueError + + if api_secret is None: + print("API_SECRET is not set!") + raise ValueError + self.bfx: BfxWrapper = BfxWrapper(api_key, api_secret) self.status: Dict[Symbol, SymbolStatus] = {} self.ticker: Ticker = Ticker(tick_duration) diff --git a/main.py b/main.py index d1a60a0..2a135f0 100755 --- a/main.py +++ b/main.py @@ -485,70 +485,34 @@ # if __name__ == "__main__": # asyncio.run(Screen.wrapper(main)) import asyncio -import shutil -from typing import List +import os -from asciimatics.screen import Screen, ManagedScreen -from asciimatics.widgets import Frame, Layout, Text -from bfxapi import Position +import dotenv from bfxbot import BfxBot -import dotenv -import os -import bfxapi - from bfxbot.currency import Symbol -from bfxbot.models import Strategy, SymbolStatus, PositionState, Event, EventKind -from bfxbot.utils import TAKER_FEE, net_pl_percentage +from bfxbot.models import PositionState from strategy import TrailingStopStrategy dotenv.load_dotenv() +API_KEY = os.getenv("API_KEY") +API_SECRET = os.getenv("API_SECRET") + +bot = BfxBot(api_key=API_KEY, api_secret=API_SECRET) +strategy = TrailingStopStrategy() +bot.set_strategy(Symbol.BTC, strategy) +eh = bot.event_handler(Symbol.BTC) + + +@eh.on_state(PositionState.PROFIT) +def on_min_profit(ss, pw): + print("Minimum profit!") async def main(): - API_KEY = os.getenv("API_KEY") - API_SECRET = os.getenv("API_SECRET") - - if API_KEY is None: - print("API_KEY is not set! Set the var in the .env file.") - return - - if API_SECRET is None: - print("API_SECRET is not set! Set the var in the .env file.") - return - - bot = BfxBot(api_key=API_KEY, api_secret=API_SECRET, tick_duration=20) - strategy = TrailingStopStrategy() - bot.set_strategy(Symbol.BTC, strategy) - eh = bot.event_handler(Symbol.BTC) - await bot.start() while True: - await bot_loop(bot) - - -@ManagedScreen -async def bot_loop(bot: BfxBot, screen: Screen = None): - prepare_tui(screen) - screen.play() - - await bot.update() - -def prepare_tui(screen: Screen): - w, h = shutil.get_terminal_size() - - frame = Frame(screen, w, h, has_border=False) - - info_layout = Layout([1]) - graph_layout = Layout([1]) - footer_layout = Layout([1]) - - frame.add_layout(info_layout) - frame.add_layout(graph_layout) - frame.add_layout(footer_layout) - - info_layout.add_widget(Text(label="Test")) - + await bot.update() if __name__ == '__main__': asyncio.run(main()) \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3b19e20..46e2c48 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,5 +1,2 @@ -asciimatics==1.12.0 -asyncio==3.4.3 -playsound==1.2.2 -termcolor==1.1.0 -termplotlib==0.3.2 +python-dotenv~=0.15.0 +sympy~=1.7 \ No newline at end of file diff --git a/strategy.py b/strategy.py index 0fa6d9d..32dce20 100644 --- a/strategy.py +++ b/strategy.py @@ -1,22 +1,60 @@ from typing import List -from bfxapi import Position +import sympy.abc +from sympy import Point, solve from bfxbot.models import Strategy, PositionState, SymbolStatus, Event, EventKind from bfxbot.utils import TAKER_FEE, net_pl_percentage +class Position(object): + def __init__(self): + self.id = None + self.profit_loss_percentage = None + + pass + +class SquaredTrailingStop: + def __init__(self, p_min: Point, p_max: Point): + a = sympy.abc.a + b = sympy.abc.b + c = sympy.abc.c + + self.p_min = p_min + 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 + + s = solve([e1, e2, e3])[0] + + self.a, self.b, self.c = s[a], s[b], s[c] + + def y(self, x): + def inter_y(x): + return self.a * (x + self.b)**2 + self.c + + if x < self.p_min.x: + return self.p_min.y + elif x > self.p_max.x: + return self.p_max.y + else: + return inter_y(x) + + def profit(self, x): + if x < self.p_min.x: + return 0 + return x - self.y(x) + class TrailingStopStrategy(Strategy): BREAK_EVEN_PERC = TAKER_FEE - MIN_PROFIT_PERC = TAKER_FEE * 2.5 - GOOD_PROFIT_PERC = MIN_PROFIT_PERC * 1.5 + MIN_PROFIT_PERC = BREAK_EVEN_PERC + 0.3 + GOOD_PROFIT_PERC = MIN_PROFIT_PERC * 2.5 MAX_LOSS_PERC = -3.75 - OFFER_PERC = 0.01 + OFFER_PERC = 0.005 - TRAIL_STOP_PERCENTAGES = { - PositionState.MINIMUM_PROFIT: 0.27, - PositionState.PROFIT: 0.14 - } + 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]): events = [] @@ -38,16 +76,18 @@ class TrailingStopStrategy(Strategy): if not prev or prev.state == state: return state, events - if state ==PositionState.PROFIT: + if state == PositionState.PROFIT: events.append(Event(EventKind.REACHED_GOOD_PROFIT, position.id, ss.current_tick)) elif state == PositionState.MINIMUM_PROFIT: events.append(Event(EventKind.REACHED_MIN_PROFIT, position.id, ss.current_tick)) elif state == PositionState.BREAK_EVEN: events.append(Event(EventKind.REACHED_BREAK_EVEN, position.id, ss.current_tick)) - elif state== PositionState.LOSS: + elif state == PositionState.LOSS: events.append(Event(EventKind.REACHED_LOSS, position.id, 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)) return state, events + +