mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-11-02 07:23:25 +00:00
* remove theme refs * will come for dtabento later * remove theme refs except databento * ruff * remove mpl variables * mpl still used in po * move databento to plotly * pylint * ruff * theme ref to openbb_terminal * juans sugg * fix: removed raw arg in `display_candle` which could return the dataframe and error, added figure to `export_data` --------- Co-authored-by: teh_coderer <me@tehcoderer.com>
402 lines
13 KiB
Python
402 lines
13 KiB
Python
"""Oanda View."""
|
|
__docformat__ = "numpy"
|
|
|
|
import logging
|
|
from typing import Dict, Optional, Union
|
|
|
|
import pandas as pd
|
|
|
|
from openbb_terminal import OpenBBFigure, theme
|
|
from openbb_terminal.core.plots.plotly_ta.ta_class import PlotlyTA
|
|
from openbb_terminal.decorators import check_api_key, log_start_end
|
|
from openbb_terminal.forex.oanda.oanda_model import (
|
|
account_summary_request,
|
|
cancel_pending_order_request,
|
|
close_trades_request,
|
|
create_order_request,
|
|
fx_price_request,
|
|
get_calendar_request,
|
|
get_candles_dataframe,
|
|
open_positions_request,
|
|
open_trades_request,
|
|
order_history_request,
|
|
orderbook_plot_data_request,
|
|
pending_orders_request,
|
|
positionbook_plot_data_request,
|
|
)
|
|
from openbb_terminal.rich_config import console
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def get_fx_price(account: str, instrument: Union[str, None] = ""):
|
|
"""View price for loaded currency pair.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda account ID
|
|
instrument : Union[str, None]
|
|
Instrument code or None
|
|
"""
|
|
data = fx_price_request(accountID=account, instrument=instrument)
|
|
if data and data is not None and "prices" in data:
|
|
bid = data["prices"][0]["bids"][0]["price"]
|
|
ask = data["prices"][0]["asks"][0]["price"]
|
|
console.print(f"{instrument if instrument else ''}" + " Bid: " + bid)
|
|
console.print(f"{instrument if instrument else ''}" + " Ask: " + ask)
|
|
else:
|
|
console.print("No data was retrieved.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def get_account_summary(accountID: str):
|
|
"""Print Oanda account summary.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
"""
|
|
df_summary = account_summary_request(accountID)
|
|
if df_summary is not False and not df_summary.empty:
|
|
console.print(df_summary.to_string(index=False, header=False))
|
|
else:
|
|
console.print("No data was retrieved.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def get_order_book(
|
|
accountID: str,
|
|
instrument: str = "",
|
|
external_axes: bool = False,
|
|
):
|
|
"""
|
|
Plot the orderbook for the instrument if Oanda provides one.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
instrument : str
|
|
The loaded currency pair
|
|
external_axes : bool, optional
|
|
Whether to return the figure object or not, by default False
|
|
"""
|
|
df_orderbook_data = orderbook_plot_data_request(
|
|
accountID=accountID, instrument=instrument
|
|
)
|
|
if df_orderbook_data is not False and not df_orderbook_data.empty:
|
|
pd.set_option("display.max_rows", None)
|
|
# HELP WANTED!
|
|
# TODO:
|
|
# An early contributor left "magic constants" in this function
|
|
# help is needed to figure out the rationale behind these or
|
|
# refactor it to not include the magic numbers.
|
|
df_orderbook_data = df_orderbook_data.take(range(527, 727, 1))
|
|
book_plot(
|
|
df_orderbook_data, instrument, "Order Book", external_axes=external_axes
|
|
)
|
|
else:
|
|
console.print("No data was retrieved.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def get_position_book(
|
|
accountID: str, instrument: str = "", external_axes: bool = False
|
|
):
|
|
"""Plot a position book for an instrument if Oanda provides one.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
instrument : str
|
|
The loaded currency pair
|
|
external_axes : bool, optional
|
|
Whether to return the figure object or not, by default False
|
|
"""
|
|
df_positionbook_data = positionbook_plot_data_request(
|
|
accountID=accountID, instrument=instrument
|
|
)
|
|
if df_positionbook_data is not False and not df_positionbook_data.empty:
|
|
pd.set_option("display.max_rows", None)
|
|
# HELP WANTED!
|
|
# TODO:
|
|
# An early contributor left "magic constants" in this function
|
|
# help is needed to figure out the rationale behind these or
|
|
# refactor it to not include the magic numbers.
|
|
df_positionbook_data = df_positionbook_data.take(range(219, 300, 1))
|
|
book_plot(
|
|
df_positionbook_data,
|
|
instrument,
|
|
"Position Book",
|
|
external_axes=external_axes,
|
|
)
|
|
else:
|
|
console.print("No data was retrieved.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def list_orders(accountID: str, order_state: str = "PENDING", order_count: int = 0):
|
|
"""List order history.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
order_state : str
|
|
Filter orders by a specific state ("PENDING", "CANCELLED", etc.)
|
|
order_count : int
|
|
Limit the number of orders to retrieve
|
|
"""
|
|
df_order_list = order_history_request(order_state, order_count, accountID)
|
|
if df_order_list is not False and not df_order_list.empty:
|
|
console.print(df_order_list)
|
|
else:
|
|
console.print("No data was retrieved.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def create_order(accountID: str, instrument: str = "", price: int = 0, units: int = 0):
|
|
"""Create a buy/sell order.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
instrument : str
|
|
The loaded currency pair
|
|
price : int
|
|
The price to set for the limit order.
|
|
units : int
|
|
The number of units to place in the order request.
|
|
"""
|
|
df_orders = create_order_request(price, units, instrument, accountID)
|
|
if df_orders is not False and not df_orders.empty:
|
|
console.print(df_orders.to_string(index=False))
|
|
else:
|
|
console.print("No data was returned from Oanda.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def cancel_pending_order(accountID: str, orderID: str = ""):
|
|
"""Cancel a Pending Order.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
orderID : str
|
|
The pending order ID to cancel.
|
|
"""
|
|
order_id = cancel_pending_order_request(orderID, accountID)
|
|
if order_id is not False and order_id is not None:
|
|
console.print(f"Order {order_id} canceled.")
|
|
else:
|
|
console.print("No data was returned from Oanda.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def get_open_positions(accountID: str):
|
|
"""Get information about open positions.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
"""
|
|
df_positions = open_positions_request(accountID)
|
|
if df_positions is not False and not df_positions.empty:
|
|
console.print(df_positions.to_string(index=False))
|
|
else:
|
|
console.print("No data was returned from Oanda.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def get_pending_orders(accountID: str):
|
|
"""Get information about pending orders.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
"""
|
|
df_pending = pending_orders_request(accountID)
|
|
if df_pending is not False and not df_pending.empty:
|
|
console.print(df_pending.to_string(index=False))
|
|
elif df_pending is not False and df_pending.empty:
|
|
console.print("No pending orders.\n")
|
|
else:
|
|
console.print("No data was returned from Oanda.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def get_open_trades(accountID: str):
|
|
"""View open trades.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
"""
|
|
df_trades = open_trades_request(accountID)
|
|
if isinstance(df_trades, pd.DataFrame) and not df_trades.empty:
|
|
console.print(df_trades.to_string(index=False))
|
|
elif df_trades is not False and df_trades.empty:
|
|
console.print("No trades were found.\n")
|
|
else:
|
|
console.print("No data was returned from Oanda.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def close_trade(accountID: str, orderID: str = "", units: Union[int, None] = None):
|
|
"""Close a trade.
|
|
|
|
Parameters
|
|
----------
|
|
accountID : str
|
|
Oanda user account ID
|
|
orderID : str
|
|
ID of the order to close
|
|
units : Union[int, None]
|
|
Number of units to close. If empty default to all.
|
|
"""
|
|
df_trades = close_trades_request(orderID, units, accountID)
|
|
if df_trades is not False and not df_trades.empty:
|
|
console.print(df_trades.to_string(index=False))
|
|
elif df_trades is not False and df_trades.empty:
|
|
console.print("No trades were found.\n")
|
|
else:
|
|
console.print("No data was returned from Oanda.\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def show_candles(
|
|
instrument: str = "",
|
|
granularity: str = "D",
|
|
candlecount: int = 180,
|
|
additional_charts: Optional[Dict[str, bool]] = None,
|
|
external_axes: bool = False,
|
|
):
|
|
"""Show candle chart.
|
|
|
|
Note that additional plots (ta indicators) not supported in external axis mode.
|
|
|
|
Parameters
|
|
----------
|
|
instrument : str
|
|
The loaded currency pair
|
|
granularity : str, optional
|
|
The timeframe to get for the candle chart. Seconds: S5, S10, S15, S30
|
|
Minutes: M1, M2, M4, M5, M10, M15, M30 Hours: H1, H2, H3, H4, H6, H8, H12
|
|
Day (default): D, Week: W Month: M,
|
|
candlecount : int, optional
|
|
Limit for the number of data points
|
|
additional_charts : Dict[str, bool]
|
|
A dictionary of flags to include additional charts
|
|
external_axes : bool, optional
|
|
Whether to return the figure object or not, by default False
|
|
"""
|
|
df_candles = get_candles_dataframe(instrument, granularity, candlecount)
|
|
|
|
if not (has_volume := False) and "Volume" in df_candles.columns:
|
|
has_volume = bool(df_candles["Volume"].sum() > 0)
|
|
|
|
indicators = dict(rma=dict(length=[20, 50]))
|
|
defaults = dict(ema=dict(length=10), sma=dict(length=[20, 50]))
|
|
if additional_charts:
|
|
for key, value in additional_charts.items():
|
|
if value:
|
|
indicators[key] = defaults.get(key, {}) or {} # type: ignore
|
|
|
|
if isinstance(df_candles, pd.DataFrame):
|
|
df_candles.name = f"{instrument} {granularity}"
|
|
fig = PlotlyTA.plot(df_candles, indicators, volume=has_volume)
|
|
return fig.show(external=external_axes)
|
|
|
|
logger.error("Data not found")
|
|
console.print("[red]Data not found[/red]\n")
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
@check_api_key(["OANDA_ACCOUNT", "OANDA_TOKEN", "OANDA_ACCOUNT_TYPE"])
|
|
def calendar(instrument: str, days: int = 7):
|
|
"""View calendar of significant events.
|
|
|
|
Parameters
|
|
----------
|
|
instrument : str
|
|
The loaded currency pair
|
|
days : int
|
|
Number of days in advance
|
|
"""
|
|
df_calendar = get_calendar_request(days, instrument)
|
|
if df_calendar is not False and not df_calendar.empty:
|
|
console.print(df_calendar.to_string(index=False))
|
|
elif df_calendar is not False and df_calendar.empty:
|
|
console.print("No calendar records were found.\n")
|
|
else:
|
|
console.print("No data was returned from Oanda.\n")
|
|
|
|
|
|
# Utilities
|
|
|
|
|
|
@log_start_end(log=logger)
|
|
def book_plot(
|
|
df: pd.DataFrame,
|
|
instrument: str,
|
|
book_type: str,
|
|
external_axes: bool = False,
|
|
):
|
|
"""Plot the order book for a given instrument.
|
|
|
|
Parameters
|
|
----------
|
|
df : pd.DataFrame
|
|
Order book data
|
|
instrument : str
|
|
The loaded currency pair
|
|
book_type : str
|
|
Order book type
|
|
external_axes : bool, optional
|
|
Whether to return the figure object or not, by default False
|
|
"""
|
|
df = df.apply(pd.to_numeric)
|
|
df["shortCountPercent"] = df["shortCountPercent"] * -1
|
|
|
|
fig = OpenBBFigure(xaxis_title="Count Percent", yaxis_title="Price")
|
|
fig.set_title(f"{instrument} {book_type}")
|
|
|
|
fig.add_bar(
|
|
x=df["longCountPercent"],
|
|
y=df["price"],
|
|
name="Count Percent",
|
|
marker_color=theme.up_color,
|
|
orientation="h",
|
|
)
|
|
fig.add_bar(
|
|
x=df["shortCountPercent"],
|
|
y=df["price"],
|
|
name="Prices",
|
|
marker_color=theme.down_color,
|
|
orientation="h",
|
|
)
|
|
fig.update_layout(yaxis_nticks=20, bargap=0.01, bargroupgap=0.01)
|
|
|
|
return fig.show(external=external_axes)
|