Untitled

 avatar
unknown
python
a year ago
12 kB
9
Indexable
from textual.screen import ModalScreen, Screen
from textual.app import ComposeResult
from textual.reactive import reactive
from textual.containers import Vertical, Horizontal, Grid
from textual.widget import Widget
from textual.binding import Binding
from textual.widgets import Button, Footer, Static, Input, Label, Select
from textual import on
from model.CoinPair import CoinPair
from utils.views.balance.balancescreen import BalanceApp

import logging
logger = logging.getLogger(__name__)


class BaseCoin(Widget):

    base_coin = reactive("______")

    def render(self) -> str:
        return f"You are buying: {self.base_coin}"


class Quote(Widget):

    quote_coin = reactive("______")

    def render(self) -> str:
        return f"You are using: {self.quote_coin}"


class Quantity(Widget):

    quantity = reactive("0")

    def render(self) -> str:
        return f"You are using this for Quantity: {self.quantity}"


class Price(Widget):

    price = reactive("0")

    def render(self) -> str:
        return f"You are using this for Price Percentage: {self.price}"


class MarketPriceBuy(Widget):

    marketprice = reactive(0)

    def render(self) -> int:
        return f"Market Price: {self.marketprice}"


class TotalPriceBuy(Widget):

    total = reactive("")

    def render(self) -> str:
        return f"Total Price: {self.total}"


class SystemMessageBuy(Widget):

    message = reactive("")

    def render(self) -> str:
        return f"{self.message}"


ORDER = """MARKET
LIMIT""".splitlines()


class BuyApp(Screen):
    CSS_PATH = "buy.css"
    BINDINGS = [Binding(key="f5", action="app.pop_screen",
                        description="Close"),]

    def compose(self) -> ComposeResult:
        yield Horizontal(
            Vertical(
                Static("Buy", classes="buy"),
                Static("Exchange:"),
                Select([(exchange, exchange)
                       for exchange in self.accounts], id="exchange", allow_blank=False),
                Static("                                  "),
                Input(placeholder="Base Coin", id="base_coin"),
                BaseCoin(),
                Static("                                  "),
                Input(placeholder="Quote", id="quote_coin"),
                Quote(),
                Static("                                  "),
                Input(placeholder="Quantity", id="quantity"),
                Quantity(),
                Static("                                  "),
                Static("Order:"),
                Select([(line, line)
                       for line in ORDER], id="order", allow_blank=False),
                Static("                                  "),
                Input(placeholder="1 = 100%, 0.01 = 1%",
                      classes="hidden", id="price"),
                Price(classes="hidden"),
                Static("                                  "),
                Button("Confirm Buy", id="buy"),
                Static("                                  "),
                classes="column",
            ),
            Vertical(
                TotalPriceBuy(),
                Static("                                  "),
                MarketPriceBuy(),
                Static("                                  "),
                SystemMessageBuy(),
                Static("                                  "),
                classes="column1",
            ),
        )
        yield Footer()

    @on(Select.Changed, "#exchange")
    def select_changed_exchange(self, event: Select.Changed) -> None:
        self.exchange = str(event.value)
        logger.info(self.exchange)

    @on(Select.Changed, "#order")
    def select_changed_order_type(self, event: Select.Changed) -> None:
        self.order_type = str(event.value)
        logger.info(self.order_type)
        if self.order_type == "LIMIT":
            self.query_one(Price).set_class(not event.value, "hidden")
            self.query_one("#price").set_class(not event.value, "hidden")
        else:
            self.query_one(Price).set_class(event.value, "hidden")
            self.query_one("#price").set_class(event.value, "hidden")

    def __init__(self, accounts=[], data=None):
        super().__init__()
        self.inputs = []
        self.accounts = accounts
        self.data = data
        self.timer = None

    def load_price(self):
        self.log(f"{self.__class__} load_price")
        if self.base_coin == '' or self.quote_coin == '':
            return None

        coin_pair = CoinPair(base_coin=self.base_coin,
                             quote_coin=self.quote_coin)
        sum_price = 0
        exchange_count = 0
        for exchange in self.accounts:
            try:
                sum_price += self.accounts[exchange].get_avg_price(coin_pair)
                exchange_count += 1
            except Exception as e:
                pass
            return sum_price

        if exchange_count > 0:
            self.marketprice = str(sum_price / exchange_count)

    def cal_total_price(self):
        self.log(f"{self.__class__} cal_total_price")
        if self.quantity == '' or (self.order_type == 'LIMIT' and self.price.value == ''):
            self.total = ""
            return None

        price = self.marketprice if self.order_type == 'MARKET' else self.price.value

        try:
            total_price = float(self.quantity) * \
                float(price)
            self.total = str(total_price)
        except Exception as e:
            self.total = ""

    async def action_reset_form(self):
        self.base_coin = self.data['coin'] if 'coin' in self.data else ""
        self.exchange = self.data['exchange'] if 'exchange' in self.data else ""
        self.quote_coin = ''
        self.quantity = ''
        self.price = ''
        self.message = ''

    def validate_form(self):
        self.log(f"{self.__class__} validate_form")

        if self.exchange not in self.accounts:
            return False, "Exchange does not exist"
        elif not self.accounts[self.exchange].is_base_coin_exist(base_coin=self.query_one(BaseCoin).base_coin):
            return False, "Base coin is not supported"
        elif not self.accounts[self.exchange].is_quote_coin_exist(base_coin=self.query_one(BaseCoin).base_coin, quote_coin=self.query_one(Quote).quote_coin):
            return False, "Qoute coin is not supported"
        elif self.query_one(Quantity).quantity == '' or float(self.query_one(Quantity).quantity) < 0:
            return False, "Quantity must be greater than 0"
        elif self.order_type != 'LIMIT' and self.order_type != 'MARKET':
            return False, "Order Type must be either MARKET or LIMIT"
        elif self.order_type == 'LIMIT' and (self.query_one(Price).price == '' or float(self.query_one(Price).price) < 0):
            return False, "Price must be greater than 0"
        else:
            return True, ""

    async def on_button_pressed(self):
        logger.info("on_button_pressed")

        def check_quit(quit: bool) -> None:
            if quit:
                self.log(f"{self.__class__} on_button_pressed")
                is_valid, message = self.validate_form()
                if not is_valid:
                    self.query_one(SystemMessageBuy).message = message
                else:
                    coin_pair = CoinPair(
                        base_coin=self.query_one(BaseCoin).base_coin, quote_coin=self.query_one(Quote).quote_coin)
                    order_id, error = self.accounts[self.exchange].new_spot_buy_order(
                        coin_pair, quantity=float(self.query_one(Quantity).quantity), price=float(self.query_one(Price).price if self.order_type == 'LIMIT' else 0), order_type=self.order_type)

                    if error is None:
                        logger.info("Buy successful")
                        self.query_one(
                            SystemMessageBuy).message = f"Buy successful! Order ID: {order_id}"
                    else:
                        self.query_one(
                            SystemMessageBuy).message = f"{error.code} {error.message}"
            else:
                logger.info("Cancelled Order")
                self.query_one(
                    SystemMessageBuy).message = f"Cancelled Order"
        quote_coin = self.query_one(Quote).quote_coin
        quantity = self.query_one(Quantity).quantity
        total_value = self.query_one(TotalPriceBuy).total
        self.app.push_screen(ConfirmScreenBuy(
            self.query_one(BaseCoin).base_coin, quote_coin, total_value, quantity), check_quit)

    def on_input_changed(self, event: Input.Changed) -> None:
        if event.input.id == "base_coin":
            self.query_one(BaseCoin).base_coin = event.value
        elif event.input.id == "quote_coin":
            self.query_one(Quote).quote_coin = event.value
            self.base_coin = self.query_one(BaseCoin).base_coin
            self.quote_coin = self.query_one(Quote).quote_coin
            self.query_one(MarketPriceBuy).marketprice = self.load_price()
        elif event.input.id == "quantity":
            self.query_one(Quantity).quantity = event.value
        elif event.input.id == "price":
            self.query_one(Price).price = event.value

    @on(Input.Changed, "#price")
    def custom_function_price_change(self, event: Input.Changed):
        self.query_one(Price).price = event.value
        self.query_one(TotalPriceBuy).total = self.load_total_price()

    @on(Input.Changed, "#quantity")
    def custom_function_quantity_change(self, event: Input.Changed):
        self.query_one(Quantity).quantity = event.value
        self.query_one(TotalPriceBuy).total = self.load_total_price()

    def load_total_price(self):
        try:
            return float(self.query_one(MarketPriceBuy).marketprice) * float(self.query_one(Quantity).quantity)
        except ValueError:
            return 0


