ghubstan db419e0bdd
Try another way of appending the events module to sys.path
Still not quite right.  The IDE wants to remove the 'unused' import,
and trying to append the sys.path able the 'import sepa_mm_bot'
also makes the IDE unhappy.
2022-03-16 19:21:39 -03:00

314 lines
10 KiB
Python

import configparser
import threading
from decimal import *
from pathlib import Path
from tkinter import *
import path_config
import sepa_mm_bot
from events.event_dispatcher import EventDispatcher
from events.trade_event import TradeEvent
from logger import log
config = configparser.ConfigParser()
config.read('sepa_mm_bot.ini')
OUTPUT_PATH = Path(__file__).parent
ASSETS_PATH = OUTPUT_PATH / Path('./assets')
WINDOW_WIDTH = 970
WINDOW_HEIGHT = 575
def relative_to_assets(path: str) -> Path:
return ASSETS_PATH / Path(path)
window = Tk()
window.title('Bisq API SEPA Market Maker Bot')
window_geometry = str(WINDOW_WIDTH) + 'x' + str(WINDOW_HEIGHT) + '+100+50'
window.geometry(window_geometry)
window.configure(bg='#F2F2F2')
window.iconphoto(False, PhotoImage(file=relative_to_assets('window_icon.png')))
# Initialize editable input fields from the .ini file.
price_margin_input = StringVar()
price_margin_input.set(Decimal(config.get('general', 'reference_price_margin')))
spread_input = StringVar()
spread_input.set(Decimal(config.get('general', 'spread')))
btc_amount_input = StringVar()
btc_amount_input.set(round(int(config.get('general', 'amount')) * 0.00000001, 8))
# Polling interval is not editable in UI. Edit the .init file and restart UI.
offers_poll_interval_in_sec = int(config.get('general', 'offers_poll_interval_in_sec'))
# Payment account id is not editable in UI.
# If you want to change it, shut down the bot and edit the .ini file.
sepa_payment_account_id = config.get('general', 'sepa_payment_account_id')
# Set up event dispatcher, but delay setting up listeners until bot and canvass are created.
event_dispatcher = EventDispatcher()
# These globals are set when we handle a trade event from the event_dispatcher.
trade_id_for_start_payment_msg = None
trade_id_for_payment_received_msg = None
# Create the bot instance this UI controls.
bot = sepa_mm_bot.SepaMMBot('localhost', 9998, 'xyz', event_dispatcher)
def start():
start_thread = threading.Thread(name='Bot', target=start_bot, args=(), daemon=False)
start_thread.start()
def start_bot():
disable_button(start_button)
enable_button(stop_button)
reference_price_margin = Decimal(price_margin_input.get())
target_spread = Decimal(spread_input.get())
bot.start(int(offers_poll_interval_in_sec),
reference_price_margin,
target_spread,
int(Decimal(btc_amount_input.get()) * 10000000), # Convert the BTC input amt to Satoshis.
sepa_payment_account_id)
def stop():
shutdown_thread = threading.Thread(name='Shutdown', target=stop_bot, args=(), daemon=False)
shutdown_thread.start()
def stop_bot():
global trade_id_for_start_payment_msg
trade_id_for_start_payment_msg = None
disable_button(payment_sent_button)
canvas.itemconfig(manual_buy_trade_instructions, text='')
global trade_id_for_payment_received_msg
trade_id_for_payment_received_msg = None
disable_button(payment_received_button)
canvas.itemconfig(manual_sell_trade_instructions, text='')
disable_button(stop_button)
bot.shutdown()
enable_button(start_button)
def send_payment_started_msg():
log.info('payment_sent_button clicked')
disable_button(payment_sent_button)
global trade_id_for_start_payment_msg
bot.send_payment_started_msg(trade_id_for_start_payment_msg)
ui_update_text = 'Payment started message was sent for trade' + '\n' \
+ trade_id_for_start_payment_msg
canvas.itemconfig(manual_buy_trade_instructions, text=ui_update_text)
trade_id_for_start_payment_msg = None
def send_payment_received_msg():
log.info('payment_received_button clicked')
disable_button(payment_received_button)
global trade_id_for_payment_received_msg
bot.send_payment_received_msg(trade_id_for_payment_received_msg)
ui_update_text = 'Payment received confirmation message was sent for trade' + '\n' \
+ trade_id_for_payment_received_msg
canvas.itemconfig(manual_sell_trade_instructions, text=ui_update_text)
trade_id_for_payment_received_msg = None
canvas = Canvas(window,
bg='#F2F2F2',
width=WINDOW_WIDTH - 12,
height=WINDOW_HEIGHT - 12,
bd=5,
highlightthickness=0,
relief='ridge')
canvas.place(x=0, y=0)
canvas.create_text(
275.0,
16.0,
anchor='nw',
text='SEPA EUR / BTC Market Maker Bot',
fill='#000000',
font=('IBMPlexSans Bold', 28 * -1))
bisq_logo_file = PhotoImage(file=relative_to_assets('bisq_logo.png'))
bisq_logo_image = canvas.create_image(116.0, 34.0, image=bisq_logo_file)
canvas.create_text(
39.0,
97.0,
anchor='nw',
text='Reference Market Price Margin:',
fill='#000000',
font=('IBMPlexSans SemiBold', 14 * -1))
canvas.create_text(
395.0,
97.0,
anchor='nw',
text='Spread:',
fill='#000000',
font=('IBMPlexSans SemiBold', 13 * -1))
canvas.create_text(
336.0,
97.0,
anchor='nw',
text='%',
fill='#000000',
font=('IBMPlexSans SemiBold', 14 * -1))
canvas.create_text(
506.0,
97.0,
anchor='nw',
text='%',
fill='#000000',
font=('IBMPlexSans SemiBold', 14 * -1))
canvas.create_text(
568.0,
97.0,
anchor='nw',
text='BTC Amount:',
fill='#000000',
font=('IBMPlexSans SemiBold', 14 * -1))
manual_buy_trade_instructions = canvas.create_text(
57.0,
226.0,
anchor='nw',
text='Manual Buy BTC Instructions',
fill='#000000',
font=('IBMPlexSans SemiBold', 14 * -1))
manual_sell_trade_instructions = canvas.create_text(
520.0,
226.0,
anchor='nw',
text='Manual Sell BTC Instructions',
fill='#000000',
font=('IBMPlexSans SemiBold', 14 * -1))
# https://pythonguides.com/python-tkinter-button/
# https://www.askpython.com/python-modules/tkinter/change-button-state
start_button_image = PhotoImage(file=relative_to_assets('start_button.png'))
start_button = Button(
state=NORMAL,
bg='#F2F2F2',
image=start_button_image,
borderwidth=0,
highlightthickness=0,
command=lambda: start(),
relief='flat')
start_button.place(x=140.0, y=135.0, width=150.0, height=45.0)
stop_button_image = PhotoImage(file=relative_to_assets('stop_button.png'))
stop_button = Button(
state=DISABLED,
bg='#F2F2F2',
image=stop_button_image,
borderwidth=0,
highlightthickness=0,
command=lambda: stop(),
relief='flat')
stop_button.place(x=600.0, y=135.0, width=150.0, height=45.0)
payment_sent_button_image = PhotoImage(file=relative_to_assets('payment_sent_button.png'))
payment_sent_button = Button(
state=DISABLED,
bg='#F2F2F2',
image=payment_sent_button_image,
borderwidth=0,
highlightthickness=0,
command=lambda: send_payment_started_msg(),
relief='flat')
payment_sent_button.place(x=140.0, y=470.0, width=150.0, height=45.0)
payment_received_button__image = PhotoImage(file=relative_to_assets('payment_received_button.png'))
payment_received_button = Button(
state=DISABLED,
bg='#F2F2F2',
image=payment_received_button__image,
borderwidth=0,
highlightthickness=0,
command=lambda: send_payment_received_msg(),
relief='flat')
payment_received_button.place(x=600.0, y=470.0, width=150.0, height=45.0)
reference_price_margin_image = PhotoImage(file=relative_to_assets('reference_price_margin_entry.png'))
reference_price_margin_bg = canvas.create_image(303.5, 106.5, image=reference_price_margin_image)
reference_price_margin_entry = Entry(
bd=0,
bg='#FFFFFF',
highlightthickness=0,
textvariable=price_margin_input,
justify='right')
reference_price_margin_entry.place(x=276.0, y=96.0, width=55.0, height=19.0)
spread_image = PhotoImage(file=relative_to_assets('spread_entry.png'))
spread_bg = canvas.create_image(476.5, 104.5, image=spread_image)
spread_entry = Entry(
bd=0,
bg='#FFFFFF',
highlightthickness=0,
textvariable=spread_input,
justify='right'
)
spread_entry.place(x=452.0, y=94.0, width=49.0, height=19.0)
btc_amount_image = PhotoImage(file=relative_to_assets('btc_amount_entry.png'))
btc_amount_bg = canvas.create_image(749.0, 104.5, image=btc_amount_image)
btc_amount_entry = Entry(
bd=0,
bg='#FFFFFF',
highlightthickness=0,
textvariable=btc_amount_input,
justify='right')
btc_amount_entry.place(x=671.0, y=94.0, width=156.0, height=19.0)
def disable_button(button):
button['state'] = DISABLED
def enable_button(button):
button['state'] = NORMAL
# Event handling method must be declared before listeners are set up.
def handle_trade_event(event):
log.info('\tHandling %s...', str(event.type))
trade = event.data.trade
instructions = event.data.instructions
trade_summary = bot.get_trade_payment_summary(trade)
ui_update_text = instructions + '\n\n' + trade_summary
if event.type == TradeEvent.MUST_SEND_PAYMENT_STARTED_MSG_EVENT:
global trade_id_for_start_payment_msg
if trade_id_for_start_payment_msg is None:
canvas.itemconfig(manual_buy_trade_instructions, text=ui_update_text)
trade_id_for_start_payment_msg = trade.trade_id
enable_button(payment_sent_button)
else:
log.warning('Already waiting for manual step for trade %s', trade_id_for_start_payment_msg)
elif event.type == TradeEvent.MUST_SEND_PAYMENT_RECEIVED_MSG_EVENT:
global trade_id_for_payment_received_msg
if trade_id_for_payment_received_msg is None:
canvas.itemconfig(manual_sell_trade_instructions, text=ui_update_text)
trade_id_for_payment_received_msg = trade.trade_id
enable_button(payment_received_button)
else:
log.warning('Already waiting for manual step for trade %s', trade_id_for_payment_received_msg)
else:
raise 'Invalid event type ' + event.type
event_dispatcher.add_event_listener(TradeEvent.MUST_SEND_PAYMENT_STARTED_MSG_EVENT, handle_trade_event)
event_dispatcher.add_event_listener(TradeEvent.MUST_SEND_PAYMENT_RECEIVED_MSG_EVENT, handle_trade_event)
window.resizable(False, False)
window.mainloop()