updated types to support currency. implemented changes in both backend and frontend. balances are sent to frontend

This commit is contained in:
Giulio De Pasquale 2020-12-21 12:54:40 +00:00
parent 9829bd2c71
commit ec35fb1366
5 changed files with 89 additions and 34 deletions

View File

@ -1,17 +1,53 @@
import re
from bfxbot.bfxwrapper import Balance
from bfxbot.models import PositionWrapper from bfxbot.models import PositionWrapper
class CurrencyPair:
def __init__(self, base: str, quote: str):
self.base: str = base
self.quote: str = quote
@staticmethod
def from_str(string: str):
symbol_regex = re.compile("t(?P<base>[a-zA-Z]{3})(?P<quote>[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): def net_pl_percentage(perc: float, reference_fee_perc: float):
return perc - reference_fee_perc 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 { return {
"id": pw.position.id, "id": pw.position.id,
"amount": pw.position.amount, "amount": pw.position.amount,
"base_price": pw.position.base_price, "base_price": pw.position.base_price,
"state": str(pw.state()), "state": str(pw.state()),
"symbol": pw.position.symbol, "pair": {
"base": pair.base,
"quote": pair.quote
},
"profit_loss": pw.net_profit_loss(), "profit_loss": pw.net_profit_loss(),
"profit_loss_percentage": pw.net_profit_loss_percentage() "profit_loss_percentage": pw.net_profit_loss_percentage()
} }

19
main.py
View File

@ -11,9 +11,10 @@ from flask import Flask, render_template
from flask_socketio import SocketIO from flask_socketio import SocketIO
from bfxbot import BfxBot from bfxbot import BfxBot
from bfxbot.bfxwrapper import Balance
from bfxbot.currency import Symbol from bfxbot.currency import Symbol
from bfxbot.models import PositionWrapper, SymbolStatus, Event, EventKind, PositionState from bfxbot.models import PositionWrapper, SymbolStatus, Event, EventKind
from bfxbot.utils import pos_to_json from bfxbot.utils import pw_to_posprop, balance_to_json
from strategy import TrailingStopStrategy from strategy import TrailingStopStrategy
@ -64,15 +65,16 @@ def on_close_position(message: dict):
@socketio.on('connect') @socketio.on('connect')
def on_connect(): async def on_connect():
# sleeping on exception to avoid race condition # sleeping on exception to avoid race condition
ticks, prices, positions = [], [], [] ticks, prices, positions, balances = [], [], [], []
while not ticks or not prices: while not ticks or not prices:
try: try:
ticks = bot.symbol_status(Symbol.BTC).all_ticks() ticks = bot.symbol_status(Symbol.BTC).all_ticks()
prices = bot.symbol_status(Symbol.BTC).all_prices() prices = bot.symbol_status(Symbol.BTC).all_prices()
positions = bot.symbol_status(Symbol.BTC).current_positions() positions = bot.symbol_status(Symbol.BTC).current_positions()
balances = await bot.get_balances()
except KeyError: except KeyError:
sleep(1) sleep(1)
@ -80,7 +82,8 @@ def on_connect():
{ {
"ticks": ticks, "ticks": ticks,
"prices": prices, "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) @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 tick = event.tick
price = status.prices[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 [] positions: List[PositionWrapper] = status.positions[event.tick] if event.tick in status.positions else []
socketio.emit("new_tick", {"tick": tick, socketio.emit("new_tick", {"tick": tick,
"price": price, "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() @btc_eh.on_any_event()

View File

@ -1,7 +1,6 @@
import React, {Component} from "react" import React, {Component} from "react"
import {PositionProp} from "../types"; import {PositionProp} from "../types";
import {ClosePositionModal} from "./Overlays"; import {ClosePositionModal} from "./Overlays";
import {symbolToPair} from "../utils";
type PositionsTableState = { type PositionsTableState = {
showConfirmation: boolean, showConfirmation: boolean,
@ -56,7 +55,6 @@ export class PositionsTable extends Component<{ positions: Array<PositionProp> }
renderTableRows() { renderTableRows() {
return this.props.positions.map((position) => { return this.props.positions.map((position) => {
// TODO: move symbolToPair out of here? // TODO: move symbolToPair out of here?
const currencyPair = symbolToPair(position.symbol)
const stateBg = "bg-".concat(this.stateColor(position.state)).concat("-100 ") const stateBg = "bg-".concat(this.stateColor(position.state)).concat("-100 ")
const stateText = "text-".concat(this.stateColor(position.state)).concat("-800 ") 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) 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<PositionProp> }
</td> </td>
<td className="px-6 py-1 whitespace-nowrap"> <td className="px-6 py-1 whitespace-nowrap">
<div className="text-sm text-gray-900">{currencyPair.base}/{currencyPair.quote}</div> <div className="text-sm text-gray-900">{position.pair.base}/{position.pair.quote}</div>
{/*<div className="text-sm text-gray-500">{position.}</div>*/} {/*<div className="text-sm text-gray-500">{position.}</div>*/}
</td> </td>
<td className="px-6 py-1 whitespace-nowrap"> <td className="px-6 py-1 whitespace-nowrap">
<div className="text-sm text-gray-900">{position.base_price.toLocaleString()} {currencyPair.quote}/{currencyPair.base}</div> <div
className="text-sm text-gray-900">{position.base_price.toLocaleString()} {position.pair.quote}/{position.pair.base}</div>
{/*<div className="text-sm text-gray-500">Insert total % here?</div>*/} {/*<div className="text-sm text-gray-500">Insert total % here?</div>*/}
</td> </td>
<td className="px-6 py-1 whitespace-nowrap"> <td className="px-6 py-1 whitespace-nowrap">
<div className="text-sm text-gray-900">{position.amount.toFixed(5)} {currencyPair.base}</div> <div className="text-sm text-gray-900">{position.amount.toFixed(5)} {position.pair.base}</div>
<div className="text-sm text-gray-500">Insert total % here?</div> <div className="text-sm text-gray-500">Insert total % here?</div>
</td> </td>
<td className="px-6 py-1 whitespace-nowrap"> <td className="px-6 py-1 whitespace-nowrap">
<div className="text-sm text-gray-900 font-semibold">{position.profit_loss.toLocaleString()} {currencyPair.quote}</div> <div
<div className={"text-sm ".concat(stateClass)}>{position.profit_loss_percentage.toFixed(2)}%</div> className="text-sm text-gray-900 font-semibold">{position.profit_loss.toLocaleString()} {position.pair.quote}</div>
<div className={"text-sm ".concat(stateClass)}>{position.profit_loss_percentage.toFixed(2)}%
</div>
</td> </td>
<td className="px-6 py-1 whitespace-nowrap text-right text-sm font-medium"> <td className="px-6 py-1 whitespace-nowrap text-right text-sm font-medium">
@ -113,7 +114,8 @@ export class PositionsTable extends Component<{ positions: Array<PositionProp> }
render() { render() {
return ( return (
<div className="flex flex-col"> <div className="flex flex-col">
<ClosePositionModal positionId={this.state.positionToClose} toggleConfirmation={this.toggleConfirmation} show={this.state.showConfirmation}/> <ClosePositionModal positionId={this.state.positionToClose} toggleConfirmation={this.toggleConfirmation}
show={this.state.showConfirmation}/>
<div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8"> <div className="-my-2 overflow-x-auto sm:-mx-6 lg:-mx-8">
<div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8"> <div className="py-2 align-middle inline-block min-w-full sm:px-6 lg:px-8">
<div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg"> <div className="shadow overflow-hidden border-b border-gray-200 sm:rounded-lg">

View File

@ -1,19 +1,26 @@
import {EventProp} from "./components/Events"; import {EventProp} from "./components/Events";
export type PositionProp = { /*******************************
id: number, * Types
state: string, *******************************/
base_price: number,
export type Balance = {
currency: string,
amount: number, amount: number,
symbol: string, // exchange / margin
profit_loss: number, kind: string
profit_loss_percentage: number }
export type CurrencyPair = {
base: string,
quote: string
} }
export type FirstConnectMessage = { export type FirstConnectMessage = {
ticks: Array<number>, ticks: Array<number>,
prices: Array<number>, prices: Array<number>,
positions: Array<PositionProp> positions: Array<PositionProp>,
balances: Array<Balance>
} }
export type NewEventMessage = { export type NewEventMessage = {
@ -24,7 +31,8 @@ export type NewEventMessage = {
export type NewTickMessage = { export type NewTickMessage = {
tick: number, tick: number,
price: number, price: number,
positions: Array<PositionProp> positions: Array<PositionProp>,
balances: Array<Balance>
} }
export type PositionCloseMessage = { export type PositionCloseMessage = {
@ -32,15 +40,19 @@ export type PositionCloseMessage = {
position_id: number, position_id: number,
} }
export type CurrencyPair = { export type PositionProp = {
base: string, id: number,
quote: string state: string,
base_price: number,
amount: number,
pair: CurrencyPair,
profit_loss: number,
profit_loss_percentage: number
} }
export type Currency = { /*******************************
name: string, * ENUMS
short_name: string *******************************/
}
export enum EventName { export enum EventName {
NewTick = "new_tick", NewTick = "new_tick",