class ConfirmScreenBuy(ModalScreen[bool]):
    """Screen with a dialog to confirm purchase."""

    def __init__(self, base_coin: str, quote_coin: str, total_value: float, quantity: str):
        super().__init__()
        self.base_coin = base_coin
        self.quote_coin = quote_coin
        self.total_value = total_value
        self.quantity = quantity
        self.balance_app = BalanceApp()

    def retrieve_coin_amount(self, coin_symbol: str):
        coin_amount = self.balance_app.fetch_wallet_coin_amount(coin_symbol)
        return coin_amount

    def compose(self) -> ComposeResult:
        total_price_formatted = "{:.2f}".format(self.total_value)
        coin_symbol = self.base_coin
        amount = self.retrieve_coin_amount(coin_symbol)
        amount_base_coin = "{:.2f}".format(amount)
        amount_total = amount + self.total_value
        total_all_in_all = "{:.2f}".format(amount_total)

        yield Grid(
            Label("Here are the results of your transaction:", id="questions"),
            Label(f"Current amount of " + self.base_coin +
                  " :          " + str(amount_base_coin), id="sample"),
            Label("Quote Coin Amount of " + self.quote_coin + " :          " +
                  total_price_formatted + " " + self.quote_coin, id="display"),
            Label(self.base_coin +
                  " after purchase :          " + str(amount_total) + " or " + total_all_in_all, id="total"),
            Button("Continue?", variant="success", id="quit"),
            Button("Cancel?", variant="warning", id="cancel"),
            id="dialog",
        )

    def on_button_pressed(self, event: Button.Pressed) -> None:
        if event.button.id == "quit":
            self.dismiss(True)
        else:
            self.dismiss(False)


if __name__ == "__main__":
    app = BuyApp()

    app.run()
Editor is loading...
Leave a Comment