Source code for tradeexecutor.cli.approval
"""Approve new trades in the console."""
from typing import List
import textwrap
from prompt_toolkit import print_formatted_text, HTML
from prompt_toolkit.shortcuts import checkboxlist_dialog, message_dialog
from tradeexecutor.state.state import State
from tradeexecutor.state.portfolio import Portfolio
from tradeexecutor.state.position import TradingPosition
from tradeexecutor.state.trade import TradeExecution
from tradeexecutor.strategy.approval import ApprovalModel
[docs]class CLIApprovalModel(ApprovalModel):
    """Confirm trades in the CLI before they go through.
    The terminal execution of the bot stops until the user confirms the trades.
    If no one is there to press a key then nothing happens.
    """
[docs]    def render_portfolio(self, portfolio: Portfolio) -> HTML:
        """Render the current portfolio holdings using ANSI formatting.
        https://python-prompt-toolkit.readthedocs.io/en/master/pages/printing_text.html
        :return: promp_toolkit HTML for displaying the portfolio
        """
        equity = portfolio.calculate_total_equity()
        cash = portfolio.get_cash()
        text = textwrap.dedent(f"""
            Total equity <ansigreen>${equity:,.2f}</ansigreen>
            Current cash <ansigreen>${cash:,.2f}</ansigreen>""")
        text += '\n'
        positions: List[TradingPosition] = list(portfolio.get_executed_positions())
        if positions:
            for tp in positions:
                text += f"<b>{tp.get_name()}</b>: <ansigreen>${tp.get_value():,.2f}</ansigreen> at quantity of <ansigreen>{tp.get_quantity()} {tp.get_quantity_unit_name()}</ansigreen>\n"
        else:
            text += f"<ansired>No open positions</ansired>"
        return HTML(text)
[docs]    def confirm_trades(self, state: State, trades: List[TradeExecution]) -> List[TradeExecution]:
        """Create a checkbox list to approve trades using prompt_toolkit.
        See https://python-prompt-toolkit.readthedocs.io/en/master/pages/dialogs.html#checkbox-list-dialog
        :param state: The current trade execution state
        :param trades: New trades to be executed
        :return: What trades went through human approval
        """
        # Show the user what we got
        text = self.render_portfolio(state.portfolio)
        new_trades = [
            (t.trade_id, t.get_human_description())
            for t in trades
        ]
        # Did not detect any new trades
        if len(new_trades) == 0:
            message_dialog(
                title='No new trades to execute',
                text=text).run()
            return []
        approvals_dialog = checkboxlist_dialog(
            title="New trades to execute",
            text=text,
            values=new_trades,
        )
        approvals = approvals_dialog.run()
        return [t for t in trades if t.trade_id in approvals]