# #!/usr/bin/env python import asyncio import os import threading from time import sleep from typing import List import dotenv from flask import Flask, render_template from flask_socketio import SocketIO from bfxbot import BfxBot from bfxbot.bfxwrapper import Balance from bfxbot.currency import TradingPair, Symbol from bfxbot.models import PositionWrapper, SymbolStatus, Event, EventKind from bfxbot.utils import pw_to_posprop, balance_to_json from strategy import TrailingStopStrategy async def bot_loop(): await bot.start() while True: await bot.update() loop = asyncio.new_event_loop() dotenv.load_dotenv() API_KEY = os.getenv("API_KEY") API_SECRET = os.getenv("API_SECRET") app = Flask(__name__) socketio = SocketIO(app, async_mode="threading") bot = BfxBot(api_key=API_KEY, api_secret=API_SECRET, symbols=[TradingPair.BTC], quote=Symbol.USD, tick_duration=20) strategy = TrailingStopStrategy() bot.set_strategy(TradingPair.BTC, strategy) btc_eh = bot.symbol_event_handler(TradingPair.BTC) # initializing and starting bot on other thread threading.Thread(target=lambda: asyncio.run(bot_loop())).start() ################################### # Flask callbacks ################################### @app.route('/') def entry(): return render_template('index.html') ################################### # Socker.IO callbacks ################################### @socketio.on("close_position") def on_close_position(message: dict): position_id = message['position_id'] loop.run_until_complete(bot.close_position(position_id)) @socketio.on('connect') def on_connect(): # sleeping on exception to avoid race condition ticks, prices, positions, balances = [], [], [], [] while not ticks or not prices: try: ticks = bot.symbol_status(TradingPair.BTC).all_ticks() prices = bot.symbol_status(TradingPair.BTC).all_prices() positions = bot.symbol_status(TradingPair.BTC).current_positions() balances = loop.run_until_complete(bot.get_balances()) except KeyError: sleep(1) socketio.emit("first_connect", { "ticks": ticks, "prices": prices, "positions": list(map(pw_to_posprop, positions)), "balances": list(map(balance_to_json, balances)) }) @socketio.on('get_profit_loss') def on_get_profit_loss(message): start = message['start'] end = message['end'] profit_loss = loop.run_until_complete(bot.get_profit_loss(start, end)) socketio.emit("put_profit_loss", { "pl" : profit_loss[0], "pl_perc": profit_loss[1] }) ################################### # Bot callbacks ################################### @btc_eh.on_event(EventKind.CLOSE_POSITION) async def on_close_position(event: Event, _): print("CLOSING!") await bot.close_position(event.metadata.position_id) @btc_eh.on_any_position_state() async def on_any_state(pw: PositionWrapper, ss: SymbolStatus): await strategy.update_stop_percentage(pw, ss) @btc_eh.on_event(EventKind.NEW_TICK) async def on_new_tick(event: Event, status: SymbolStatus): tick = event.tick price = status.prices[event.tick] balances: List[Balance] = await bot.get_balances() positions: List[PositionWrapper] = status.positions[event.tick] if event.tick in status.positions else [] socketio.emit("new_tick", {"tick": tick, "price": price, "positions": list(map(pw_to_posprop, positions)), "balances": list(map(balance_to_json, balances))}) @btc_eh.on_any_event() def on_any_event(event: Event, _): socketio.emit("new_event", { "tick": event.tick, "kind": event.kind.name }) if __name__ == '__main__': socketio.run(app)