implemented close position event and backend algorithm.
This commit is contained in:
parent
f6a318257a
commit
f9f14df519
@ -1,9 +1,11 @@
|
||||
from time import sleep
|
||||
from typing import Dict, List
|
||||
from typing import Dict, List, Optional, Tuple
|
||||
|
||||
from bfxapi import Order
|
||||
|
||||
from bfxbot.bfxwrapper import BfxWrapper
|
||||
from bfxbot.currency import Symbol
|
||||
from bfxbot.models import SymbolStatus, Ticker, EventHandler, Strategy, Event, EventKind
|
||||
from bfxbot.models import SymbolStatus, Ticker, EventHandler, Strategy, Event, EventKind, OFFER_PERC, PositionWrapper
|
||||
|
||||
|
||||
class BfxBot:
|
||||
@ -29,6 +31,14 @@ class BfxBot:
|
||||
for s in self.symbols:
|
||||
self.__status[s] = SymbolStatus(s)
|
||||
|
||||
def __position_wrapper_from_id(self, position_id) -> Optional[Tuple[PositionWrapper, SymbolStatus]]:
|
||||
for s in self.__status.values():
|
||||
pw = s.active_position_wrapper_from_id(position_id)
|
||||
|
||||
if pw:
|
||||
return pw, s
|
||||
return None
|
||||
|
||||
async def __update_status__(self):
|
||||
active_positions = await self.__bfx.get_active_position()
|
||||
|
||||
@ -57,11 +67,45 @@ class BfxBot:
|
||||
## TODO: handle _on_new_tick() from Strategy
|
||||
await self.__status[symbol].__add_event__(Event(EventKind.NEW_TICK, self.__ticker.current_tick))
|
||||
|
||||
async def best_position_closing_price(self, position_id: int) -> Optional[float]:
|
||||
pw: Optional[PositionWrapper] = self.__position_wrapper_from_id(position_id)
|
||||
|
||||
if not pw:
|
||||
return None
|
||||
|
||||
is_long_pos = pw.position.amount < 0
|
||||
|
||||
pub_tick = await self.__bfx.get_public_ticker(pw.position.symbol)
|
||||
|
||||
bid_price = pub_tick[0]
|
||||
ask_price = pub_tick[2]
|
||||
|
||||
if is_long_pos:
|
||||
closing_price = bid_price * (1 - OFFER_PERC / 100)
|
||||
else:
|
||||
closing_price = ask_price * (1 + OFFER_PERC / 100)
|
||||
|
||||
return closing_price
|
||||
|
||||
def close_order(self, symbol: Symbol, order_id: int):
|
||||
print(f"I would have closed order {order_id} for {symbol}")
|
||||
|
||||
def close_position(self, symbol: Symbol, position_id: int):
|
||||
print(f"I would have closed order {position_id} for {symbol}")
|
||||
async def close_position(self, position_id: int):
|
||||
pw, ss = self.__position_wrapper_from_id(position_id)
|
||||
|
||||
if not pw:
|
||||
print("Could not find open position!")
|
||||
return
|
||||
|
||||
closing_price = await self.best_position_closing_price(pw.position.id)
|
||||
|
||||
amount = pw.position.amount * -1
|
||||
|
||||
open_orders = await self.__bfx.get_active_orders(pw.position.symbol)
|
||||
|
||||
if not open_orders:
|
||||
await self.__bfx.submit_order(pw.position.symbol, closing_price, amount, Order.Type.LIMIT)
|
||||
await ss.__add_event(Event(EventKind.ORDER_SUBMITTED, ss.current_tick))
|
||||
|
||||
def set_strategy(self, symbol, strategy: Strategy):
|
||||
if symbol in self.__status:
|
||||
@ -72,13 +116,13 @@ class BfxBot:
|
||||
async def start(self):
|
||||
await self.__update_status__()
|
||||
|
||||
def symbol_event_handler(self, symbol) -> EventHandler:
|
||||
def symbol_event_handler(self, symbol) -> Optional[EventHandler]:
|
||||
if symbol not in self.__status:
|
||||
return None
|
||||
|
||||
return self.__status[symbol].eh
|
||||
|
||||
def symbol_status(self, symbol: Symbol) -> SymbolStatus:
|
||||
def symbol_status(self, symbol: Symbol) -> Optional[SymbolStatus]:
|
||||
if symbol not in self.__status:
|
||||
return None
|
||||
|
||||
|
@ -1,12 +1,16 @@
|
||||
import inspect
|
||||
import time
|
||||
from enum import Enum
|
||||
from typing import List, Dict, Tuple
|
||||
from typing import List, Dict, Tuple, Optional
|
||||
|
||||
from bfxapi import Order, Position
|
||||
|
||||
from bfxbot.currency import Symbol
|
||||
|
||||
OFFER_PERC = 0.008
|
||||
TAKER_FEE = 0.2
|
||||
MAKER_FEE = 0.1
|
||||
|
||||
|
||||
def __add_to_dict_list__(dict: Dict[int, List], k, v):
|
||||
if k not in dict:
|
||||
@ -59,6 +63,7 @@ class PositionState(Enum):
|
||||
def __repr__(self):
|
||||
return self.__str__()
|
||||
|
||||
|
||||
class Ticker:
|
||||
def __init__(self, sec) -> None:
|
||||
self.seconds: int = sec
|
||||
@ -142,12 +147,18 @@ class SymbolStatus:
|
||||
def current_price(self):
|
||||
return self.prices[self.current_tick]
|
||||
|
||||
def previous_pw(self, pid: int) -> PositionWrapper:
|
||||
def previous_pw(self, pid: int) -> Optional[PositionWrapper]:
|
||||
if self.current_tick == 1:
|
||||
return None
|
||||
|
||||
return next(filter(lambda x: x.position.id == pid, self.positions[self.current_tick - 1]))
|
||||
|
||||
def active_position_wrapper_from_id(self, position_id: int) -> Optional[PositionWrapper]:
|
||||
for pw in self.positions[self.current_tick]:
|
||||
if pw.position.id == position_id:
|
||||
return pw
|
||||
return None
|
||||
|
||||
def set_tick_price(self, tick, price):
|
||||
self.prices[tick] = price
|
||||
|
||||
|
@ -1,8 +1,5 @@
|
||||
from bfxbot.models import PositionWrapper
|
||||
|
||||
TAKER_FEE = 0.2
|
||||
MAKER_FEE = 0.1
|
||||
|
||||
|
||||
def net_pl_percentage(perc: float, reference_fee_perc: float):
|
||||
return perc - reference_fee_perc
|
||||
|
7
main.py
7
main.py
@ -24,7 +24,7 @@ async def bot_loop():
|
||||
await bot.update()
|
||||
|
||||
|
||||
asyncio.new_event_loop()
|
||||
loop = asyncio.new_event_loop()
|
||||
|
||||
dotenv.load_dotenv()
|
||||
|
||||
@ -58,9 +58,9 @@ def entry():
|
||||
@socketio.on("close_position")
|
||||
def on_close_position(message: dict):
|
||||
position_id = message['position_id']
|
||||
symbol= Symbol.from_str(message['symbol'])
|
||||
|
||||
bot.close_position(symbol, position_id)
|
||||
loop.run_until_complete(bot.close_position(position_id))
|
||||
|
||||
|
||||
@socketio.on('connect')
|
||||
def on_connect():
|
||||
@ -106,5 +106,6 @@ def on_any_event(event: Event, _):
|
||||
"kind": event.kind.name
|
||||
})
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
socketio.run(app, debug=True)
|
||||
|
@ -4,8 +4,9 @@ import sympy.abc
|
||||
from bfxapi import Position
|
||||
from sympy import Point, solve
|
||||
|
||||
from bfxbot.models import Strategy, PositionState, SymbolStatus, Event, EventKind, EventMetadata, PositionWrapper
|
||||
from bfxbot.utils import TAKER_FEE, net_pl_percentage
|
||||
from bfxbot.models import Strategy, PositionState, SymbolStatus, Event, EventKind, EventMetadata, PositionWrapper, \
|
||||
TAKER_FEE
|
||||
from bfxbot.utils import net_pl_percentage
|
||||
|
||||
|
||||
class SquaredTrailingStop:
|
||||
@ -47,7 +48,6 @@ class TrailingStopStrategy(Strategy):
|
||||
MIN_PROFIT_PERC = BREAK_EVEN_PERC + 0.3
|
||||
GOOD_PROFIT_PERC = MIN_PROFIT_PERC * 2.5
|
||||
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))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user