[a-zA-Z]{3})") + + match = symbol_regex.match(string) + + if not match: + return None + + return CurrencyPair(match.group("base"), match.group("quote")) + + def net_pl_percentage(perc: float, reference_fee_perc: float): return perc - reference_fee_perc -def pos_to_json(pw: PositionWrapper): +def balance_to_json(balance: Balance): + return { + 'currency': balance.currency, + 'amount': balance.amount, + 'kind': balance.kind.value + } + + +def pw_to_posprop(pw: PositionWrapper): + pair = CurrencyPair.from_str(pw.position.symbol) + + if not pair: + raise ValueError + return { "id": pw.position.id, "amount": pw.position.amount, "base_price": pw.position.base_price, "state": str(pw.state()), - "symbol": pw.position.symbol, + "pair": { + "base": pair.base, + "quote": pair.quote + }, "profit_loss": pw.net_profit_loss(), "profit_loss_percentage": pw.net_profit_loss_percentage() } diff --git a/main.py b/main.py index 6e8e450..b7d0e1e 100755 --- a/main.py +++ b/main.py @@ -11,9 +11,10 @@ from flask import Flask, render_template from flask_socketio import SocketIO from bfxbot import BfxBot +from bfxbot.bfxwrapper import Balance from bfxbot.currency import Symbol -from bfxbot.models import PositionWrapper, SymbolStatus, Event, EventKind, PositionState -from bfxbot.utils import pos_to_json +from bfxbot.models import PositionWrapper, SymbolStatus, Event, EventKind +from bfxbot.utils import pw_to_posprop, balance_to_json from strategy import TrailingStopStrategy @@ -64,15 +65,16 @@ def on_close_position(message: dict): @socketio.on('connect') -def on_connect(): +async def on_connect(): # sleeping on exception to avoid race condition - ticks, prices, positions = [], [], [] + ticks, prices, positions, balances = [], [], [], [] while not ticks or not prices: try: ticks = bot.symbol_status(Symbol.BTC).all_ticks() prices = bot.symbol_status(Symbol.BTC).all_prices() positions = bot.symbol_status(Symbol.BTC).current_positions() + balances = await bot.get_balances() except KeyError: sleep(1) @@ -80,7 +82,8 @@ def on_connect(): { "ticks": ticks, "prices": prices, - "positions": list(map(pos_to_json, positions)) + "positions": list(map(pw_to_posprop, positions)), + "balances": list(map(balance_to_json, balances)) }) @@ -100,15 +103,17 @@ async def on_any_state(pw: PositionWrapper, ss: SymbolStatus): @btc_eh.on_event(EventKind.NEW_TICK) -def on_new_tick(event: Event, status: SymbolStatus): +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(pos_to_json, positions))}) + "positions": list(map(pw_to_posprop, positions)), + "balances": list(map(balance_to_json, balances))}) @btc_eh.on_any_event() diff --git a/websrc/components/Tables.tsx b/websrc/components/Tables.tsx index 7f85f68..e8a3a5d 100644 --- a/websrc/components/Tables.tsx +++ b/websrc/components/Tables.tsx @@ -1,7 +1,6 @@ import React, {Component} from "react" import {PositionProp} from "../types"; import {ClosePositionModal} from "./Overlays"; -import {symbolToPair} from "../utils"; type PositionsTableState = { showConfirmation: boolean, @@ -56,7 +55,6 @@ export class PositionsTable extends Component<{ positions: Array} renderTableRows() { return this.props.positions.map((position) => { // TODO: move symbolToPair out of here? - const currencyPair = symbolToPair(position.symbol) const stateBg = "bg-".concat(this.stateColor(position.state)).concat("-100 ") const stateText = "text-".concat(this.stateColor(position.state)).concat("-800 ") const stateClass = "px-2 inline-flex text-xs leading-5 font-semibold rounded-full ".concat(stateBg).concat(stateText) @@ -72,23 +70,26 @@ export class PositionsTable extends Component<{ positions: Array } - {currencyPair.base}/{currencyPair.quote}+{position.pair.base}/{position.pair.quote}{/*{position.}*/}- {position.base_price.toLocaleString()} {currencyPair.quote}/{currencyPair.base}+{position.base_price.toLocaleString()} {position.pair.quote}/{position.pair.base}{/*Insert total % here?*/}- {position.amount.toFixed(5)} {currencyPair.base}+{position.amount.toFixed(5)} {position.pair.base}Insert total % here?- {position.profit_loss.toLocaleString()} {currencyPair.quote}-{position.profit_loss_percentage.toFixed(2)}%+{position.profit_loss.toLocaleString()} {position.pair.quote}+{position.profit_loss_percentage.toFixed(2)}% +@@ -113,7 +114,8 @@ export class PositionsTable extends Component<{ positions: Array } render() { return ( -+ diff --git a/websrc/types.ts b/websrc/types.ts index f1a043b..7af6041 100644 --- a/websrc/types.ts +++ b/websrc/types.ts @@ -1,19 +1,26 @@ import {EventProp} from "./components/Events"; -export type PositionProp = { - id: number, - state: string, - base_price: number, +/******************************* + * Types + *******************************/ + +export type Balance = { + currency: string, amount: number, - symbol: string, - profit_loss: number, - profit_loss_percentage: number + // exchange / margin + kind: string +} + +export type CurrencyPair = { + base: string, + quote: string } export type FirstConnectMessage = { ticks: Array, prices: Array , - positions: Array + positions: Array , + balances: Array } export type NewEventMessage = { @@ -24,7 +31,8 @@ export type NewEventMessage = { export type NewTickMessage = { tick: number, price: number, - positions: Array + positions: Array , + balances: Array } export type PositionCloseMessage = { @@ -32,15 +40,19 @@ export type PositionCloseMessage = { position_id: number, } -export type CurrencyPair = { - base: string, - quote: string +export type PositionProp = { + id: number, + state: string, + base_price: number, + amount: number, + pair: CurrencyPair, + profit_loss: number, + profit_loss_percentage: number } -export type Currency = { - name: string, - short_name: string -} +/******************************* +* ENUMS +*******************************/ export enum EventName { NewTick = "new_tick",