mirror of
https://github.com/OpenBB-finance/OpenBB.git
synced 2026-10-27 12:40:35 +00:00
Big UX improvement (#1041)
Co-authored-by: jmaslek <jmaslek11@gmail.com> Co-authored-by: Chavithra <chavithra@gmail.com> Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com> Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com> Co-authored-by: Didier Rodrigues Lopes <dro.lopes@campus.fct.unl.pt> * improve user experience phase 1 * add difflib replacement * implement auto-complete on discovery menu * auoto-complete, difflib,jump through menus with cd * Updating tests : terminal * improve navigation on gst * add q command to terminal.py * usage instructions * Updating tests : terminal * fix cd command * vars updated * show menu when user is going through menus * allow user to forget the intiail / on scripting * terminal to accept text file with configuration * refactor -n to -l and clean code * add all commands to the tests_stocks_disc * remove fds from disc since we have search on stocks/ and stocks/sia * hopefully fix tests * skip test * fix completer type and calling menu after command for stocks/disc * show help menu when moving forward/backwards in menus * skip test * skip test - bug fix * add reset, help and quit to choices * improve stocks/disc controller * refactor sia menu * UX improvements for sia menu * improve ux design * refactor ca controller * fix infinite loop * improve ux on dps controller * Add capability of going back from a menu going into a menu * Ux economy (#1056) * First round of UX improvements on economy menu * Do fred menu + sample tests file * revert to > on menu * rename jobs file to .gst * remove cd option * improve return * final refactoring of disc_controller * fix ca,dps,sia,disc,stocks and terminal controllers * work on ba_controller menu * fix reset and try_except * UX on screener controller * fix for full reset name * Ux etfs (#1059) * Refactor ETF menu for new UX * Add couple more examples to test * r->reset * Clean up switch * gov menu refactored * fix reset issue * 80% of refactoring options and pricing/payoff menu is done * improve payoff controller * minor fix * fix: refactor pred and ta stocks controller * fix: refactor fa(and fmp) controllers * fix: refactored dd controller (still missing .gst tests) * fix infinite loop bug * refactor options controller * add tests for payoff and pricing * fix reset for options * UX Upgrade for Crypto controller(s) (#1049) * refactor crypto controller * refactor crypto/defi controller * added simple test and fixes not returning queue in commands * Clean up unused functions / iex load (#1047) * Clean up unused functions and adjust iex load function to not depend on dates * Had an incorrect tab on s_start definition * wrong default arg * regenerate pytest yml * Comment out parametrization * Tests : Stocks > Discovery (#1050) * Updating tests : stocks/discovery/ark * Updating tests : stocks/discovery/fidelity * Updating tests : stocks/discovery/fidelity * Updating tests : stocks/discovery/fidelity * Updating tests : stocks/discovery/seeking_alpha * Updating tests : stocks/discovery/seeking_alpha * Updating tests : stocks/discovery/shortinterest * Updating tests : stocks/discovery/shortinterest * Updating tests : stocks/discovery/seeking_alpha * Updating tests : stocks/discovery/shortinterest * Updating tests : stocks/discovery/disc_controller * Updating tests : stocks/dd * Updating tests : stocks/fa * Updating tests : stocks/discovery/financedatabase * Updating tests : stocks/discovery/finnhub * Updating tests : stocks/discovery/geekofwallstreet * Updating tests : stocks/discovery/marketbeat * Updating tests : stocks/discovery/nasdaq * Updating tests : stocks / discovery / yahoofinance * Updating tests : black * Updating tests : unused import * Updating tests : stocks/discovery/ark * Tests : Stocks > Doc + Helpers + Government (#1051) * Updating tests : stocks/government/controller * Updating tests : stocks/government * Updating tests : conftest * Updating tests : stocks > government * Updating tests : stocks/government * Updating tests : docs * Tests : Stocks > Insider (#1054) * Updating tests : stocks > helper * Updating tests : doc * Updating tests : clean stocks > fa > av * Updating tests : stocks/insider/controller * Updating tests : stocks/insider/business insider * Updating tests : stocks/insider/finviz * Updating tests : stocks/insider/openinsider * Updating tests : stocks/insider/controller * Updating tests : black * Updating tests : stocks/insider/controller * Tests : Stocks > Backtesting + Behavioural Analysis (#1057) * Updating tests : add __init__ files * Updating tests : stocsk/behavioural_analysis * Updating tests : black * Updating tests : stocks/backtesting * Updating tests : stocks/backtesting * fix: converted top to limit arguments * fix: refactor disc controller, updated docs * fix: refactor dd_controller and docs * fix: refactor nft controller and docs * fix: pass test temporarily, onchain controller refactor, docs * fix: overview controller refactor, fix quit command * fix: linting issues * fix: refacot ta controller * fix: refactor pred controller * fix: linting * fix: crypto controller * fix: added tests, updated all controlers * fix: menu print renamed from stocks to crypto controller, added tests * fix: fix faster function execution * Fix annoying pylint C0201 error * fix: renamed commands to long format * DISC Cleanup (#1058) * Get rid/cleanup of marketbeat functions * Get rid of last unused disc function + fix related tests * fix test * Dividend History Chart (#1053) * Dividend History Chart * changed to default number 10 * changed divs flag default to false * updating the expected result for test_fa_controller. * else statement * fix: added load on dd * fix: added autocompletion in all commands with argument choices * fix: linting * Workflows + Stocks > Insider (#1061) * Updating code : stocks/insider remove unused file * Updating workflows : tests - set mypy==0.910 * fix: linting * Fixing Linting (#1062) * Updating code : fixing linting mypy message = Too many arguments for <lambda> * Updating workflows : allow newest mypy version * Updating code : apply black * Updating code : black * fix: updated predict controller help to match stocks * Ux: portfolio (#1063) * First step of portfolio UX * Add all broker controllers to UX stuff * UX Improvement for PO Co-authored-by: Didier Rodrigues Lopes <dro.lopes@campus.fct.unl.pt> * UX Improvement for resources (#1064) Co-authored-by: Didier Rodrigues Lopes <dro.lopes@campus.fct.unl.pt> * UX: stock/bt (#1065) * UX: stocks>qa (#1068) * fix mypy lambda * skip tests for now * remove unused paths.py * rename tests/simulator to tests/e2e * refactor options/screener menu * UX : move `gst` files location * fix stocks_controller * UX upgrade of the Forex menu (#1048) * Add basic forex menu test * Add forex routes to terminal paths * Remove commands from paths because they're not menus * WIP Refactor forex controller * refactor crypto controller * Remove commented code * Improve user output with printed text * Add "help" back to the list of choices * Enable the command queue for the forex controller * refactor crypto/defi controller * Fix candle chart blocking the terminal * Black * Return help and reset to the list of known args * added simple test and fixes not returning queue in commands * Add currency codes loading to the av_model * Return the queue in edge cases (when commands don't process) * Split select into to and from to enable autocompletion * Style text output * Stop printing help after the execution of every command * Update test scenarios * Black * fix: converted top to limit arguments * fix: refactor disc controller, updated docs * Delete oanda the old view file that was moved into the oanda menu * fix: refactor dd_controller and docs * fix: refactor nft controller and docs * fix: pass test temporarily, onchain controller refactor, docs * fix: overview controller refactor, fix quit command * fix: linting issues * fix: refacot ta controller * fix: refactor pred controller * fix: linting * fix: crypto controller * fix: added tests, updated all controlers * fix: menu print renamed from stocks to crypto controller, added tests * fix: fix faster function execution * Implement basic UX improvement of the oanda controller * Update the forex controller * Fix annoying pylint C0201 error * Add latest convention changes to the forex controller * fix: renamed commands to long format * fix: added load on dd * Merge the latest updates of the controller design * fix: added autocompletion in all commands with argument choices * fix: linting * fix: linting * fix: updated predict controller help to match stocks * Update the forex menu e2e test scripts * Add check_non_negative_float helper * Update the controller with the latest changes to the convention * Update the currency pair loading mechanism * Move data models logic to the models file * Update update oanda views to be interact with the models * Upgrade oanda controller * Remove raw stocks ba and eda code from the forex menu * Update "help wanted" banner for ba and eda in forex menu * Remove remainders of stocks ba and eda from forex context * Improve typing, docstrings and error messages * Remove stocks ba and eda code that was merged from other ux branches * Add fixes that prevented incorrect argument handling * Remove -argument from the candles call If there is an expected agr `-g` and you put an incorrect argument `-argument` the difflib will interpret `argument` as `g` Co-authored-by: jose-donato <zmcdonato@gmail.com> Co-authored-by: Didier Rodrigues Lopes <dro.lopes@campus.fct.unl.pt> * fix reset for menus that require ticker * UX improvement on research menu * skip insider controller test * initial refactoring of insider_controller * improve insider controller ux part 2 * finish refactoring ins controller ux menu * fix bug of when commands in queue have args * skip test * comment test stuff * UX: stocks>qa (#1071) * refactor return self.queue * fix return None * minor fix * minor tabulate fix * Get rid of leading '/' in terminal and add some newlines on quits. (#1074) * Get rid of leading '/' in terminal and add some newlines on quits. * print crypto help on menu entry * looks like a format issue here... * newlines on quit * remove unnecessary line break * fix pylint issue * refactor * refactor * allow portfolio optimization to quit context * add exit at end of scripts * improve dd_controller * test ux in stocks/dd menu * Ux upgrade for reports context (#1075) * Refactor reports and upgrade them with the latest controller convention * Add test scenarios for the reports context * test ux stocks/ta * ux branch scripts/ta * improvement on main param * Upgrades for the UX upgrade for the fa and fa/fmp menus (#1076) * Change --num to --limit * Remove inconsistent/unreliable functions from the choices list * Improve docstring compliance with numpy doc format * Fix hardcoded class id in dcf scrapper * Fix annoying pylint error in discordbot that was blocking the hook * Add test scenarios for the fa and fa/fmp menus * Fix forgotten replacement of --num to --limit * Fix tests * Return some excluded commands to the choices list * Fix dd report to reflect the updates * Fix set function not returning the queue * Update gst test files * fix exit on ca from another submenu * fix changes of options/screener * fix dps controller * fix fa when running out of AV calls * Fix TVL plot locking interpreter during defi test * Add exception handling for broken coingecko scrapper * skip fa/av test that fails * fix dps tests Co-authored-by: Chavithra PARANA <chavithra@gmail.com> Co-authored-by: jmaslek <jmaslek11@gmail.com> Co-authored-by: jose-donato <zmcdonato@gmail.com> Co-authored-by: jose-donato <43375532+jose-donato@users.noreply.github.com> Co-authored-by: Theodore Aptekarev <aptekarev@gmail.com> Co-authored-by: Danglewood <85772166+deeleeramone@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
77428f81b0
commit
a85758c9ec
@@ -16,6 +16,8 @@ from gamestonk_terminal.stocks.screener import finviz_view
|
||||
from gamestonk_terminal.stocks.screener import finviz_model
|
||||
from gamestonk_terminal.helper_funcs import plot_autoscale
|
||||
|
||||
# pylint:disable=no-member
|
||||
|
||||
|
||||
async def historical_command(ctx, signal="most_volatile", start=""):
|
||||
"""Displays historical price comparison between similar companies [Yahoo Finance]"""
|
||||
@@ -31,7 +33,7 @@ async def historical_command(ctx, signal="most_volatile", start=""):
|
||||
|
||||
screen = ticker.Ticker()
|
||||
|
||||
if signal in list(finviz_model.d_signals.keys()):
|
||||
if signal in finviz_model.d_signals:
|
||||
screen.set_filter(signal=finviz_model.d_signals[signal])
|
||||
|
||||
else:
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# pylint: disable=W0612
|
||||
from datetime import datetime, timedelta
|
||||
import asyncio
|
||||
import discord
|
||||
|
||||
@@ -6,6 +6,8 @@ import pandas as pd
|
||||
import pmdarima
|
||||
from statsmodels.tsa.arima.model import ARIMA
|
||||
|
||||
ICS = ["aic", "aicc", "bic", "hqic", "oob"]
|
||||
|
||||
|
||||
def get_arima_model(
|
||||
values: Union[pd.Series, pd.DataFrame],
|
||||
|
||||
@@ -7,6 +7,9 @@ import numpy as np
|
||||
import pandas as pd
|
||||
from statsmodels.tsa.holtwinters import ExponentialSmoothing
|
||||
|
||||
TRENDS = ["N", "A", "Ad"]
|
||||
SEASONS = ["N", "A", "M"]
|
||||
|
||||
|
||||
def get_exponential_smoothing_model(
|
||||
data: Union[pd.Series, np.ndarray],
|
||||
|
||||
@@ -6,6 +6,8 @@ import pandas as pd
|
||||
import numpy as np
|
||||
from scipy.stats import norm
|
||||
|
||||
DISTRIBUTIONS = ["normal", "lognormal"]
|
||||
|
||||
|
||||
def get_mc_brownian(
|
||||
data: Union[pd.Series, np.ndarray], n_future: int, n_sims: int, use_log=True
|
||||
|
||||
@@ -157,6 +157,7 @@ def parse_args(prog: str, description: str, other_args: List[str]):
|
||||
"default: usually the same as false, uses env/TensorFlow default",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--loops",
|
||||
action="store",
|
||||
dest="n_loops",
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import os
|
||||
import warnings
|
||||
from typing import Any
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
@@ -405,7 +406,8 @@ def display_normality(df: pd.DataFrame, target: str, export: str = ""):
|
||||
"""
|
||||
data = df[target]
|
||||
normal = qa_model.get_normality(data)
|
||||
stats1 = normal.copy()
|
||||
stats1 = normal.copy().T
|
||||
print(stats1)
|
||||
stats1.loc[:, stats1.iloc[1, :] > 0.05] = stats1.loc[
|
||||
:, stats1.iloc[1, :] > 0.05
|
||||
].apply(lambda x: color_red(x[0]), axis=1)
|
||||
@@ -443,6 +445,8 @@ def display_qqplot(name: str, df: pd.DataFrame, target: str):
|
||||
target : str
|
||||
Column in data to look at
|
||||
"""
|
||||
# Statsmodels has a UserWarning for marker kwarg-- which we dont use
|
||||
warnings.filterwarnings(category=UserWarning, action="ignore")
|
||||
data = df[target]
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
qqplot(data, stats.distributions.norm, fit=True, line="45", ax=ax)
|
||||
|
||||
@@ -4,6 +4,9 @@ ___docformat__ = "numpy"
|
||||
import pandas as pd
|
||||
import pandas_ta as ta
|
||||
|
||||
WINDOW_LENGTHS = [20, 50]
|
||||
WINDOW_LENGTHS2 = [10, 20]
|
||||
|
||||
|
||||
def ema(
|
||||
s_interval: str, df_stock: pd.DataFrame, length: int, offset: int
|
||||
|
||||
@@ -4,6 +4,8 @@ __docformat__ = "numpy"
|
||||
import pandas as pd
|
||||
import pandas_ta as ta
|
||||
|
||||
MAMODES = ["ema", "sma", "wma", "hma", "zlma"]
|
||||
|
||||
|
||||
def bbands(
|
||||
s_interval: str, df_stock: pd.DataFrame, length: int, n_std: float, mamode: str
|
||||
|
||||
@@ -6,9 +6,6 @@ env_files = [f for f in os.listdir() if f.endswith(".env")]
|
||||
if env_files:
|
||||
load_dotenv(env_files[0])
|
||||
|
||||
# Choose one of: stocks, options, crypto, economy, etf, portfolio, forex
|
||||
DEFAULT_CONTEXT = ""
|
||||
|
||||
# Set to True to see full stack traces for debugging/error reporting
|
||||
DEBUG_MODE = False
|
||||
|
||||
|
||||
@@ -1,27 +1,26 @@
|
||||
"""Cryptocurrency Controller"""
|
||||
"""Cryptocurrency Context Controller"""
|
||||
__docformat__ = "numpy"
|
||||
# pylint: disable=R0904, C0302, R1710, W0622
|
||||
# pylint: disable=R0904, C0302, R1710, W0622, C0201
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import matplotlib.pyplot as plt
|
||||
from typing import List, Union
|
||||
import pandas as pd
|
||||
from colorama import Style
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from binance.client import Client
|
||||
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
|
||||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||||
get_flair,
|
||||
parse_known_args_and_warn,
|
||||
check_positive,
|
||||
system_clear,
|
||||
try_except,
|
||||
MENU_GO_BACK,
|
||||
MENU_QUIT,
|
||||
MENU_RESET,
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
|
||||
from gamestonk_terminal.cryptocurrency.due_diligence import (
|
||||
coinpaprika_view,
|
||||
binance_view,
|
||||
@@ -30,31 +29,42 @@ from gamestonk_terminal.cryptocurrency.due_diligence import (
|
||||
binance_model,
|
||||
coinbase_model,
|
||||
)
|
||||
|
||||
from gamestonk_terminal.cryptocurrency.cryptocurrency_helpers import (
|
||||
FIND_KEYS,
|
||||
load,
|
||||
find,
|
||||
load_ta_data,
|
||||
plot_chart,
|
||||
)
|
||||
|
||||
|
||||
import gamestonk_terminal.config_terminal as cfg
|
||||
|
||||
# pylint: disable=import-outside-toplevel
|
||||
|
||||
|
||||
CRYPTO_SOURCES = {
|
||||
"bin": "Binance",
|
||||
"cg": "CoinGecko",
|
||||
"cp": "CoinPaprika",
|
||||
"cb": "Coinbase",
|
||||
}
|
||||
|
||||
|
||||
class CryptoController:
|
||||
CHOICES = [
|
||||
"?",
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_COMMAND = [
|
||||
CHOICES_COMMANDS = [
|
||||
"headlines",
|
||||
"chart",
|
||||
"load",
|
||||
@@ -63,23 +73,16 @@ class CryptoController:
|
||||
|
||||
CHOICES_MENUS = ["ta", "dd", "ov", "disc", "onchain", "defi", "nft", "pred"]
|
||||
|
||||
SOURCES = {
|
||||
"bin": "Binance",
|
||||
"cg": "CoinGecko",
|
||||
"cp": "CoinPaprika",
|
||||
"cb": "Coinbase",
|
||||
}
|
||||
|
||||
DD_VIEWS_MAPPING = {
|
||||
"cg": pycoingecko_view,
|
||||
"cp": coinpaprika_view,
|
||||
"bin": binance_view,
|
||||
}
|
||||
|
||||
CHOICES += CHOICES_COMMAND
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
CHOICES += CHOICES_MENUS
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""CONSTRUCTOR"""
|
||||
|
||||
self.crypto_parser = argparse.ArgumentParser(add_help=False, prog="crypto")
|
||||
@@ -91,41 +94,50 @@ class CryptoController:
|
||||
self.current_currency = ""
|
||||
self.source = ""
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["load"]["--source"] = {c: {} for c in CRYPTO_SOURCES.keys()}
|
||||
choices["find"]["--source"] = {c: {} for c in CRYPTO_SOURCES.keys()}
|
||||
choices["find"]["-k"] = {c: {} for c in FIND_KEYS}
|
||||
choices["headlines"] = {c: {} for c in finbrain_crypto_view.COINS}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = """
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
reset reset terminal and reload configs
|
||||
load load a specific cryptocurrency for analysis
|
||||
find alternate way to search for coins
|
||||
"""
|
||||
help_text += (
|
||||
f"\nCoin: {self.current_coin}" if self.current_coin != "" else "\nCoin: ?"
|
||||
)
|
||||
help_text += (
|
||||
f"\nSource: {self.SOURCES.get(self.source, '?')}\n"
|
||||
f"\nSource: {CRYPTO_SOURCES.get(self.source, '?')}\n"
|
||||
if self.source != ""
|
||||
else "\nSource: ?\n"
|
||||
)
|
||||
help_text += """
|
||||
chart view a candle chart for a specific cryptocurrency
|
||||
headlines crypto sentiment from 15+ major news headlines [Finbrain]
|
||||
"""
|
||||
dim = Style.DIM if not self.current_coin else ""
|
||||
help_text += f"""
|
||||
load load a specific cryptocurrency for analysis
|
||||
chart view a candle chart for a specific cryptocurrency
|
||||
find alternate way to search for coins
|
||||
headlines crypto sentiment from 15+ major news headlines [Finbrain]
|
||||
|
||||
> disc discover trending cryptocurrencies, e.g.: top gainers, losers, top sentiment
|
||||
> ov overview of the cryptocurrencies, e.g.: market cap, DeFi, latest news, top exchanges, stables{dim}
|
||||
> dd due-diligence for loaded coin, e.g.: coin information, social media, market stats
|
||||
> ta technical analysis for loaded coin, e.g.: ema, macd, rsi, adx, bbands, obv
|
||||
> pred prediction techniques e.g.: regression, arima, rnn, lstm, conv1d, monte carlo
|
||||
{Style.RESET_ALL if not self.current_coin else ""}
|
||||
> onchain information on different blockchains, e.g.: eth gas fees, active asset addresses, whale alerts
|
||||
> defi decentralized finance information, e.g.: dpi, llama, tvl, lending, borrow, funding
|
||||
> nft non-fungible tokens, e.g.: today drops
|
||||
"""
|
||||
> disc discover trending cryptocurrencies, e.g.: top gainers, losers, top sentiment
|
||||
> ov overview of the cryptocurrencies, e.g.: market cap, DeFi, latest news, top exchanges, stables
|
||||
> onchain information on different blockchains, e.g.: eth gas fees, whale alerts, DEXes info
|
||||
> defi decentralized finance information, e.g.: dpi, llama, tvl, lending, borrow, funding
|
||||
> nft non-fungible tokens, e.g.: today drops{dim}
|
||||
> dd due-diligence for loaded coin, e.g.: coin information, social media, market stats
|
||||
> ta technical analysis for loaded coin, e.g.: ema, macd, rsi, adx, bbands, obv
|
||||
> pred prediction techniques e.g.: regression, arima, rnn, lstm, conv1d, monte carlo{Style.RESET_ALL if not self.current_coin else ""}
|
||||
""" # noqa
|
||||
print(help_text)
|
||||
|
||||
def switch(self, an_input: str):
|
||||
@@ -133,28 +145,41 @@ What do you want to do?
|
||||
|
||||
Returns
|
||||
-------
|
||||
MENU_GO_BACK, MENU_QUIT, MENU_RESET
|
||||
MENU_GO_BACK - Show main context menu again
|
||||
MENU_QUIT - Quit terminal
|
||||
MENU_RESET - Reset terminal and go back to same previous menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.crypto_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -162,21 +187,44 @@ What do you want to do?
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return MENU_GO_BACK
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - exit the program"""
|
||||
return MENU_QUIT
|
||||
"""Process quit menu command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process Reset command - exit the program"""
|
||||
return MENU_RESET
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "crypto")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "reset", "crypto"]
|
||||
|
||||
def call_load(self, other_args):
|
||||
"""Process load command"""
|
||||
@@ -216,26 +264,26 @@ What do you want to do?
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
self.current_coin, self.source = self.current_coin, None
|
||||
return
|
||||
if ns_parser:
|
||||
source = ns_parser.source
|
||||
|
||||
source = ns_parser.source
|
||||
for arg in ["--source", source]:
|
||||
if arg in other_args:
|
||||
other_args.remove(arg)
|
||||
|
||||
for arg in ["--source", source]:
|
||||
if arg in other_args:
|
||||
other_args.remove(arg)
|
||||
|
||||
self.current_coin, self.source, self.symbol = load(
|
||||
coin=ns_parser.coin, source=ns_parser.source
|
||||
)
|
||||
self.current_coin, self.source, self.symbol = load(
|
||||
coin=ns_parser.coin, source=ns_parser.source
|
||||
)
|
||||
return self.queue
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
self.current_coin, self.source = self.current_coin, None
|
||||
return self.queue
|
||||
|
||||
except TypeError:
|
||||
print("Couldn't load data\n")
|
||||
return self.queue
|
||||
|
||||
def call_chart(self, other_args):
|
||||
"""Process chart command"""
|
||||
@@ -381,37 +429,41 @@ What do you want to do?
|
||||
)
|
||||
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if self.source in ["bin", "cb"]:
|
||||
limit = ns_parser.limit
|
||||
interval = ns_parser.interval
|
||||
days = 0
|
||||
else:
|
||||
limit = 0
|
||||
interval = "1day"
|
||||
days = ns_parser.days
|
||||
|
||||
plot_chart(
|
||||
coin=self.current_coin,
|
||||
limit=limit,
|
||||
interval=interval,
|
||||
days=days,
|
||||
currency=ns_parser.vs,
|
||||
source=self.source,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
|
||||
if ns_parser:
|
||||
if self.source in ["bin", "cb"]:
|
||||
limit = ns_parser.limit
|
||||
interval = ns_parser.interval
|
||||
days = 0
|
||||
else:
|
||||
limit = 0
|
||||
interval = "1day"
|
||||
days = ns_parser.days
|
||||
|
||||
plot_chart(
|
||||
coin=self.current_coin,
|
||||
limit=limit,
|
||||
interval=interval,
|
||||
days=days,
|
||||
currency=ns_parser.vs,
|
||||
source=self.source,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return self.queue
|
||||
|
||||
else:
|
||||
print(
|
||||
"No coin selected. Use 'load' to load the coin you want to look at.\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ta(self, other_args):
|
||||
"""Process ta command"""
|
||||
from gamestonk_terminal.cryptocurrency.technical_analysis import ta_controller
|
||||
@@ -530,7 +582,7 @@ What do you want to do?
|
||||
print(
|
||||
f"Couldn't find any quoted coins for provided symbol {self.current_coin}"
|
||||
)
|
||||
return
|
||||
return self.queue
|
||||
|
||||
parser.add_argument(
|
||||
"--vs",
|
||||
@@ -578,7 +630,7 @@ What do you want to do?
|
||||
print(
|
||||
f"Couldn't find any quoted coins for provided symbol {self.current_coin}"
|
||||
)
|
||||
return
|
||||
return self.queue
|
||||
|
||||
parser.add_argument(
|
||||
"--vs",
|
||||
@@ -611,84 +663,67 @@ What do you want to do?
|
||||
try:
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if self.source in ["bin", "cb"]:
|
||||
limit = ns_parser.limit
|
||||
interval = ns_parser.interval
|
||||
days = 0
|
||||
else:
|
||||
limit = 0
|
||||
interval = "1day"
|
||||
days = ns_parser.days
|
||||
|
||||
if self.source in ["bin", "cb"]:
|
||||
limit = ns_parser.limit
|
||||
interval = ns_parser.interval
|
||||
days = 0
|
||||
else:
|
||||
limit = 0
|
||||
interval = "1day"
|
||||
days = ns_parser.days
|
||||
|
||||
self.current_df, self.current_currency = load_ta_data(
|
||||
coin=self.current_coin,
|
||||
source=self.source,
|
||||
currency=ns_parser.vs,
|
||||
days=days,
|
||||
limit=limit,
|
||||
interval=interval,
|
||||
)
|
||||
self.current_df, self.current_currency = load_ta_data(
|
||||
coin=self.current_coin,
|
||||
source=self.source,
|
||||
currency=ns_parser.vs,
|
||||
days=days,
|
||||
limit=limit,
|
||||
interval=interval,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return self.queue
|
||||
|
||||
if self.current_currency != "" and not self.current_df.empty:
|
||||
try:
|
||||
quit = ta_controller.menu(
|
||||
return ta_controller.menu(
|
||||
stock=self.current_df,
|
||||
ticker=self.current_coin,
|
||||
start=self.current_df.index[0],
|
||||
interval="",
|
||||
queue=self.queue,
|
||||
)
|
||||
print("")
|
||||
if quit is not None:
|
||||
if quit is True:
|
||||
return quit
|
||||
self.print_help()
|
||||
|
||||
except (ValueError, KeyError) as e:
|
||||
print(e)
|
||||
return self.queue
|
||||
else:
|
||||
return
|
||||
return self.queue
|
||||
|
||||
else:
|
||||
print(
|
||||
"No coin selected. Use 'load' to load the coin you want to look at.\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
def call_disc(self, _):
|
||||
"""Process disc command"""
|
||||
from gamestonk_terminal.cryptocurrency.discovery import discovery_controller
|
||||
|
||||
ret = discovery_controller.menu()
|
||||
if ret is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
return discovery_controller.menu(queue=self.queue)
|
||||
|
||||
def call_ov(self, _):
|
||||
"""Process ov command"""
|
||||
from gamestonk_terminal.cryptocurrency.overview import overview_controller
|
||||
|
||||
ret = overview_controller.menu()
|
||||
if ret is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
return overview_controller.menu(queue=self.queue)
|
||||
|
||||
def call_defi(self, _):
|
||||
"""Process defi command"""
|
||||
from gamestonk_terminal.cryptocurrency.defi import defi_controller
|
||||
|
||||
ret = defi_controller.menu()
|
||||
|
||||
if ret is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
return defi_controller.menu(queue=self.queue)
|
||||
|
||||
@try_except
|
||||
def call_headlines(self, other_args):
|
||||
@@ -710,38 +745,29 @@ What do you want to do?
|
||||
choices=finbrain_crypto_view.COINS,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-c")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
finbrain_crypto_view.display_crypto_sentiment_analysis(
|
||||
coin=ns_parser.coin, export=ns_parser.export
|
||||
)
|
||||
if ns_parser:
|
||||
finbrain_crypto_view.display_crypto_sentiment_analysis(
|
||||
coin=ns_parser.coin, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
def call_dd(self, _):
|
||||
"""Process dd command"""
|
||||
if self.current_coin:
|
||||
from gamestonk_terminal.cryptocurrency.due_diligence import dd_controller
|
||||
|
||||
dd = dd_controller.menu(self.current_coin, self.source, self.symbol)
|
||||
if dd is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
print(
|
||||
"No coin selected. Use 'load' to load the coin you want to look at.\n"
|
||||
return dd_controller.menu(
|
||||
self.current_coin, self.source, self.symbol, queue=self.queue
|
||||
)
|
||||
print("No coin selected. Use 'load' to load the coin you want to look at.\n")
|
||||
return self.queue
|
||||
|
||||
def call_pred(self, _):
|
||||
"""Process pred command"""
|
||||
@@ -749,11 +775,12 @@ What do you want to do?
|
||||
print(
|
||||
"No coin loaded. Please use `load <coin>` to access prediction menu\n."
|
||||
)
|
||||
return
|
||||
return self.queue
|
||||
|
||||
if self.source != "cg":
|
||||
print("Currently only supports CoinGecko source.\n")
|
||||
return
|
||||
return self.queue
|
||||
|
||||
if self.current_coin:
|
||||
from gamestonk_terminal.cryptocurrency.prediction_techniques import (
|
||||
pred_controller,
|
||||
@@ -762,40 +789,25 @@ What do you want to do?
|
||||
cryptocurrency_helpers as c_help,
|
||||
)
|
||||
|
||||
pred = pred_controller.menu(
|
||||
return pred_controller.menu(
|
||||
self.current_coin,
|
||||
c_help.load_cg_coin_data(self.current_coin, "USD", 365, "1D"),
|
||||
self.queue,
|
||||
)
|
||||
if pred is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
else:
|
||||
print(
|
||||
"No coin selected. Use 'load' to load the coin you want to look at.\n"
|
||||
)
|
||||
print("No coin selected. Use 'load' to load the coin you want to look at.\n")
|
||||
return self.queue
|
||||
|
||||
def call_onchain(self, _):
|
||||
"""Process onchain command"""
|
||||
from gamestonk_terminal.cryptocurrency.onchain import onchain_controller
|
||||
|
||||
ret = onchain_controller.menu()
|
||||
|
||||
if ret is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
return onchain_controller.menu(queue=self.queue)
|
||||
|
||||
def call_nft(self, _):
|
||||
"""Process nft command"""
|
||||
from gamestonk_terminal.cryptocurrency.nft import nft_controller
|
||||
|
||||
ret = nft_controller.menu()
|
||||
|
||||
if ret is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
return nft_controller.menu(queue=self.queue)
|
||||
|
||||
@try_except
|
||||
def call_find(self, other_args):
|
||||
@@ -812,7 +824,7 @@ What do you want to do?
|
||||
It will search for coin that has similar name to polka and display top 25 matches.
|
||||
-c, --coin stands for coin - you provide here your search query
|
||||
-k, --key it's a searching key. You can search by symbol, id or name of coin
|
||||
-t, --top it displays top N number of records.""",
|
||||
-l, --limit it displays top N number of records.""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -830,83 +842,119 @@ What do you want to do?
|
||||
dest="key",
|
||||
help="Specify by which column you would like to search: symbol, name, id",
|
||||
type=str,
|
||||
choices=["id", "symbol", "name"],
|
||||
choices=FIND_KEYS,
|
||||
default="symbol",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
"-l",
|
||||
"--limit",
|
||||
default=10,
|
||||
dest="top",
|
||||
help="Limit of records",
|
||||
dest="limit",
|
||||
help="Number of records to display",
|
||||
type=check_positive,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--source",
|
||||
dest="source",
|
||||
choices=["cp", "cg", "bin", "cb"],
|
||||
choices=CRYPTO_SOURCES.keys(),
|
||||
default="cg",
|
||||
help="Source of data.",
|
||||
type=str,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
if other_args:
|
||||
if not other_args[0][0] == "-":
|
||||
other_args.insert(0, "-c")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
find(
|
||||
coin=ns_parser.coin,
|
||||
source=ns_parser.source,
|
||||
key=ns_parser.key,
|
||||
top=ns_parser.top,
|
||||
export=ns_parser.export,
|
||||
if other_args and not other_args[0][0] == "-":
|
||||
other_args.insert(0, "-c")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
find(
|
||||
coin=ns_parser.coin,
|
||||
source=ns_parser.source,
|
||||
key=ns_parser.key,
|
||||
top=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
crypto_controller = CryptoController()
|
||||
crypto_controller.call_help(None)
|
||||
plt.close("all")
|
||||
def menu(queue: List[str] = None):
|
||||
crypto_controller = CryptoController(queue)
|
||||
first = True
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if crypto_controller.queue and len(crypto_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if crypto_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(crypto_controller.queue) > 1:
|
||||
return crypto_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = crypto_controller.queue[0]
|
||||
crypto_controller.queue = crypto_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if (
|
||||
an_input
|
||||
and an_input.split(" ")[0] in crypto_controller.CHOICES_COMMANDS
|
||||
):
|
||||
print(f"{get_flair()} /crypto/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in crypto_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (crypto)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (crypto)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if first:
|
||||
crypto_controller.print_help()
|
||||
first = False
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and crypto_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /crypto/ $ ",
|
||||
completer=crypto_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /crypto/ $ ")
|
||||
|
||||
try:
|
||||
process_input = crypto_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
|
||||
# Process the input command
|
||||
crypto_controller.queue = crypto_controller.switch(an_input)
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, crypto_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
crypto_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
else:
|
||||
candidate_input = similar_cmd[0]
|
||||
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
crypto_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
crypto_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
an_input = ""
|
||||
crypto_controller.queue = []
|
||||
|
||||
@@ -39,6 +39,8 @@ from gamestonk_terminal import feature_flags as gtff
|
||||
# TODO: Improve implementation of coin loading
|
||||
# Currently adding this function to helpers for implementing prediction menu
|
||||
|
||||
INTERVALS = ["1H", "3H", "6H", "1D"]
|
||||
|
||||
|
||||
def load_cg_coin_data(
|
||||
coin: str, currency: str = "USD", days: int = 365, sampling: str = "1D"
|
||||
@@ -204,7 +206,7 @@ def load(
|
||||
Tuple[Union[str, pycoingecko_model.Coin], str, str]
|
||||
- str or Coin object for provided coin
|
||||
- str with source of the loaded data. CoinGecko, CoinPaprika, or Binance
|
||||
- str with
|
||||
- str with symbol
|
||||
"""
|
||||
|
||||
current_coin = "" # type: Optional[Any]
|
||||
@@ -242,7 +244,11 @@ def load(
|
||||
return current_coin, None, None
|
||||
|
||||
|
||||
FIND_KEYS = ["id", "symbol", "name"]
|
||||
|
||||
# TODO: Find better algorithm then difflib.get_close_matches to find most similar coins
|
||||
|
||||
|
||||
def find(source: str, coin: str, key: str, top: int, export: str) -> None:
|
||||
"""Find similar coin by coin name,symbol or id.
|
||||
|
||||
|
||||
@@ -2,9 +2,12 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
import difflib
|
||||
|
||||
from typing import List, Union
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from gamestonk_terminal.cryptocurrency.defi import graph_model
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
@@ -20,6 +23,7 @@ from gamestonk_terminal.helper_funcs import (
|
||||
from gamestonk_terminal.cryptocurrency.defi import (
|
||||
defirate_view,
|
||||
defipulse_view,
|
||||
llama_model,
|
||||
llama_view,
|
||||
substack_view,
|
||||
graph_view,
|
||||
@@ -31,10 +35,16 @@ class DefiController:
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS = [
|
||||
@@ -54,13 +64,27 @@ class DefiController:
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Constructor"""
|
||||
self.defi_parser = argparse.ArgumentParser(add_help=False, prog="defi")
|
||||
self.defi_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["llama"]["-s"] = {c: {} for c in llama_model.LLAMA_FILTERS}
|
||||
choices["tokens"]["-s"] = {c: {} for c in graph_model.TOKENS_FILTERS}
|
||||
choices["pairs"]["-s"] = {c: {} for c in graph_model.PAIRS_FILTERS}
|
||||
choices["pools"]["-s"] = {c: {} for c in graph_model.POOLS_FILTERS}
|
||||
choices["swaps"]["-s"] = {c: {} for c in graph_model.SWAPS_FILTERS}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
@@ -72,28 +96,40 @@ class DefiController:
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.defi_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -101,17 +137,50 @@ class DefiController:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, *_):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "defi")
|
||||
self.queue.insert(0, "crypto")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "reset", "crypto", "defi"]
|
||||
|
||||
@try_except
|
||||
def call_dpi(self, other_args: List[str]):
|
||||
@@ -127,11 +196,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="top N number records",
|
||||
help="Number of records to display",
|
||||
default=15,
|
||||
)
|
||||
|
||||
@@ -157,15 +226,14 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
defipulse_view.display_defipulse(
|
||||
top=ns_parser.top,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
defipulse_view.display_defipulse(
|
||||
top=ns_parser.limit,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_llama(self, other_args: List[str]):
|
||||
@@ -181,11 +249,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="top N number records",
|
||||
help="Number of records to display",
|
||||
default=10,
|
||||
)
|
||||
|
||||
@@ -196,16 +264,7 @@ class DefiController:
|
||||
type=str,
|
||||
help="Sort by given column. Default: tvl",
|
||||
default="tvl",
|
||||
choices=[
|
||||
"tvl",
|
||||
"symbol",
|
||||
"category",
|
||||
"chains",
|
||||
"change_1h",
|
||||
"change_1d",
|
||||
"change_7d",
|
||||
"tvl",
|
||||
],
|
||||
choices=llama_model.LLAMA_FILTERS,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -228,16 +287,15 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
llama_view.display_defi_protocols(
|
||||
top=ns_parser.top,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
description=ns_parser.description,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
llama_view.display_defi_protocols(
|
||||
top=ns_parser.limit,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
description=ns_parser.description,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_tvl(self, other_args: List[str]):
|
||||
@@ -253,11 +311,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="top N number records",
|
||||
help="Number of records to display",
|
||||
default=10,
|
||||
)
|
||||
|
||||
@@ -265,10 +323,9 @@ class DefiController:
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
llama_view.display_defi_tvl(top=ns_parser.top, export=ns_parser.export)
|
||||
if ns_parser:
|
||||
llama_view.display_defi_tvl(top=ns_parser.limit, export=ns_parser.export)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_funding(self, other_args: List[str]):
|
||||
@@ -284,11 +341,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="top N number records",
|
||||
help="Number of records to display",
|
||||
default=10,
|
||||
)
|
||||
|
||||
@@ -304,12 +361,11 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
defirate_view.display_funding_rates(
|
||||
top=ns_parser.top, current=ns_parser.current, export=ns_parser.export
|
||||
)
|
||||
if ns_parser:
|
||||
defirate_view.display_funding_rates(
|
||||
top=ns_parser.limit, current=ns_parser.current, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_borrow(self, other_args: List[str]):
|
||||
@@ -325,11 +381,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="top N number records",
|
||||
help="Number of records to display",
|
||||
default=10,
|
||||
)
|
||||
|
||||
@@ -345,12 +401,11 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
defirate_view.display_borrow_rates(
|
||||
top=ns_parser.top, current=ns_parser.current, export=ns_parser.export
|
||||
)
|
||||
if ns_parser:
|
||||
defirate_view.display_borrow_rates(
|
||||
top=ns_parser.limit, current=ns_parser.current, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_lending(self, other_args: List[str]):
|
||||
@@ -366,11 +421,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="top N number records",
|
||||
help="Number of records to display",
|
||||
default=15,
|
||||
)
|
||||
|
||||
@@ -386,12 +441,11 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
defirate_view.display_lending_rates(
|
||||
top=ns_parser.top, current=ns_parser.current, export=ns_parser.export
|
||||
)
|
||||
if ns_parser:
|
||||
defirate_view.display_lending_rates(
|
||||
top=ns_parser.limit, current=ns_parser.current, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_newsletter(self, other_args: List[str]):
|
||||
@@ -407,11 +461,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="top N number records",
|
||||
help="Number of records to display",
|
||||
default=10,
|
||||
)
|
||||
|
||||
@@ -419,10 +473,11 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
substack_view.display_newsletters(top=ns_parser.top, export=ns_parser.export)
|
||||
if ns_parser:
|
||||
substack_view.display_newsletters(
|
||||
top=ns_parser.limit, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_tokens(self, other_args: List[str]):
|
||||
@@ -460,14 +515,7 @@ class DefiController:
|
||||
type=str,
|
||||
help="Sort by given column. Default: index",
|
||||
default="index",
|
||||
choices=[
|
||||
"index",
|
||||
"symbol",
|
||||
"name",
|
||||
"tradeVolumeUSD",
|
||||
"totalLiquidity",
|
||||
"txCount",
|
||||
],
|
||||
choices=graph_model.TOKENS_FILTERS,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -482,16 +530,15 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
graph_view.display_uni_tokens(
|
||||
skip=ns_parser.skip,
|
||||
limit=ns_parser.limit,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
graph_view.display_uni_tokens(
|
||||
skip=ns_parser.skip,
|
||||
limit=ns_parser.limit,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_stats(self, other_args: List[str]):
|
||||
@@ -510,10 +557,9 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
graph_view.display_uni_stats(export=ns_parser.export)
|
||||
if ns_parser:
|
||||
graph_view.display_uni_stats(export=ns_parser.export)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_pairs(self, other_args: List[str]):
|
||||
@@ -529,11 +575,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="Number of records",
|
||||
help="Number of records to display",
|
||||
default=10,
|
||||
)
|
||||
|
||||
@@ -570,15 +616,7 @@ class DefiController:
|
||||
type=str,
|
||||
help="Sort by given column. Default: created",
|
||||
default="created",
|
||||
choices=[
|
||||
"created",
|
||||
"pair",
|
||||
"token0",
|
||||
"token1",
|
||||
"volumeUSD",
|
||||
"txCount",
|
||||
"totalSupply",
|
||||
],
|
||||
choices=graph_model.PAIRS_FILTERS,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -593,18 +631,17 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
graph_view.display_recently_added(
|
||||
top=ns_parser.top,
|
||||
days=ns_parser.days,
|
||||
min_volume=ns_parser.vol,
|
||||
min_tx=ns_parser.tx,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
graph_view.display_recently_added(
|
||||
top=ns_parser.limit,
|
||||
days=ns_parser.days,
|
||||
min_volume=ns_parser.vol,
|
||||
min_tx=ns_parser.tx,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_pools(self, other_args: List[str]):
|
||||
@@ -620,11 +657,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="Number of records",
|
||||
help="Number of records to display",
|
||||
default=10,
|
||||
)
|
||||
|
||||
@@ -635,15 +672,7 @@ class DefiController:
|
||||
type=str,
|
||||
help="Sort by given column. Default: volumeUSD",
|
||||
default="volumeUSD",
|
||||
choices=[
|
||||
"volumeUSD",
|
||||
"token0.name",
|
||||
"token0.symbol",
|
||||
"token1.name",
|
||||
"token1.symbol",
|
||||
"volumeUSD",
|
||||
"txCount",
|
||||
],
|
||||
choices=graph_model.POOLS_FILTERS,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -658,15 +687,14 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
graph_view.display_uni_pools(
|
||||
top=ns_parser.top,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
graph_view.display_uni_pools(
|
||||
top=ns_parser.limit,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_swaps(self, other_args: List[str]):
|
||||
@@ -682,11 +710,11 @@ class DefiController:
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
dest="top",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
help="Number of records",
|
||||
help="Number of records to display",
|
||||
default=10,
|
||||
)
|
||||
|
||||
@@ -697,7 +725,7 @@ class DefiController:
|
||||
type=str,
|
||||
help="Sort by given column. Default: timestamp",
|
||||
default="timestamp",
|
||||
choices=["timestamp", "token0", "token1", "amountUSD"],
|
||||
choices=graph_model.SWAPS_FILTERS,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
@@ -712,24 +740,19 @@ class DefiController:
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
graph_view.display_last_uni_swaps(
|
||||
top=ns_parser.top,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
graph_view.display_last_uni_swaps(
|
||||
top=ns_parser.limit,
|
||||
sortby=ns_parser.sortby,
|
||||
descend=ns_parser.descend,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = """
|
||||
Decentralized Finance:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
Decentralized Finance Menu:
|
||||
|
||||
Overview:
|
||||
llama DeFi protocols listed on DeFi Llama
|
||||
@@ -739,42 +762,87 @@ Overview:
|
||||
funding Funding reates - current or last 30 days average
|
||||
borrow DeFi borrow rates - current or last 30 days average
|
||||
lending DeFi ending rates - current or last 30 days average
|
||||
|
||||
Uniswap:
|
||||
tokens Tokens trade-able on Uniswap
|
||||
stats Base statistics about Uniswap
|
||||
pairs Recently added pairs on Uniswap
|
||||
pools Pools by volume on Uniswap
|
||||
swaps Recent swaps done on Uniswap"""
|
||||
print(help_text, "\n")
|
||||
swaps Recent swaps done on Uniswap
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
|
||||
def menu():
|
||||
def menu(queue: List[str] = None):
|
||||
"""Defi Menu"""
|
||||
defi_controller = DefiController()
|
||||
defi_controller.call_help(None)
|
||||
defi_controller = DefiController(queue=queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if defi_controller.queue and len(defi_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if defi_controller.queue[0] in ("q", "..", "quit"):
|
||||
if len(defi_controller.queue) > 1:
|
||||
return defi_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = defi_controller.queue[0]
|
||||
defi_controller.queue = defi_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in defi_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /crypto/defi/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in defi_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (crypto)>(defi)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (crypto)>(defi)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
defi_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and defi_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /crypto/defi/ $ ",
|
||||
completer=defi_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /crypto/defi/ $ ")
|
||||
|
||||
try:
|
||||
process_input = defi_controller.switch(an_input)
|
||||
# Process the input command
|
||||
defi_controller.queue = defi_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
defi_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
else:
|
||||
candidate_input = similar_cmd[0]
|
||||
|
||||
if process_input is False:
|
||||
return False
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
defi_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
|
||||
if process_input is True:
|
||||
return True
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
defi_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
an_input = ""
|
||||
defi_controller.queue = []
|
||||
|
||||
@@ -11,6 +11,39 @@ from gamestonk_terminal.cryptocurrency.dataframe_helpers import (
|
||||
|
||||
UNI_URL = "https://api.thegraph.com/subgraphs/name/uniswap/uniswap-v2"
|
||||
|
||||
SWAPS_FILTERS = ["timestamp", "token0", "token1", "amountUSD"]
|
||||
|
||||
POOLS_FILTERS = [
|
||||
"volumeUSD",
|
||||
"token0.name",
|
||||
"token0.symbol",
|
||||
"token1.name",
|
||||
"token1.symbol",
|
||||
"volumeUSD",
|
||||
"txCount",
|
||||
]
|
||||
|
||||
TOKENS_FILTERS = [
|
||||
"index",
|
||||
"symbol",
|
||||
"name",
|
||||
"tradeVolumeUSD",
|
||||
"totalLiquidity",
|
||||
"txCount",
|
||||
]
|
||||
|
||||
PAIRS_FILTERS = [
|
||||
"created",
|
||||
"pair",
|
||||
"token0",
|
||||
"token1",
|
||||
"volumeUSD",
|
||||
"txCount",
|
||||
"totalSupply",
|
||||
]
|
||||
|
||||
# TODO: convert USD values to int. otherwise sort by these columns won't work
|
||||
|
||||
|
||||
def query_graph(url: str, query: str) -> dict:
|
||||
"""Helper methods for querying graphql api. [Source: https://thegraph.com/en/]
|
||||
|
||||
@@ -7,6 +7,17 @@ import requests
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
|
||||
LLAMA_FILTERS = [
|
||||
"tvl",
|
||||
"symbol",
|
||||
"category",
|
||||
"chains",
|
||||
"change_1h",
|
||||
"change_1d",
|
||||
"change_7d",
|
||||
"name",
|
||||
]
|
||||
|
||||
|
||||
def get_defi_protocols() -> pd.DataFrame:
|
||||
"""Returns information about listed DeFi protocols, their current TVL and changes to it in the last hour/day/week.
|
||||
@@ -57,7 +68,7 @@ def get_defi_tvl() -> pd.DataFrame:
|
||||
pd.DataFrame
|
||||
Historical values of total sum of Total Value Locked from all listed protocols.
|
||||
"""
|
||||
response = requests.get("https://api.llama.fi/charts")
|
||||
response = requests.get("https://api.llama.fi/charts", timeout=5)
|
||||
if response.status_code != 200:
|
||||
raise Exception(f"Status code: {response.status_code}. Reason: {response.text}")
|
||||
try:
|
||||
|
||||
@@ -94,6 +94,9 @@ def display_defi_tvl(top: int, export: str = "") -> None:
|
||||
df = df.tail(top)
|
||||
df["totalLiquidityUSD"] = df["totalLiquidityUSD"] / 1_000_000_000
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
|
||||
plt.plot(df["date"], df["totalLiquidityUSD"], "-ok", ms=2)
|
||||
|
||||
@@ -5,47 +5,47 @@ others. The usage of the following commands along with an example will be exploi
|
||||
|
||||
[CoinGecko](#CoinGecko)
|
||||
|
||||
* [cg_coins](#cg_coins)
|
||||
* show coins available on CoinGecko
|
||||
* [cg_trending](#cg_trending)
|
||||
* show trending coins on CoinGecko
|
||||
* [cg_most_voted](#cg_most_voted)
|
||||
* show most voted coins on CoinGecko
|
||||
* [cg_top_volume](#cg_top_volume)
|
||||
* show coins with highest volume on CoinGecko
|
||||
* [cg_recently](#cg_recently)
|
||||
* show recently added on CoinGecko
|
||||
* [cg_sentiment](#cg_sentiment)
|
||||
* show coins with most positive sentiment
|
||||
* [cg_gainers](#cg_gainers)
|
||||
* show top gainers - coins which price gained the most in given period
|
||||
* [cg_losers](#cg_losers)
|
||||
* show top losers - coins which price dropped the most in given period
|
||||
* [cg_yfarms](#cg_yfarms)
|
||||
* show top Yield Farms
|
||||
* [cg_top_defi](#cg_top_defi)
|
||||
* show top DeFi Protocols
|
||||
* [cg_top_dex](#cg_top_dex)
|
||||
* show top Decentralized Exchanges
|
||||
* [cg_top_nft](#cg_top_nft)
|
||||
* show top Non Fungible Tokens
|
||||
- [cg_coins](#cg_coins)
|
||||
- show coins available on CoinGecko
|
||||
- [cg_trending](#cg_trending)
|
||||
- show trending coins on CoinGecko
|
||||
- [cg_most_voted](#cg_most_voted)
|
||||
- show most voted coins on CoinGecko
|
||||
- [cg_top_volume](#cg_top_volume)
|
||||
- show coins with highest volume on CoinGecko
|
||||
- [cg_recently](#cg_recently)
|
||||
- show recently added on CoinGecko
|
||||
- [cg_sentiment](#cg_sentiment)
|
||||
- show coins with most positive sentiment
|
||||
- [cg_gainers](#cg_gainers)
|
||||
- show top gainers - coins which price gained the most in given period
|
||||
- [cg_losers](#cg_losers)
|
||||
- show top losers - coins which price dropped the most in given period
|
||||
- [cg_yfarms](#cg_yfarms)
|
||||
- show top Yield Farms
|
||||
- [cg_top_defi](#cg_top_defi)
|
||||
- show top DeFi Protocols
|
||||
- [cg_top_dex](#cg_top_dex)
|
||||
- show top Decentralized Exchanges
|
||||
- [cg_top_nft](#cg_top_nft)
|
||||
- show top Non Fungible Tokens
|
||||
|
||||
[CoinPaprika](#CoinPaprika)
|
||||
|
||||
* [cp_coins](#cp_coins)
|
||||
* show coins available on CoinPaprika
|
||||
* [cp_search](#cp_search)
|
||||
* show search on CoinPaprika
|
||||
- [cp_coins](#cp_coins)
|
||||
- show coins available on CoinPaprika
|
||||
- [cp_search](#cp_search)
|
||||
- show search on CoinPaprika
|
||||
|
||||
[CoinMarketCap](#CoinMarketCap)
|
||||
|
||||
* [cmc_top](#cmc_top)
|
||||
* show top coin from coinmarketcap.com
|
||||
- [cmc_top](#cmc_top)
|
||||
- show top coin from coinmarketcap.com
|
||||
|
||||
[Binance](#Binance)
|
||||
|
||||
* [bin_coins](#bin_coins)
|
||||
* show coins available on Binance
|
||||
- [bin_coins](#bin_coins)
|
||||
- show coins available on Binance
|
||||
|
||||
# CoinGecko <a name="CoinGecko"></a>
|
||||
|
||||
@@ -59,13 +59,13 @@ Display all available coins in coingecko. You can search with pagination mechani
|
||||
or you can just search by letter like `cg_coins --letter d --key name --top 10` it will display top 10 matches for coins
|
||||
which name starts with letter `d` and search will be done in column `name`
|
||||
|
||||
* -s/--skip - number of records to skip. Default is 0. There are thousands of coins, so there is mechanism to paginate
|
||||
- -s/--skip - number of records to skip. Default is 0. There are thousands of coins, so there is mechanism to paginate
|
||||
through them. e.q if you want to see only records from 500-750 you should use `cg_coins --skip 500 --top 250` or if
|
||||
you want to see 1200-1880 you should use `coins --skip 1200 --top 680`.
|
||||
* -t/--top - number of news to display. One page of news contains 25 news, so to get 250 news script needs to scrape 10
|
||||
- -t/--top - number of news to display. One page of news contains 25 news, so to get 250 news script needs to scrape 10
|
||||
pages (it can take some time). Default 100. E.g `news --top 150`
|
||||
* -l/--letter - first letters of coin by which you want to search
|
||||
* -k/--key - search key. With this parameter you can specify in which column you would like to search. Choose on from
|
||||
- -l/--letter - first letters of coin by which you want to search
|
||||
- -k/--key - search key. With this parameter you can specify in which column you would like to search. Choose on from
|
||||
`name, id, symbol`
|
||||
|
||||

|
||||
@@ -73,75 +73,75 @@ which name starts with letter `d` and search will be done in column `name`
|
||||
## cg_trending <a name="cg_trending"></a>
|
||||
|
||||
```text
|
||||
usage: cg_trending [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_trending [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows most trending coins on CoinGecko.
|
||||
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_trending --top 10`
|
||||
* -s/--sortby - sort by given column. You can chose on from `rank, name, price_usd, price_btc`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_trending --top 10 --links`
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_trending --top 10`
|
||||
- -s/--sortby - sort by given column. You can chose on from `rank, name, price_usd, price_btc`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_trending --top 10 --urls`
|
||||
|
||||

|
||||
|
||||
## cg_most_voted <a name="cg_most_voted"></a>
|
||||
|
||||
```text
|
||||
usage: cg_most_voted [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_most_voted [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows most voted coins on CoinGecko.
|
||||
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_most_voted --top 10`
|
||||
* -s/--sortby - sort by given column. You can chose on from `rank, name, price_usd, price_btc`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_most_voted --top 10 --links`
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_most_voted --top 10`
|
||||
- -s/--sortby - sort by given column. You can chose on from `rank, name, price_usd, price_btc`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_most_voted --top 10 --urls`
|
||||
|
||||

|
||||
|
||||
## cg_most_visited <a name="cg_most_visited"></a>
|
||||
|
||||
```text
|
||||
usage: cg_most_visited [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_most_visited [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows most visited coins on CoinGecko.
|
||||
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_most_visited --top 10`
|
||||
* -s/--sortby - sort by given column. You can chose on from `rank, name, price_usd, price_btc`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_most_visited --top 10 --links`
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_most_visited --top 10`
|
||||
- -s/--sortby - sort by given column. You can chose on from `rank, name, price_usd, price_btc`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_most_visited --top 10 --urls`
|
||||
|
||||

|
||||
|
||||
## cg_sentiment <a name="cg_sentiment"></a>
|
||||
|
||||
```text
|
||||
usage: cg_sentiment [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_sentiment [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows coins with the most positive sentiment on CoinGecko
|
||||
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_sentiment --top 10`
|
||||
* -s/--sortby - sort by given column. You can chose on from `rank, name, price_usd, price_btc`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_sentiment --top 10 --links`
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_sentiment --top 10`
|
||||
- -s/--sortby - sort by given column. You can chose on from `rank, name, price_usd, price_btc`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_sentiment --top 10 --urls`
|
||||
|
||||

|
||||
|
||||
## cg_recently <a name="cg_recently"></a>
|
||||
|
||||
```text
|
||||
usage: cg_recently [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_recently [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows coins which were recently added on CoinGecko
|
||||
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_recently --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can chose on from `rank, name, symbol, price, change_24h, change_1h, added`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_recently --top 10 --links`
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_recently --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can chose on from `rank, name, symbol, price, change_24h, change_1h, added`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_recently --top 10 --urls`
|
||||
|
||||

|
||||
|
||||
@@ -153,44 +153,44 @@ usage: cg_top_volume [-t --top] [-s --sortby] [--descend]
|
||||
|
||||
Shows coins with the highest transactions volume on CoinGecko
|
||||
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_top_volume --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can chose on from
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_top_volume --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can chose on from
|
||||
`rank, name, symbol, price, change_1h, change_24h, change_7d , volume_24h`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
## cg_gainers <a name="cg_gainers"></a>
|
||||
|
||||
```text
|
||||
usage: cg_gainers [-p --period] [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_gainers [-p --period] [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows largest gainers - coins which gain the most in given period.
|
||||
|
||||
* -p/--period - time period in which coins gained the most in price. One from `1h, 24h, 7d, 14d, 30d, 60d, 1y`.
|
||||
- -p/--period - time period in which coins gained the most in price. One from `1h, 24h, 7d, 14d, 30d, 60d, 1y`.
|
||||
If you want to see top gainers in last 24h `cg_gainers -p 24h` or top gainers in last 60days `cg_gainers -p 60d`
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_gainers --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can chose on from `rank, name, symbol, price, change, volume`.
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_gainers --period 24h --links`
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_gainers --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can chose on from `rank, name, symbol, price, change, volume`.
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_gainers --period 24h --urls`
|
||||
|
||||

|
||||
|
||||
## cg_losers <a name="cg_losers"></a>
|
||||
|
||||
```text
|
||||
usage: cg_losers [-p --period] [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_losers [-p --period] [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows largest losers - coins which lost the most in given period.
|
||||
|
||||
* -p/--period - time period in which coins lost the most in price. One from `1h, 24h, 7d, 14d, 30d, 60d, 1y`.
|
||||
- -p/--period - time period in which coins lost the most in price. One from `1h, 24h, 7d, 14d, 30d, 60d, 1y`.
|
||||
If you want to see top losers in last 24h `cg_losers -p 24h` or top losers in last 60days `cg_losers -p 60d`
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_losers --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can chose on from `rank, name, symbol, price, change, volume`.
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_losers --period 24h --links`
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_losers --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can chose on from `rank, name, symbol, price, change, volume`.
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_losers --period 24h --urls`
|
||||
|
||||
)
|
||||
|
||||
@@ -204,26 +204,26 @@ Shows Top Yield Farming Pools by Value Locked from <https://www.coingecko.com/en
|
||||
Yield farming, also referred to as liquidity mining, is a way to generate rewards with cryptocurrency holdings.
|
||||
In simple terms, it means locking up cryptocurrencies and getting rewards.
|
||||
|
||||
* -t/--top - number of yield farms to display. To display top 10 yield farms: `cg_yfarms --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can sort data by `rank, name, value_locked, return_year`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
- -t/--top - number of yield farms to display. To display top 10 yield farms: `cg_yfarms --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can sort data by `rank, name, value_locked, return_year`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
## cg_top_defi <a name="cg_top_defi"></a>
|
||||
|
||||
```text
|
||||
usage: cg_top_defi [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_top_defi [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows Top DeFi Coins by Market Capitalization from <https://www.coingecko.com/en/defi>
|
||||
DeFi or Decentralized Finance refers to financial services that are built on top of distributed networks with no central
|
||||
intermediaries.
|
||||
|
||||
* -t/--top - number of defi coins to display. To display top 10 defi coins: `cg_top_defi --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can sort data by `rank, name, symbol, price, change_24h, change_1h, change_7d`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_top_defi --links`.
|
||||
- -t/--top - number of defi coins to display. To display top 10 defi coins: `cg_top_defi --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can sort data by `rank, name, symbol, price, change_24h, change_1h, change_7d`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_top_defi --urls`.
|
||||
|
||||

|
||||
|
||||
@@ -238,28 +238,28 @@ Decentralized exchanges or DEXs are autonomous decentralized applications (DApps
|
||||
sellers to trade without having to give up control over their funds to any intermediary or custodian. Source:
|
||||
[coinmarketcap](#https://coinmarketcap.com/alexandria/article/what-are-decentralized-exchanges-dex)
|
||||
|
||||
* -t/--top - number of Decentralized Exchanges to display. To display top 10 DEX: `cg_top_dex --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can sort data by
|
||||
- -t/--top - number of Decentralized Exchanges to display. To display top 10 DEX: `cg_top_dex --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can sort data by
|
||||
`rank, name, volume_24h, n_coins, n_pairs, visits, most_traded, market_share_by_vol most_traded_pairs, market_share_by_volume`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
## cg_top_nft <a name="cg_top_nft"></a>
|
||||
|
||||
```text
|
||||
usage: cg_top_nft [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_top_nft [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows Top NFT Coins by Market Capitalization from <https://www.coingecko.com/en/nft>
|
||||
NFT (Non-fungible Token) refers to digital assets with unique characteristics.
|
||||
Examples of NFT include crypto artwork, collectibles, game items, financial products, and more.
|
||||
|
||||
* -t/--top - number of NFT to display. To display top 10 NFT Coins: `cg_top_nft --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can sort data by
|
||||
- -t/--top - number of NFT to display. To display top 10 NFT Coins: `cg_top_nft --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can sort data by
|
||||
`rank, name, symbol, price, change_24h, change_1h, change_7d, volume_24h, market_cap`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_top_nft --links`.
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_top_nft --urls`.
|
||||
|
||||

|
||||
|
||||
@@ -275,12 +275,12 @@ Display all available coins in coinpaprika. You can search with pagination mecha
|
||||
or you can just search by letter like `cp_coins --letter d --key name --top 10` it will display top 10 matches for coins
|
||||
which name starts with letter `d` and search will be done in column `name`
|
||||
|
||||
* -s/--skip: Number of records to skip. Default is 0. There are thousands of coins, so there is mechanism to paginate
|
||||
- -s/--skip: Number of records to skip. Default is 0. There are thousands of coins, so there is mechanism to paginate
|
||||
through them. e.q if you want to see only records from 500-750 you should use `cp_coins --skip 500 --top 250` or if
|
||||
you want to see 1200-1880 you should use `cp_coins --skip 1200 --top 680`.
|
||||
* -t/--top: Number of coins to display. E.g `cp_coins --top 150`
|
||||
* -l/--letter: First letters of coin by which you want to search
|
||||
* -k/--key: Search key. With this parameter you can specify in which column you would like to search. Choose on from
|
||||
- -t/--top: Number of coins to display. E.g `cp_coins --top 150`
|
||||
- -l/--letter: First letters of coin by which you want to search
|
||||
- -k/--key: Search key. With this parameter you can specify in which column you would like to search. Choose on from
|
||||
`name, id, symbol`
|
||||
|
||||
## cp_search <a name="cp_search"></a>
|
||||
@@ -291,11 +291,11 @@ usage: cp_search [-q --query] [-c --cat] [-s --sort] [-t --top] [--descend]
|
||||
|
||||
Search over CoinPaprika API
|
||||
|
||||
* -q/--query: Phrase for search
|
||||
* -c/--cat: Categories to search: currencies|exchanges|icos|people|tags|all. Default: all
|
||||
* -t/--top: Number of found records to display.
|
||||
* -s/--sort: Sort by given column. You can chose on from `category, id, name`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
- -q/--query: Phrase for search
|
||||
- -c/--cat: Categories to search: currencies|exchanges|icos|people|tags|all. Default: all
|
||||
- -t/--top: Number of found records to display.
|
||||
- -s/--sort: Sort by given column. You can chose on from `category, id, name`.
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
@@ -309,10 +309,10 @@ cmc_top [-n] [-s --sort SORTBY] [--descend]
|
||||
|
||||
This command displays the top n cryptocurrencies from coinmarketcap.com.
|
||||
|
||||
* -n: Number of coins to look at (after sort). Defaults to 10.
|
||||
* -s/--sort : Column to sort by. One of {Symbol,CMC_Rank,LastPrice,DayPctChange,MarketCap}. Defaults to sorting by the
|
||||
- -n: Number of coins to look at (after sort). Defaults to 10.
|
||||
- -s/--sort : Column to sort by. One of {Symbol,CMC_Rank,LastPrice,DayPctChange,MarketCap}. Defaults to sorting by the
|
||||
CMC rank.
|
||||
* --descend : Flag to change the sorting order. Sorts in ascending order (since default order is the Rank which starts
|
||||
- --descend : Flag to change the sorting order. Sorts in ascending order (since default order is the Rank which starts
|
||||
at 1).
|
||||
|
||||
<img width="990" alt="crypto" src="https://user-images.githubusercontent.com/25267873/115787544-4746d100-a3ba-11eb-9433-b7cb9142404a.png">
|
||||
@@ -330,9 +330,9 @@ means that baseAsset is ETH and quoteAsset is BTC. You can search with paginatio
|
||||
`bin_coins --skip [num] --top [num]` or you can just search by letter like `bin_coins --letter d --key symbol --top 10`
|
||||
it will display top 10 matches for coins which symbol starts with letter `d` and search will be done in column `symbol`
|
||||
|
||||
* -s/--skip: Number of records to skip. Default is 0. There are thousands of coins, so there is mechanism to paginate
|
||||
- -s/--skip: Number of records to skip. Default is 0. There are thousands of coins, so there is mechanism to paginate
|
||||
through them. e.q if you want to see only records from 500-750 you should use `bin_coins --skip 500 --top 250`
|
||||
* -t/--top: Number of coins to display.
|
||||
* -l/--letter: First letters of coin by which you want to search
|
||||
* -k/--key: Search key. With this parameter you can specify in which column you would like to search. Choose on from
|
||||
- -t/--top: Number of coins to display.
|
||||
- -l/--letter: First letters of coin by which you want to search
|
||||
- -k/--key: Search key. With this parameter you can specify in which column you would like to search. Choose on from
|
||||
`symbol, quoteAsset, baseAsset`
|
||||
|
||||
@@ -5,6 +5,8 @@ import pandas as pd
|
||||
from coinmarketcapapi import CoinMarketCapAPI
|
||||
import gamestonk_terminal.config_terminal as cfg
|
||||
|
||||
FILTERS = ["Symbol", "CMC_Rank", "LastPrice", "DayPctChange", "MarketCap"]
|
||||
|
||||
|
||||
def get_cmc_top_n() -> pd.DataFrame:
|
||||
"""Shows top n coins. [Source: CoinMarketCap]
|
||||
|
||||
@@ -5,6 +5,17 @@ from typing import Optional, Any
|
||||
import pandas as pd
|
||||
from gamestonk_terminal.cryptocurrency.coinpaprika_helpers import PaprikaSession
|
||||
|
||||
CATEGORIES = [
|
||||
"currencies",
|
||||
"exchanges",
|
||||
"icos",
|
||||
"people",
|
||||
"tags",
|
||||
"all",
|
||||
]
|
||||
|
||||
FILTERS = ["category", "id", "name"]
|
||||
|
||||
|
||||
def get_search_results(
|
||||
query: str, category: Optional[Any] = None, modifier: Optional[Any] = None
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -3,6 +3,7 @@ __docformat__ = "numpy"
|
||||
|
||||
import pandas as pd
|
||||
from pycoingecko import CoinGeckoAPI
|
||||
from requests.adapters import RetryError
|
||||
from gamestonk_terminal.cryptocurrency.dataframe_helpers import (
|
||||
percent_to_float,
|
||||
create_df_index,
|
||||
@@ -37,6 +38,56 @@ CATEGORIES = {
|
||||
"most_visited": 4,
|
||||
}
|
||||
|
||||
GAINERS_FILTERS = ["Rank", "Symbol", "Name", "Volume", "Price", "Change"]
|
||||
TRENDING_FILTERS = [
|
||||
"Rank",
|
||||
"Name",
|
||||
"Price_BTC",
|
||||
"Price_USD",
|
||||
]
|
||||
RECENTLY_FILTERS = [
|
||||
"Rank",
|
||||
"Name",
|
||||
"Symbol",
|
||||
"Price",
|
||||
"Change_1h",
|
||||
"Change_24h",
|
||||
"Added",
|
||||
"Url",
|
||||
]
|
||||
|
||||
YFARMS_FILTERS = [
|
||||
"Rank",
|
||||
"Name",
|
||||
"Value_Locked",
|
||||
"Return_Year",
|
||||
]
|
||||
|
||||
CAP_FILTERS = [
|
||||
"Rank",
|
||||
"Name",
|
||||
"Symbol",
|
||||
"Price",
|
||||
"Change_1h",
|
||||
"Change_24h",
|
||||
"Change_7d",
|
||||
"Volume_24h",
|
||||
"Market_Cap",
|
||||
]
|
||||
|
||||
DEX_FILTERS = [
|
||||
"Name",
|
||||
"Rank",
|
||||
"Volume_24h",
|
||||
"Coins",
|
||||
"Pairs",
|
||||
"Visits",
|
||||
"Most_Traded",
|
||||
"Market_Share",
|
||||
]
|
||||
|
||||
# TODO: convert Volume and other str that should be int to int otherwise sort won't work
|
||||
|
||||
|
||||
def get_gainers_or_losers(period: str = "1h", typ: str = "gainers") -> pd.DataFrame:
|
||||
"""Scrape data about top gainers - coins which gain the most in given period and
|
||||
@@ -66,7 +117,12 @@ def get_gainers_or_losers(period: str = "1h", typ: str = "gainers") -> pd.DataFr
|
||||
)
|
||||
|
||||
url = f"https://www.coingecko.com/en/coins/trending{PERIODS.get(period)}"
|
||||
rows = scrape_gecko_data(url).find_all("tbody")[category.get(typ)].find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find_all("tbody")[category.get(typ)].find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
url = GECKO_BASE_URL + row.find("a")["href"]
|
||||
@@ -117,9 +173,14 @@ def get_discovered_coins(category: str = "trending") -> pd.DataFrame:
|
||||
f"Wrong category name\nPlease chose one from list: {CATEGORIES.keys()}"
|
||||
)
|
||||
url = "https://www.coingecko.com/en/discover"
|
||||
popular = scrape_gecko_data(url).find_all(
|
||||
"div", class_="col-12 col-sm-6 col-md-6 col-lg-4"
|
||||
)[CATEGORIES[category]]
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
popular = scraped_data.find_all("div", class_="col-12 col-sm-6 col-md-6 col-lg-4")[
|
||||
CATEGORIES[category]
|
||||
]
|
||||
rows = popular.find_all("a")
|
||||
results = []
|
||||
btc_price = get_btc_price()
|
||||
@@ -165,7 +226,12 @@ def get_recently_added_coins() -> pd.DataFrame:
|
||||
]
|
||||
|
||||
url = "https://www.coingecko.com/en/coins/recently_added"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
|
||||
for row in rows:
|
||||
@@ -211,7 +277,12 @@ def get_yield_farms() -> pd.DataFrame:
|
||||
"Return_Year",
|
||||
]
|
||||
url = "https://www.coingecko.com/en/yield-farming"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
row_cleaned = clean_row(row)[:-2]
|
||||
@@ -276,7 +347,12 @@ def get_top_volume_coins() -> pd.DataFrame:
|
||||
"Market_Cap",
|
||||
]
|
||||
url = "https://www.coingecko.com/en/coins/high_volume"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
row_cleaned = clean_row(row)
|
||||
@@ -302,7 +378,12 @@ def get_top_defi_coins() -> pd.DataFrame:
|
||||
"""
|
||||
|
||||
url = "https://www.coingecko.com/en/defi"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
|
||||
@@ -362,7 +443,12 @@ def get_top_dexes() -> pd.DataFrame:
|
||||
"Market_Share",
|
||||
]
|
||||
url = "https://www.coingecko.com/en/dex"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
row_cleaned = clean_row(row)
|
||||
@@ -399,7 +485,12 @@ def get_top_nfts() -> pd.DataFrame:
|
||||
"""
|
||||
|
||||
url = "https://www.coingecko.com/en/nft"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
link = GECKO_BASE_URL + row.find("a")["href"]
|
||||
|
||||
@@ -39,35 +39,39 @@ def display_gainers(
|
||||
if sortby == "Change":
|
||||
sortby = f"%Change_{period}"
|
||||
|
||||
df = pycoingecko_model.get_gainers_or_losers(
|
||||
period=period, typ="gainers"
|
||||
).sort_values(by=sortby, ascending=descend)
|
||||
df = pycoingecko_model.get_gainers_or_losers(period=period, typ="gainers")
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
df_data = df.copy()
|
||||
df_data = df.copy()
|
||||
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"gainers",
|
||||
df_data,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"gainers",
|
||||
df_data,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_losers(
|
||||
@@ -94,35 +98,39 @@ def display_losers(
|
||||
if sortby == "Change":
|
||||
sortby = f"%Change_{period}"
|
||||
|
||||
df = pycoingecko_model.get_gainers_or_losers(
|
||||
period=period, typ="losers"
|
||||
).sort_values(by=sortby, ascending=descend)
|
||||
df = pycoingecko_model.get_gainers_or_losers(period=period, typ="losers")
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
df_data = df.copy()
|
||||
df_data = df.copy()
|
||||
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"losers",
|
||||
df_data,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"losers",
|
||||
df_data,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_discover(
|
||||
@@ -151,37 +159,42 @@ def display_discover(
|
||||
"""
|
||||
|
||||
df = pycoingecko_model.get_discovered_coins(category=category)
|
||||
df.index = df.index + 1
|
||||
df.reset_index(inplace=True)
|
||||
df.rename(columns={"index": "Rank"}, inplace=True)
|
||||
if not df.empty:
|
||||
df.index = df.index + 1
|
||||
df.reset_index(inplace=True)
|
||||
df.rename(columns={"index": "Rank"}, inplace=True)
|
||||
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
df_data = df.copy()
|
||||
df_data = df.copy()
|
||||
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
category,
|
||||
df_data,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
category,
|
||||
df_data,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_recently_added(
|
||||
@@ -345,30 +358,34 @@ def display_top_volume_coins(top: int, sortby: str, descend: bool, export: str)
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = pycoingecko_model.get_top_volume_coins().sort_values(
|
||||
by=sortby, ascending=descend
|
||||
)
|
||||
df = pycoingecko_model.get_top_volume_coins()
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"volume",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"volume",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_top_nft(
|
||||
@@ -396,33 +413,38 @@ def display_top_nft(
|
||||
|
||||
df = pycoingecko_model.get_top_nfts().sort_values(by=sortby, ascending=descend)
|
||||
|
||||
df_data = df.copy()
|
||||
if not df.empty:
|
||||
df_data = df.copy()
|
||||
|
||||
if links is True:
|
||||
df = df[["Rank", "Name", "Symbol", "Url"]]
|
||||
else:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
if links is True:
|
||||
df = df[["Rank", "Name", "Symbol", "Url"]]
|
||||
else:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"nft",
|
||||
df_data,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"nft",
|
||||
df_data,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_yieldfarms(top: int, sortby: str, descend: bool, export: str) -> None:
|
||||
@@ -442,27 +464,33 @@ def display_yieldfarms(top: int, sortby: str, descend: bool, export: str) -> Non
|
||||
"""
|
||||
|
||||
df = pycoingecko_model.get_yield_farms()
|
||||
df_data = df.copy()
|
||||
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
if not df.empty:
|
||||
df_data = df.copy()
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".0f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".0f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"yfarms",
|
||||
df_data,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"yfarms",
|
||||
df_data,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
@@ -6,46 +6,46 @@ will be available in menu.
|
||||
|
||||
[CoinGecko](#CoinGecko)
|
||||
|
||||
* [info](#info)
|
||||
* show coin base information
|
||||
* [market](#market)
|
||||
* show market data
|
||||
* [ath](#ath)
|
||||
* show all time high related metrics
|
||||
* [atl](#atl)
|
||||
* show all time low related metrics
|
||||
* [score](#score)
|
||||
* show different scores related to loaded coin
|
||||
* [web](#web)
|
||||
* show websites related to loaded coin
|
||||
* [social](#social)
|
||||
* show social media urls for loaded coin
|
||||
* [bc](#bc)
|
||||
* show block-chain explorers urls for loaded coins
|
||||
* [dev](#dev)
|
||||
* show developers data for loaded coins
|
||||
- [info](#info)
|
||||
- show coin base information
|
||||
- [market](#market)
|
||||
- show market data
|
||||
- [ath](#ath)
|
||||
- show all time high related metrics
|
||||
- [atl](#atl)
|
||||
- show all time low related metrics
|
||||
- [score](#score)
|
||||
- show different scores related to loaded coin
|
||||
- [web](#web)
|
||||
- show websites related to loaded coin
|
||||
- [social](#social)
|
||||
- show social media urls for loaded coin
|
||||
- [bc](#bc)
|
||||
- show block-chain explorers urls for loaded coins
|
||||
- [dev](#dev)
|
||||
- show developers data for loaded coins
|
||||
|
||||
[CoinPaprika](#CoinPaprika)
|
||||
|
||||
* [basic](#basic)
|
||||
* show coin basic information
|
||||
* [ps](#ps)
|
||||
* show coin price, supply, market cap related metrics.
|
||||
* [mkt](#mkt)
|
||||
* show all markets for loaded coin
|
||||
* [ex](#ex)
|
||||
* show all exchanges where loaded coin is listed
|
||||
* [twitter](#twitter)
|
||||
* show up to 50 last tweets for loaded coin
|
||||
* [events](#events )
|
||||
* show events related to loaded coin
|
||||
- [basic](#basic)
|
||||
- show coin basic information
|
||||
- [ps](#ps)
|
||||
- show coin price, supply, market cap related metrics.
|
||||
- [mkt](#mkt)
|
||||
- show all markets for loaded coin
|
||||
- [ex](#ex)
|
||||
- show all exchanges where loaded coin is listed
|
||||
- [twitter](#twitter)
|
||||
- show up to 50 last tweets for loaded coin
|
||||
- [events](#events)
|
||||
- show events related to loaded coin
|
||||
|
||||
[Binance](#Binance)
|
||||
|
||||
* [book](#book)
|
||||
* show order book
|
||||
* [balance](#book)
|
||||
* show coin balance
|
||||
- [book](#book)
|
||||
- show order book
|
||||
- [balance](#book)
|
||||
- show coin balance
|
||||
|
||||
## chart <a name="chart">
|
||||
|
||||
@@ -117,7 +117,7 @@ usage: score
|
||||
```
|
||||
|
||||
In this view you can find different kind of scores for loaded coin. Those scores represents different rankings,
|
||||
sentiment metrics, some user stats and other, like: coingecko_rank, coingecko_score, developer_score, community_score,
|
||||
sentiment metrics, some user stats and other, like: coingecko_rank, coingecko_score, developer_score, community_score,
|
||||
liquidity_score, sentiment_votes_up_percentage, sentiment_votes_down_percentage, public_interest_score, facebook_likes,
|
||||
twitter_followers, reddit_average_posts_48h, reddit_average_comments_48h, reddit_subscribers,
|
||||
reddit_accounts_active_48h, telegram_channel_user_count, alexa_rank, bing_matches.
|
||||
@@ -188,25 +188,25 @@ usage: ps [--vs]
|
||||
Price, supply related data for loaded coin. There you find metrics like:
|
||||
name, symbol, rank, supply, volume, ath, market cap, price change related metrics
|
||||
|
||||
* --vs: The currency to look against. Available options are:
|
||||
- --vs: The currency to look against. Available options are:
|
||||
`BTC,ETH,USD,EUR,PLN,KRW,GBP,CAD,JPY,RUB,TRY,NZD,AUD,CHF,UAH,HKD,SGD,NGN,PHP,MXN,BRL,THB,CLP,CNY,CZK,DKK,HUF,IDR,ILS,INR,MYR,NOK,PKR,SEK,TWD,ZAR,VND,BOB,COP,PEN,ARS,ISK`.
|
||||
Default: USD
|
||||
|
||||
## mkt <a name="mkt"></a>
|
||||
|
||||
```text
|
||||
usage: mkt [--vs] [-t --top] [-s --sort] [--descend] [-l --links]
|
||||
usage: mkt [--vs] [-t --top] [-s --sort] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Get all markets found for given coin.
|
||||
|
||||
* --vs: The currency to look against. Available options are:
|
||||
- --vs: The currency to look against. Available options are:
|
||||
`BTC,ETH,USD,EUR,PLN,KRW,GBP,CAD,JPY,RUB,TRY,NZD,AUD,CHF,UAH,HKD,SGD,NGN,PHP,MXN,BRL,THB,CLP,CNY,CZK,DKK,HUF,IDR,ILS,INR,MYR,NOK,PKR,SEK,TWD,ZAR,VND,BOB,COP,PEN,ARS,ISK`.
|
||||
Default: USD
|
||||
* -t/--top - number of markets to display. To display top 10 markets: `mkt --top 10 --sort trust_score`
|
||||
* -s/--sort - sort by given column. You can chose on from `pct_volume_share, exchange, pair, trust_score, volume,price`.
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `mkt --links`
|
||||
- -t/--top - number of markets to display. To display top 10 markets: `mkt --top 10 --sort trust_score`
|
||||
- -s/--sort - sort by given column. You can chose on from `pct_volume_share, exchange, pair, trust_score, volume,price`.
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `mkt --links`
|
||||
|
||||
## ex <a name="ex"></a>
|
||||
|
||||
@@ -216,9 +216,9 @@ usage: ex [-t --top] [-s --sort] [--descend]
|
||||
|
||||
Get all exchanges found for given coin.
|
||||
|
||||
* -t/--top: Number of exchanges to display. To display top 10 exchanges: `ex --top 10 --sort fiats`
|
||||
* -s/--sort: Sort by given column. You can chose on from `id, name, adjusted_volume_24h_share, fiats`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
- -t/--top: Number of exchanges to display. To display top 10 exchanges: `ex --top 10 --sort fiats`
|
||||
- -s/--sort: Sort by given column. You can chose on from `id, name, adjusted_volume_24h_share, fiats`.
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
|
||||
## twitter <a name="twitter"></a>
|
||||
|
||||
@@ -228,39 +228,39 @@ usage: twitter [-t --top] [-s --sort] [--descend]
|
||||
|
||||
Show last tweets for given coin.
|
||||
|
||||
* -t/--top: Number of tweets to display. To display top 10 tweets: `twitter --top 10 --sort date`
|
||||
* -s/--sort: Sort by given column. You can chose on from `date, user_name, status, retweet_count, like_count`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
- -t/--top: Number of tweets to display. To display top 10 tweets: `twitter --top 10 --sort date`
|
||||
- -s/--sort: Sort by given column. You can chose on from `date, user_name, status, retweet_count, like_count`.
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
|
||||
## events <a name="events"></a>
|
||||
|
||||
```text
|
||||
usage: events [-t --top] [-s --sort] [--descend] [-l --links]
|
||||
usage: events [-t --top] [-s --sort] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Show information about most important coins events.
|
||||
|
||||
* -t/--top: Number of events to display. To display top 10 events: `events --top 10 --sort date`
|
||||
* -s/--sort: Sort by given column. You can chose on from `date, date_to, name, description, is_conference`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
* -l/--links: Flag to show urls. Using this flag will add additional column with urls e.g. `events --links`. If you
|
||||
- -t/--top: Number of events to display. To display top 10 events: `events --top 10 --sort date`
|
||||
- -s/--sort: Sort by given column. You can chose on from `date, date_to, name, description, is_conference`.
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
- -u/--urls: Flag to show urls. Using this flag will add additional column with urls e.g. `events --links`. If you
|
||||
will use this flag url column will be displayed.
|
||||
|
||||
# Binance <a name="Binance"></a>
|
||||
|
||||
## book <a name="book"></a>
|
||||
## book <a name="book"></a>
|
||||
|
||||
```text
|
||||
usage: book -l/--limit
|
||||
```
|
||||
|
||||
Gets and shows the order book for the given coin/currency. Shows the cumulative amount of orders.
|
||||
Gets and shows the order book for the given coin/currency. Shows the cumulative amount of orders.
|
||||
|
||||
* -l/--limit Number of orders to get on each side. One of [5,10,20,50,100,500,1000,5000]. Defaults to 100.
|
||||
- -l/--limit Number of orders to get on each side. One of [5,10,20,50,100,500,1000,5000]. Defaults to 100.
|
||||
|
||||

|
||||
|
||||
## balance <a name="balance"></a>
|
||||
## balance <a name="balance"></a>
|
||||
|
||||
```text
|
||||
usage: balance
|
||||
|
||||
@@ -6,6 +6,8 @@ from gamestonk_terminal import config_terminal as cfg
|
||||
|
||||
api_url = "https://open-api.coinglass.com/api/pro/v1/"
|
||||
|
||||
INTERVALS = [0, 1, 2, 4]
|
||||
|
||||
|
||||
def get_open_interest_per_exchange(symbol: str, interval: int) -> pd.DataFrame:
|
||||
"""Returns open interest by exchange for a certain symbol
|
||||
|
||||
@@ -94,7 +94,7 @@ def get_coin_events_by_id(coin_id: str = "eth-ethereum") -> pd.DataFrame:
|
||||
|
||||
session = PaprikaSession()
|
||||
res = session.make_request(session.ENDPOINTS["coin_events"].format(coin_id))
|
||||
if not res:
|
||||
if not res or "error" in res:
|
||||
return pd.DataFrame()
|
||||
data = pd.DataFrame(res)
|
||||
data["description"] = data["description"].apply(
|
||||
|
||||
@@ -18,6 +18,21 @@ register_matplotlib_converters()
|
||||
# pylint: disable=inconsistent-return-statements
|
||||
# pylint: disable=C0302, too-many-lines
|
||||
|
||||
TWEETS_FILTERS = ["date", "user_name", "status", "retweet_count", "like_count"]
|
||||
|
||||
EVENTS_FILTERS = ["date", "date_to", "name", "description", "is_conference"]
|
||||
|
||||
EX_FILTERS = ["id", "name", "adjusted_volume_24h_share", "fiats"]
|
||||
|
||||
MARKET_FILTERS = [
|
||||
"pct_volume_share",
|
||||
"exchange",
|
||||
"pair",
|
||||
"trust_score",
|
||||
"volume",
|
||||
"price",
|
||||
]
|
||||
|
||||
CURRENCIES = [
|
||||
"BTC",
|
||||
"ETH",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,7 @@ from gamestonk_terminal.common.behavioural_analysis.finbrain_view import (
|
||||
from gamestonk_terminal.common.behavioural_analysis.finbrain_model import get_sentiment
|
||||
from gamestonk_terminal.helper_funcs import export_data
|
||||
|
||||
PATH = os.path.dirname(os.path.abspath(__file__))
|
||||
PATH = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
try:
|
||||
COINS_JSON = pd.read_json(PATH + "/data/finbrain_coins.json")
|
||||
|
||||
@@ -158,6 +158,8 @@ GLASSNODE_SUPPORTED_ASSETS = [
|
||||
"ZRX",
|
||||
]
|
||||
|
||||
INTERVALS = ["1h", "24h", "10m", "1w", "1month"]
|
||||
|
||||
|
||||
def get_active_addresses(
|
||||
asset: str, interval: str, since: int, until: int
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import argparse
|
||||
from typing import List
|
||||
import difflib
|
||||
from typing import List, Union
|
||||
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
@@ -21,10 +22,16 @@ class NFTController:
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS = [
|
||||
@@ -36,33 +43,35 @@ class NFTController:
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Constructor"""
|
||||
self.nft_parser = argparse.ArgumentParser(add_help=False, prog="nft")
|
||||
self.nft_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in self.CHOICES}
|
||||
)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
|
||||
help_text = """
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
Non Fungible Tokens Menu:
|
||||
|
||||
nftcalendar.io:
|
||||
today today's NFT drops
|
||||
upcoming upcoming NFT drops
|
||||
ongoing Ongoing NFT drops
|
||||
newest Recently NFTs added
|
||||
"""
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
def switch(self, an_input: str):
|
||||
@@ -80,23 +89,37 @@ nftcalendar.io:
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.nft_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -104,17 +127,50 @@ nftcalendar.io:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help Command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "nft")
|
||||
self.queue.insert(0, "crypto")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "reset", "crypto", "nft"]
|
||||
|
||||
@try_except
|
||||
def call_today(self, other_args: List[str]):
|
||||
@@ -126,23 +182,22 @@ nftcalendar.io:
|
||||
description="Today's NFT drops [Source: nftcalendar.io]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
type=check_positive,
|
||||
help="Number of NFT collections to display",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
default=5,
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
nftcalendar_view.display_nft_today_drops(
|
||||
num=ns_parser.num,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
nftcalendar_view.display_nft_today_drops(
|
||||
num=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_upcoming(self, other_args: List[str]):
|
||||
@@ -154,23 +209,22 @@ nftcalendar.io:
|
||||
description="Upcoming's NFT drops [Source: nftcalendar.io]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
type=check_positive,
|
||||
help="Number of NFT collections to display",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
default=5,
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
nftcalendar_view.display_nft_upcoming_drops(
|
||||
num=ns_parser.num,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
nftcalendar_view.display_nft_upcoming_drops(
|
||||
num=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ongoing(self, other_args: List[str]):
|
||||
@@ -182,23 +236,22 @@ nftcalendar.io:
|
||||
description="Ongoing's NFT drops [Source: nftcalendar.io]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
type=check_positive,
|
||||
help="Number of NFT collections to display",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
default=5,
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
nftcalendar_view.display_nft_ongoing_drops(
|
||||
num=ns_parser.num,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
nftcalendar_view.display_nft_ongoing_drops(
|
||||
num=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_newest(self, other_args: List[str]):
|
||||
@@ -210,48 +263,95 @@ nftcalendar.io:
|
||||
description="Newest's NFT drops [Source: nftcalendar.io]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
type=check_positive,
|
||||
help="Number of NFT collections to display",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
default=5,
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
nftcalendar_view.display_nft_newest_drops(
|
||||
num=ns_parser.num,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
nftcalendar_view.display_nft_newest_drops(
|
||||
num=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
def menu(queue: List[str] = None):
|
||||
"""NFT Menu"""
|
||||
nft_controller = NFTController()
|
||||
nft_controller.call_help(None)
|
||||
while True:
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in nft_controller.CHOICES}
|
||||
)
|
||||
nft_controller = NFTController(queue=queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (nft)> ",
|
||||
completer=completer,
|
||||
)
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if nft_controller.queue and len(nft_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if nft_controller.queue[0] in ("q", "..", "quit"):
|
||||
if len(nft_controller.queue) > 1:
|
||||
return nft_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = nft_controller.queue[0]
|
||||
nft_controller.queue = nft_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in nft_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /crypto/nft/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (nft)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
nft_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and nft_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /crypto/nft/ $ ",
|
||||
completer=nft_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /crypto/nft/ $ ")
|
||||
|
||||
try:
|
||||
process_input = nft_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
nft_controller.queue = nft_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exit\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
nft_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
else:
|
||||
candidate_input = similar_cmd[0]
|
||||
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
nft_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
nft_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
an_input = ""
|
||||
nft_controller.queue = []
|
||||
|
||||
@@ -32,6 +32,38 @@ class BitQueryTimeoutException(Exception):
|
||||
|
||||
BQ_URL = "https://graphql.bitquery.io"
|
||||
CURRENCIES = ["ETH", "USD", "BTC", "USDT"]
|
||||
LT_FILTERS = ["exchange", "trades", "tradeAmount"]
|
||||
LT_KIND = ["dex", "time"]
|
||||
INTERVALS = ["day", "month", "week"]
|
||||
DVCP_FILTERS = [
|
||||
"date",
|
||||
"exchange",
|
||||
"base",
|
||||
"quote",
|
||||
"open",
|
||||
"high",
|
||||
"low",
|
||||
"close",
|
||||
"tradeAmount",
|
||||
"trades",
|
||||
]
|
||||
UEAT_FILTERS = [
|
||||
"date",
|
||||
"uniqueSenders",
|
||||
"transactions",
|
||||
"averageGasPrice",
|
||||
"mediumGasPrice",
|
||||
"maximumGasPrice",
|
||||
]
|
||||
TTCP_FILTERS = ["base", "quoted", "trades", "tradeAmount"]
|
||||
BAAS_FILTERS = [
|
||||
"date",
|
||||
"baseCurrency",
|
||||
"quoteCurrency",
|
||||
"dailySpread",
|
||||
"averageBidPrice",
|
||||
"averageAskPrice",
|
||||
]
|
||||
DECENTRALIZED_EXCHANGES = [
|
||||
"1inch",
|
||||
"AfroDex",
|
||||
@@ -469,7 +501,7 @@ def get_token_volume_on_dexes(
|
||||
return pd.DataFrame()
|
||||
|
||||
df = _extract_dex_trades(data)[["exchange.fullName", "tradeAmount", "count"]]
|
||||
df.columns = ["exchange", "tradeAmount", "trades"]
|
||||
df.columns = LT_FILTERS
|
||||
return df[~df["exchange"].str.startswith("<")].sort_values(
|
||||
by="tradeAmount", ascending=False
|
||||
)
|
||||
@@ -497,7 +529,7 @@ def get_ethereum_unique_senders(interval: str = "day", limit: int = 90) -> pd.Da
|
||||
"week": 7,
|
||||
}
|
||||
|
||||
if interval not in intervals.keys():
|
||||
if interval not in intervals:
|
||||
interval = "day"
|
||||
|
||||
days = min(limit * intervals[interval], 90)
|
||||
@@ -527,16 +559,7 @@ def get_ethereum_unique_senders(interval: str = "day", limit: int = 90) -> pd.Da
|
||||
|
||||
df = pd.DataFrame(data["ethereum"]["transactions"])
|
||||
df["date"] = df["date"].apply(lambda x: x["date"])
|
||||
return df[
|
||||
[
|
||||
"date",
|
||||
"uniqueSenders",
|
||||
"transactions",
|
||||
"averageGasPrice",
|
||||
"mediumGasPrice",
|
||||
"maximumGasPrice",
|
||||
]
|
||||
]
|
||||
return df[UEAT_FILTERS]
|
||||
|
||||
|
||||
def get_most_traded_pairs(
|
||||
@@ -585,7 +608,7 @@ def get_most_traded_pairs(
|
||||
|
||||
df = _extract_dex_trades(data)
|
||||
df.columns = ["trades", "tradeAmount", "base", "quoted"]
|
||||
return df[["base", "quoted", "trades", "tradeAmount"]]
|
||||
return df[TTCP_FILTERS]
|
||||
|
||||
|
||||
def get_spread_for_crypto_pair(
|
||||
|
||||
@@ -11,6 +11,44 @@ import requests
|
||||
from gamestonk_terminal.cryptocurrency.dataframe_helpers import create_df_index
|
||||
import gamestonk_terminal.config_terminal as cfg
|
||||
|
||||
PRICES_FILTERS = [
|
||||
"date",
|
||||
"cap",
|
||||
"volumeConverted",
|
||||
"open",
|
||||
"high",
|
||||
"close",
|
||||
"low",
|
||||
]
|
||||
|
||||
TOP_FILTERS = [
|
||||
"rank",
|
||||
"name",
|
||||
"symbol",
|
||||
"price",
|
||||
"txsCount",
|
||||
"transfersCount",
|
||||
"holdersCount",
|
||||
]
|
||||
|
||||
TH_FILTERS = [
|
||||
"value",
|
||||
]
|
||||
|
||||
BALANCE_FILTERS = [
|
||||
"index",
|
||||
"balance",
|
||||
"tokenName",
|
||||
"tokenSymbol",
|
||||
]
|
||||
|
||||
HIST_FILTERS = ["timestamp", "transactionHash", "token", "value"]
|
||||
|
||||
HOLDERS_FILTERS = [
|
||||
"balance",
|
||||
"share",
|
||||
]
|
||||
|
||||
|
||||
def split_cols_with_dot(column: str) -> str:
|
||||
"""Split column name in data frame columns whenever there is a dot between 2 words.
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,16 @@ import pandas as pd
|
||||
import numpy as np
|
||||
import gamestonk_terminal.config_terminal as cfg
|
||||
|
||||
FILTERS = [
|
||||
"date",
|
||||
"symbol",
|
||||
"blockchain",
|
||||
"amount",
|
||||
"amount_usd",
|
||||
"from",
|
||||
"to",
|
||||
]
|
||||
|
||||
|
||||
class ApiKeyException(Exception):
|
||||
"""Api Key Exception object"""
|
||||
|
||||
210
gamestonk_terminal/cryptocurrency/overview/README.md
vendored
210
gamestonk_terminal/cryptocurrency/overview/README.md
vendored
@@ -5,69 +5,69 @@ The usage of the following commands along with an example will be exploited belo
|
||||
|
||||
[CoinGecko](#CoinGecko)
|
||||
|
||||
* [cg_global](#cg_global)
|
||||
* show global crypto market info
|
||||
* [cg_defi](#cg_defi)
|
||||
* show global DeFi market info
|
||||
* [cg_stables](#cg_stables)
|
||||
* show Stable Coins
|
||||
* [cg_nft_today](#cg_nft_today)
|
||||
* show NFT Of The Day
|
||||
* [cg_nft](#cg_nft)
|
||||
* show NFT Market Status
|
||||
* [cg_exchanges](#cg_exchanges)
|
||||
* show Top Crypto Exchanges
|
||||
* [cg_exrates](#cg_exrates)
|
||||
* show Coin Exchange Rates
|
||||
* [cg_platforms](#cg_platforms)
|
||||
* show Crypto Financial Platforms
|
||||
* [cg_products](#cg_products)
|
||||
* show Crypto Financial Products
|
||||
* [cg_indexes](#cg_indexes)
|
||||
* show Crypto Indexes
|
||||
* [cg_derivatives](#cg_derivatives)
|
||||
* show Crypto Derivatives
|
||||
* [cg_categories](#cg_categories)
|
||||
* show Crypto Categories
|
||||
* [cg_hold](#cg_hold)
|
||||
* show eth, btc holdings overview statistics
|
||||
* [cg_companies](#cg_companies)
|
||||
* show eth, btc holdings by public companies
|
||||
- [cg_global](#cg_global)
|
||||
- show global crypto market info
|
||||
- [cg_defi](#cg_defi)
|
||||
- show global DeFi market info
|
||||
- [cg_stables](#cg_stables)
|
||||
- show Stable Coins
|
||||
- [cg_nft_today](#cg_nft_today)
|
||||
- show NFT Of The Day
|
||||
- [cg_nft](#cg_nft)
|
||||
- show NFT Market Status
|
||||
- [cg_exchanges](#cg_exchanges)
|
||||
- show Top Crypto Exchanges
|
||||
- [cg_exrates](#cg_exrates)
|
||||
- show Coin Exchange Rates
|
||||
- [cg_platforms](#cg_platforms)
|
||||
- show Crypto Financial Platforms
|
||||
- [cg_products](#cg_products)
|
||||
- show Crypto Financial Products
|
||||
- [cg_indexes](#cg_indexes)
|
||||
- show Crypto Indexes
|
||||
- [cg_derivatives](#cg_derivatives)
|
||||
- show Crypto Derivatives
|
||||
- [cg_categories](#cg_categories)
|
||||
- show Crypto Categories
|
||||
- [cg_hold](#cg_hold)
|
||||
- show eth, btc holdings overview statistics
|
||||
- [cg_companies](#cg_companies)
|
||||
- show eth, btc holdings by public companies
|
||||
|
||||
[CoinPaprika](#CoinPaprika)
|
||||
|
||||
* [cp_global](#cp_global)
|
||||
* show global crypto market info
|
||||
* [cp_info](#cp_info)
|
||||
* show basic info about all coins available on CoinPaprika
|
||||
* [cp_markets](#cp_markets)
|
||||
* show market related info about all coins available on CoinPaprika
|
||||
* [cp_exchanges](#cp_exchanges)
|
||||
* list all exchanges available on CoinPaprika
|
||||
* [cp_exmarkets](#cp_exmarkets)
|
||||
* show all available markets on given exchange
|
||||
* [cp_platforms](#cp_platforms)
|
||||
* list blockchain platforms/ecosystems eg. ethereum, solana, kusama, terra
|
||||
* [cp_contracts](#cp_contracts)
|
||||
* show all smart contracts for given platform
|
||||
- [cp_global](#cp_global)
|
||||
- show global crypto market info
|
||||
- [cp_info](#cp_info)
|
||||
- show basic info about all coins available on CoinPaprika
|
||||
- [cp_markets](#cp_markets)
|
||||
- show market related info about all coins available on CoinPaprika
|
||||
- [cp_exchanges](#cp_exchanges)
|
||||
- list all exchanges available on CoinPaprika
|
||||
- [cp_exmarkets](#cp_exmarkets)
|
||||
- show all available markets on given exchange
|
||||
- [cp_platforms](#cp_platforms)
|
||||
- list blockchain platforms/ecosystems eg. ethereum, solana, kusama, terra
|
||||
- [cp_contracts](#cp_contracts)
|
||||
- show all smart contracts for given platform
|
||||
|
||||
# CoinGecko <a name="CoinGecko">
|
||||
|
||||
## cg_global <a name="cg_global"></a>
|
||||
## cg_global <a name="cg_global"></a>
|
||||
|
||||
```text
|
||||
usage: cg_global
|
||||
```
|
||||
|
||||
Display global statistics about Crypto Market like: active_cryptocurrencies, upcoming_icos, ongoing_icos, ended_icos,
|
||||
markets, market_cap_change_percentage_24h, eth_market_cap_in_pct, btc_market_cap_in_pct, altcoin_market_cap_in_pct.
|
||||
markets, market_cap_change_percentage_24h, eth_market_cap_in_pct, btc_market_cap_in_pct, altcoin_market_cap_in_pct.
|
||||
|
||||

|
||||
|
||||
## news <a name="news"></a>
|
||||
## news <a name="news"></a>
|
||||
|
||||
```text
|
||||
usage: cg_news [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_news [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows latest news from <https://www.coingecko.com/en/news>. Display columns: index, title, author, posted columns.
|
||||
@@ -75,11 +75,11 @@ You can sort by each of column above, using `--sort` parameter and also do it de
|
||||
also possible to display urls to news with `--links` flag. If you want to display top 75 news, sorted by author use
|
||||
`cg_news -t 75 -s author` if you want to see urls to source of the news use `cg_news -t 75 -l`
|
||||
|
||||
* -t/--top - number of news to display. One page of news contains 25 news, so to get 250 news script needs to scrape 10
|
||||
- -t/--top - number of news to display. One page of news contains 25 news, so to get 250 news script needs to scrape 10
|
||||
pages (it can take some time). Default 100. E.g `news --top 150`
|
||||
* -s/--sortby - sort by given column. You can chose on from `index, title, author, posted`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls and display only `index, url` columns
|
||||
- -s/--sortby - sort by given column. You can chose on from `index, title, author, posted`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls and display only `index, url` columns
|
||||
|
||||

|
||||
|
||||
@@ -98,17 +98,17 @@ defi_to_eth_ratio, trading_volume_24h, defi_dominance, top_coin_name, top_coin_d
|
||||
## cg_stables <a name="cg_stables"></a>
|
||||
|
||||
```text
|
||||
usage: cg_stables [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_stables [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows stablecoins by market capitalization. Stablecoins are cryptocurrencies that attempt to peg their market value to
|
||||
some external reference like the U.S. dollar or to a commodity's price such as gold.
|
||||
|
||||
* -t/--top - number of coins to display. To display top 10 coins: `cg_stables --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can sort data by
|
||||
- -t/--top - number of coins to display. To display top 10 coins: `cg_stables --top 10 --sortby rank`
|
||||
- -s/--sortby - sort by given column. You can sort data by
|
||||
`rank, name, symbol, price, change_24h, exchanges, market_cap, change_30d`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_stables --links`.
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_stables --links`.
|
||||
If you will use this flag `rank, name, symbol, url` columns will be displayed.
|
||||
|
||||

|
||||
@@ -140,16 +140,16 @@ Examples of NFT include crypto artwork, collectibles, game items, financial prod
|
||||
## cg_exchanges <a name="cg_exchanges"></a>
|
||||
|
||||
```text
|
||||
usage: cg_exchanges [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_exchanges [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows top crypto exchanges base on trust score.
|
||||
|
||||
* -t/--top - number of exchanges to display. To display top 10 exchanges by trust_score:
|
||||
- -t/--top - number of exchanges to display. To display top 10 exchanges by trust_score:
|
||||
`cg_exchanges --top 10 --sortby trust_score --descend`
|
||||
* -s/--sortby - sort by given column. You can sort data by `rank, trust_score, id, name, country, established, trade_volume_24h_btc`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_exchanges --links`.
|
||||
- -s/--sortby - sort by given column. You can sort data by `rank, trust_score, id, name, country, established, trade_volume_24h_btc`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_exchanges --links`.
|
||||
Using `links` parameter will display only `rank, name, url` columns
|
||||
|
||||

|
||||
@@ -162,10 +162,10 @@ usage: cg_exrates [-t --top] [-s --sortby] [--descend]
|
||||
|
||||
Shows list of crypto, fiats, commodity exchange rates from CoinGecko
|
||||
|
||||
* -t/--top - number of exchanges to display. To display top 10 exchanges by trust_score:
|
||||
- -t/--top - number of exchanges to display. To display top 10 exchanges by trust_score:
|
||||
`cg_exrates --top 10 --sortby name --descend`
|
||||
* -s/--sortby - sort by given column. You can sort by `index,name,unit, value, type`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
- -s/--sortby - sort by given column. You can sort by `index,name,unit, value, type`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
@@ -178,9 +178,9 @@ usage: cg_platforms [-t --top] [-s --sortby] [--descend]
|
||||
Shows Top Crypto Financial Platforms in which you can borrow or lend your crypto e.g. Celsius, Nexo, Crypto.com, Aave
|
||||
and others.
|
||||
|
||||
* -t/--top - number of crypto platforms to display. To display top 10 platforms: `cg_platforms --top 10 --sortby name`
|
||||
* -s/--sortby - sort by given column. You can sort data by `rank, name, category, centralized`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
- -t/--top - number of crypto platforms to display. To display top 10 platforms: `cg_platforms --top 10 --sortby name`
|
||||
- -s/--sortby - sort by given column. You can sort data by `rank, name, category, centralized`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
@@ -192,9 +192,9 @@ usage: cg_products [-t --top] [-s --sortby] [--descend]
|
||||
|
||||
Shows Top Crypto Financial Products with which you can earn yield, borrow or lend your crypto.
|
||||
|
||||
* -t/--top - number of crypto products to display. To display top 10 products: `cg_products --top 10 --sortby name`
|
||||
* -s/--sortby - sort by given column. You can sort data by `rank, platform, identifier, supply_rate_percentage, borrow_rate_percentage`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
- -t/--top - number of crypto products to display. To display top 10 products: `cg_products --top 10 --sortby name`
|
||||
- -s/--sortby - sort by given column. You can sort data by `rank, platform, identifier, supply_rate_percentage, borrow_rate_percentage`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
@@ -207,9 +207,9 @@ usage: cg_indexes [-t --top] [-s --sortby] [--descend]
|
||||
Shows list of crypto indexes from CoinGecko.Each crypto index is made up of a selection of cryptocurrencies, grouped
|
||||
together and weighted by market cap.
|
||||
|
||||
* -t/--top - number of crypto indexes to display. To display top 10 indexes: `cg_indexes --top 10 --sortby name`
|
||||
* -s/--sortby - sort by given column. You can sort data by `rank, name, id, market, last, is_multi_asset_composite`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
- -t/--top - number of crypto indexes to display. To display top 10 indexes: `cg_indexes --top 10 --sortby name`
|
||||
- -s/--sortby - sort by given column. You can sort data by `rank, name, id, market, last, is_multi_asset_composite`
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
@@ -223,28 +223,28 @@ Shows list of crypto derivatives from CoinGecko. Crypto derivatives are secondar
|
||||
derive their value from a primary underlying asset. In this case, the primary asset would be a cryptocurrency such as
|
||||
Bitcoin. The most popular crypto derivatives are crypto futures, crypto options, and perpetual contracts.
|
||||
|
||||
* -t/--top - number of crypto derivatives to display. To display top 10 derivatives: `cg_derivatives --top 10 --sortby symbol`
|
||||
* -s/--sortby - sort by given column. You can sort by
|
||||
- -t/--top - number of crypto derivatives to display. To display top 10 derivatives: `cg_derivatives --top 10 --sortby symbol`
|
||||
- -s/--sortby - sort by given column. You can sort by
|
||||
`rank, market, symbol, price, pct_change_24h, contract_type, basis, spread, funding_rate, volume_24h`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
|
||||

|
||||
|
||||
## cg_categories <a name="cg_categories"></a>
|
||||
|
||||
```text
|
||||
usage: cg_categories [-t --top] [-s --sortby] [--descend] [-l --links]
|
||||
usage: cg_categories [-t --top] [-s --sortby] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Shows top cryptocurrency categories by market capitalization from <https://www.coingecko.com/en/categories>
|
||||
It includes categories like: stablecoins, defi, solana ecosystem, polkadot ecosystem and many others.
|
||||
|
||||
* -t/--top - number of crypto categories to display. To display top 10 crypto categories:
|
||||
- -t/--top - number of crypto categories to display. To display top 10 crypto categories:
|
||||
`cg_categories --top 10 --sortby rank`
|
||||
* -s/--sortby - sort by given column. You can sort by
|
||||
- -s/--sortby - sort by given column. You can sort by
|
||||
`rank, name, change_1h, change_24h, change_7d, market_cap, volume_24h, n_of_coins`
|
||||
* --descend - flag to sort in descending order (lowest first)
|
||||
* -l/--links - flag to show urls. Using this flag will add additional column with urls e.g. `cg_categories --links`.
|
||||
- --descend - flag to sort in descending order (lowest first)
|
||||
- -u/--urls - flag to show urls. Using this flag will add additional column with urls e.g. `cg_categories --links`.
|
||||
Using `links` parameter will display only `rank, name, url` columns
|
||||
|
||||

|
||||
@@ -259,7 +259,7 @@ Shows overview of public companies that holds ethereum or bitcoin
|
||||
Displays most important metrics like: Total Bitcoin/Ethereum Holdings, Total Value (USD), Public Companies
|
||||
Bitcoin/Ethereum Dominance, Companies.
|
||||
|
||||
* -c/--coin - chose a coin. Only available for ethereum or bitcoin. If you want to see overview of public companies
|
||||
- -c/--coin - chose a coin. Only available for ethereum or bitcoin. If you want to see overview of public companies
|
||||
that holds ethereum use `hold --coin ethereum` for bitcoin `hold --coin bitcoin`
|
||||
|
||||

|
||||
@@ -273,9 +273,9 @@ usage: cg_companies [-c --coin] [- --links]
|
||||
Shows Ethereum/Bitcoin Holdings by Public Companies. Track publicly traded companies around the world that are buying
|
||||
ethereum as part of corporate treasury
|
||||
|
||||
* -c/--coin - chose a coin. Only available for ethereum or bitcoin. List of public companies that holds ethereum
|
||||
- -c/--coin - chose a coin. Only available for ethereum or bitcoin. List of public companies that holds ethereum
|
||||
`cg_companies --coin ethereum` for bitcoin `cg_companies --coin bitcoin`
|
||||
* -l/--links - You can use additional flag `--links` to see urls to announcement about buying btc or eth by given
|
||||
- -u/--urls - You can use additional flag `--links` to see urls to announcement about buying btc or eth by given
|
||||
company. In this case of usage `links` only columns `rank, company, url` will be displayed
|
||||
|
||||

|
||||
@@ -300,13 +300,13 @@ usage: cp_info [-t --top] [-s --sort] [--descend]
|
||||
|
||||
Show basic coin information for all coins from CoinPaprika
|
||||
|
||||
* --vs: The currency to look against. Available options are:
|
||||
- --vs: The currency to look against. Available options are:
|
||||
`BTC,ETH,USD,EUR,PLN,KRW,GBP,CAD,JPY,RUB,TRY,NZD,AUD,CHF,UAH,HKD,SGD,NGN,PHP,MXN,BRL,THB,CLP,CNY,CZK,DKK,HUF,IDR,ILS,INR,MYR,NOK,PKR,SEK,TWD,ZAR,VND,BOB,COP,PEN,ARS,ISK`.
|
||||
Default: USD
|
||||
* -t/--top: Number of coins to display. To display top 10 coins: `cp_info --top 10 --sort rank`
|
||||
* -s/--sort: Sort by given column. You can chose on from
|
||||
- -t/--top: Number of coins to display. To display top 10 coins: `cp_info --top 10 --sort rank`
|
||||
- -s/--sort: Sort by given column. You can chose on from
|
||||
`rank, name, symbol, price, volume_24h, circulating_supply, total_supply, max_supply, ath_price,market_cap, beta_value`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
|
||||
## cp_markets <a name="cp_markets"></a>
|
||||
|
||||
@@ -316,13 +316,13 @@ usage: cp_markets [--vs] [-t --top] [-s --sort] [--descend]
|
||||
|
||||
Show market related (price, supply, volume) coin information for all coins on CoinPaprika
|
||||
|
||||
* --vs: The currency to look against. Available options are:
|
||||
- --vs: The currency to look against. Available options are:
|
||||
`BTC,ETH,USD,EUR,PLN,KRW,GBP,CAD,JPY,RUB,TRY,NZD,AUD,CHF,UAH,HKD,SGD,NGN,PHP,MXN,BRL,THB,CLP,CNY,CZK,DKK,HUF,IDR,ILS,INR,MYR,NOK,PKR,SEK,TWD,ZAR,VND,BOB,COP,PEN,ARS,ISK`.
|
||||
Default: USD
|
||||
* -t/--top: Number of coins to display. To display top 10 coins: `cp_markets --top 10 --sort rank`
|
||||
* -s/--sort: Sort by given column. You can chose on from
|
||||
- -t/--top: Number of coins to display. To display top 10 coins: `cp_markets --top 10 --sort rank`
|
||||
- -s/--sort: Sort by given column. You can chose on from
|
||||
`rank, name, symbol, price, volume_24h, mcap_change_24h, pct_change_1h, pct_change_24h, ath_price, pct_from_ath`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
|
||||
## cp_exchanges <a name="cp_exchanges"></a>
|
||||
|
||||
@@ -332,28 +332,28 @@ usage: cp_exchanges [--vs] [-t --top] [-s --sort] [--descend]
|
||||
|
||||
Show all exchanges from CoinPaprika
|
||||
|
||||
* --vs: The currency to look against. Available options are:
|
||||
- --vs: The currency to look against. Available options are:
|
||||
`BTC,ETH,USD,EUR,PLN,KRW,GBP,CAD,JPY,RUB,TRY,NZD,AUD,CHF,UAH,HKD,SGD,NGN,PHP,MXN,BRL,THB,CLP,CNY,CZK,DKK,HUF,IDR,ILS,INR,MYR,NOK,PKR,SEK,TWD,ZAR,VND,BOB,COP,PEN,ARS,ISK`.
|
||||
Default: USD
|
||||
* -t/--top: Number of coins to display. To display top 10 coins: `cp_exchanges --top 10 --sort rank`
|
||||
* -s/--sort: Sort by given column. You can chose on from
|
||||
- -t/--top: Number of coins to display. To display top 10 coins: `cp_exchanges --top 10 --sort rank`
|
||||
- -s/--sort: Sort by given column. You can chose on from
|
||||
`rank, name, currencies, markets, fiats, confidence, volume_24h, volume_7d, volume_30d, sessions_per_month`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
|
||||
## cp_exmarkets <a name="cp_exmarkets"></a>
|
||||
|
||||
```text
|
||||
usage: cp_exmarkets [-e --exchange] [-t --top] [-s --sort] [--descend] [-l --links]
|
||||
usage: cp_exmarkets [-e --exchange] [-t --top] [-s --sort] [--descend] [-u --urls]
|
||||
```
|
||||
|
||||
Get all exchange markets found for given exchange
|
||||
|
||||
* -e/--exchange: Identifier of exchange e.g for Binance Exchange: `binance`
|
||||
* -t/--top: Number of markets to display. To display top 10 markets: `cp_exmarkets --top 10 --sort reported_volume_24h_share`
|
||||
* -s/--sort: Sort by given column. You can chose on from
|
||||
- -e/--exchange: Identifier of exchange e.g for Binance Exchange: `binance`
|
||||
- -t/--top: Number of markets to display. To display top 10 markets: `cp_exmarkets --top 10 --sort reported_volume_24h_share`
|
||||
- -s/--sort: Sort by given column. You can chose on from
|
||||
`pair , base_currency_name, quote_currency_name, category, reported_volume_24h_share, trust_score, market_url`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
* -l/--links: Flag to show urls. Using this flag will add additional column with urls e.g. `cp_exmarkets --links`.
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
- -u/--urls: Flag to show urls. Using this flag will add additional column with urls e.g. `cp_exmarkets --links`.
|
||||
If you will use this flag url column will be displayed.
|
||||
|
||||
## cp_platforms <a name="cp_platforms"></a>
|
||||
@@ -372,7 +372,7 @@ usage: cp_contracts [-p --platform] [-t --top] [-s --sort] [--descend]
|
||||
|
||||
Gets all contract addresses for given blockchain platform
|
||||
|
||||
* -p/--platform: smart contract platform id. Available coins:
|
||||
- -p/--platform: smart contract platform id. Available coins:
|
||||
|
||||
```text
|
||||
btc-bitcoin, eos-eos, eth-ethereum, xrp-xrp, bch-bitcoin-cash, xem-nem, neo-neo, xlm-stellar, etc-ethereum-classic,
|
||||
@@ -382,7 +382,7 @@ ncakeswap, fsxu-flashx-ultra, chik-chickenkebab-finance, jgn-juggernaut7492, crx
|
||||
eubi-eubi-token, swam-swapmatic-token, shells-shells
|
||||
```
|
||||
|
||||
* -t/--top: Number of smart contracts to display. To display 10 smart contracts:
|
||||
- -t/--top: Number of smart contracts to display. To display 10 smart contracts:
|
||||
`cp_contracts -p eth-ethereum --top 10 --sort index`
|
||||
* -s/--sort: Sort by given column. You can chose on from `index, id, type, active, address`.
|
||||
* --descend: Flag to sort in descending order (lowest first)
|
||||
- -s/--sort: Sort by given column. You can chose on from `index, id, type, active, address`.
|
||||
- --descend: Flag to sort in descending order (lowest first)
|
||||
|
||||
@@ -6,6 +6,17 @@ from gamestonk_terminal.cryptocurrency.coinbase_helpers import (
|
||||
make_coinbase_request,
|
||||
)
|
||||
|
||||
PAIRS_FILTERS = [
|
||||
"id",
|
||||
"display_name",
|
||||
"base_currency",
|
||||
"quote_currency",
|
||||
"base_min_size",
|
||||
"base_max_size",
|
||||
"min_market_funds",
|
||||
"max_market_funds",
|
||||
]
|
||||
|
||||
|
||||
def get_trading_pairs() -> pd.DataFrame:
|
||||
"""Get a list of available currency pairs for trading. [Source: Coinbase]
|
||||
|
||||
@@ -7,6 +7,59 @@ import pandas as pd
|
||||
from dateutil import parser
|
||||
from gamestonk_terminal.cryptocurrency.coinpaprika_helpers import PaprikaSession
|
||||
|
||||
MARKETS_FILTERS = [
|
||||
"rank",
|
||||
"name",
|
||||
"symbol",
|
||||
"price",
|
||||
"volume_24h",
|
||||
"mcap_change_24h",
|
||||
"pct_change_1h",
|
||||
"pct_change_24h",
|
||||
"ath_price",
|
||||
"pct_from_ath",
|
||||
]
|
||||
|
||||
EXMARKETS_FILTERS = [
|
||||
"pair",
|
||||
"base_currency_name",
|
||||
"quote_currency_name",
|
||||
"category",
|
||||
"reported_volume_24h_share",
|
||||
"trust_score",
|
||||
"market_url",
|
||||
]
|
||||
|
||||
INFO_FILTERS = [
|
||||
"rank",
|
||||
"name",
|
||||
"symbol",
|
||||
"price",
|
||||
"volume_24h",
|
||||
"circulating_supply",
|
||||
"total_supply",
|
||||
"max_supply",
|
||||
"ath_price",
|
||||
"market_cap",
|
||||
"beta_value",
|
||||
]
|
||||
|
||||
EXCHANGES_FILTERS = [
|
||||
"rank",
|
||||
"name",
|
||||
"currencies",
|
||||
"markets",
|
||||
"fiats",
|
||||
"confidence",
|
||||
"volume_24h",
|
||||
"volume_7d",
|
||||
"volume_30d",
|
||||
"sessions_per_month",
|
||||
]
|
||||
|
||||
|
||||
CONTRACTS_FILTERS = ["id", "type", "active"]
|
||||
|
||||
|
||||
def get_global_market() -> pd.DataFrame:
|
||||
"""Return data frame with most important global crypto statistics like:
|
||||
@@ -313,7 +366,7 @@ def get_contract_platform(platform_id: str = "eth-ethereum") -> pd.DataFrame:
|
||||
Returns
|
||||
-------
|
||||
pandas.DataFrame
|
||||
id, type, active, balance
|
||||
id, type, active
|
||||
"""
|
||||
|
||||
session = PaprikaSession()
|
||||
@@ -321,4 +374,4 @@ def get_contract_platform(platform_id: str = "eth-ethereum") -> pd.DataFrame:
|
||||
session.ENDPOINTS["contract_platform_addresses"].format(platform_id)
|
||||
)
|
||||
|
||||
return pd.DataFrame(contract_platforms)[["id", "type", "active", "balance"]]
|
||||
return pd.DataFrame(contract_platforms)[["id", "type", "active"]]
|
||||
|
||||
@@ -11,6 +11,28 @@ import requests
|
||||
|
||||
import gamestonk_terminal.config_terminal as cfg
|
||||
|
||||
CATEGORIES = ["news", "media"]
|
||||
|
||||
SORT_FILTERS = [
|
||||
"published_at",
|
||||
"domain",
|
||||
"title",
|
||||
"negative_votes",
|
||||
"positive_votes",
|
||||
]
|
||||
|
||||
FILTERS = [
|
||||
"rising",
|
||||
"hot",
|
||||
"bullish",
|
||||
"bearish",
|
||||
"important",
|
||||
"saved",
|
||||
"lol",
|
||||
]
|
||||
|
||||
REGIONS = ["en", "de", "es", "fr", "nl", "it", "pt", "ru"]
|
||||
|
||||
|
||||
class ApiKeyException(Exception):
|
||||
"""Api Key Exception object"""
|
||||
@@ -59,7 +81,7 @@ def make_request(**kwargs: Any) -> dict:
|
||||
]:
|
||||
url += f"&filter={filter_}"
|
||||
|
||||
if region and region in ["en", "de", "es", "fr", "nl", "it", "pt", "ru"]:
|
||||
if region and region in REGIONS:
|
||||
url += f"®ions={region}"
|
||||
|
||||
response = requests.get(url)
|
||||
@@ -124,7 +146,7 @@ def get_news(
|
||||
DataFrame with recent news from different sources filtered by provided parameters.
|
||||
"""
|
||||
|
||||
if post_kind not in ["news", "media"]:
|
||||
if post_kind not in CATEGORIES:
|
||||
post_kind = "news"
|
||||
|
||||
results = []
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -15,8 +15,72 @@ from gamestonk_terminal.cryptocurrency.pycoingecko_helpers import (
|
||||
clean_row,
|
||||
scrape_gecko_data,
|
||||
GECKO_BASE_URL,
|
||||
RetryError,
|
||||
)
|
||||
|
||||
HOLD_COINS = ["ethereum", "bitcoin"]
|
||||
|
||||
NEWS_FILTERS = ["Index", "Title", "Author", "Posted"]
|
||||
|
||||
CATEGORIES_FILTERS = [
|
||||
"Rank",
|
||||
"Name",
|
||||
"Change_1h",
|
||||
"Change_24h",
|
||||
"Change_7d",
|
||||
"Market_Cap",
|
||||
"Volume_24h",
|
||||
"Coins",
|
||||
]
|
||||
|
||||
STABLES_FILTERS = [
|
||||
"Rank",
|
||||
"Name",
|
||||
"Symbol",
|
||||
"Price",
|
||||
"Change_24h",
|
||||
"Exchanges",
|
||||
"Market_Cap",
|
||||
"Change_30d",
|
||||
]
|
||||
|
||||
PRODUCTS_FILTERS = [
|
||||
"Rank",
|
||||
"Platform",
|
||||
"Identifier",
|
||||
"Supply_Rate",
|
||||
"Borrow_Rate",
|
||||
]
|
||||
|
||||
PLATFORMS_FILTERS = ["Rank", "Name", "Category", "Centralized"]
|
||||
|
||||
EXCHANGES_FILTERS = [
|
||||
"Rank",
|
||||
"Trust_Score",
|
||||
"Id",
|
||||
"Name",
|
||||
"Country",
|
||||
"Year Established",
|
||||
"Trade_Volume_24h_BTC",
|
||||
]
|
||||
|
||||
EXRATES_FILTERS = ["Index", "Name", "Unit", "Value", "Type"]
|
||||
|
||||
INDEXES_FILTERS = ["Rank", "Name", "Id", "Market", "Last", "MultiAsset"]
|
||||
|
||||
DERIVATIVES_FILTERS = [
|
||||
"Rank",
|
||||
"Market",
|
||||
"Symbol",
|
||||
"Price",
|
||||
"Pct_Change_24h",
|
||||
"Contract_Type",
|
||||
"Basis",
|
||||
"Spread",
|
||||
"Funding_Rate",
|
||||
"Volume_24h",
|
||||
]
|
||||
|
||||
|
||||
def get_holdings_overview(endpoint: str = "bitcoin") -> pd.DataFrame:
|
||||
"""Scrapes overview of public companies that holds ethereum or bitcoin
|
||||
@@ -34,9 +98,12 @@ def get_holdings_overview(endpoint: str = "bitcoin") -> pd.DataFrame:
|
||||
"""
|
||||
|
||||
url = f"https://www.coingecko.com/en/public-companies-{endpoint}"
|
||||
rows = scrape_gecko_data(url).find_all(
|
||||
"span", class_="overview-box d-inline-block p-3 mr-2"
|
||||
)
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find_all("span", class_="overview-box d-inline-block p-3 mr-2")
|
||||
kpis = {}
|
||||
for row in rows:
|
||||
row_cleaned = clean_row(row)
|
||||
@@ -69,7 +136,12 @@ def get_companies_assets(endpoint: str = "bitcoin") -> pd.DataFrame:
|
||||
"""
|
||||
|
||||
url = f"https://www.coingecko.com/en/public-companies-{endpoint}"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
link = row.find("a")["href"]
|
||||
@@ -110,7 +182,12 @@ def get_news(n: int = 100) -> pd.DataFrame:
|
||||
dfs = []
|
||||
for page in range(1, n_of_pages):
|
||||
url = f"https://www.coingecko.com/en/news?page={page}"
|
||||
rows = scrape_gecko_data(url).find_all("article")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find_all("article")
|
||||
results = []
|
||||
for row in rows:
|
||||
header = row.find("header")
|
||||
@@ -162,7 +239,12 @@ def get_top_crypto_categories() -> pd.DataFrame:
|
||||
"Url",
|
||||
]
|
||||
url = "https://www.coingecko.com/en/categories"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
url = GECKO_BASE_URL + row.find("a")["href"]
|
||||
@@ -216,7 +298,12 @@ def get_stable_coins() -> pd.DataFrame:
|
||||
"Url",
|
||||
]
|
||||
url = "https://www.coingecko.com/en/stablecoins"
|
||||
rows = scrape_gecko_data(url).find("tbody").find_all("tr")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find("tbody").find_all("tr")
|
||||
results = []
|
||||
for row in rows:
|
||||
link = GECKO_BASE_URL + row.find("a")["href"]
|
||||
@@ -265,8 +352,12 @@ def get_nft_of_the_day() -> pd.DataFrame:
|
||||
"""
|
||||
|
||||
url = "https://www.coingecko.com/en/nft"
|
||||
soup = scrape_gecko_data(url)
|
||||
row = soup.find("div", class_="tw-px-4 tw-py-5 sm:tw-p-6")
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
row = scraped_data.find("div", class_="tw-px-4 tw-py-5 sm:tw-p-6")
|
||||
try:
|
||||
*author, description, _ = clean_row(row)
|
||||
if len(author) > 3:
|
||||
@@ -303,9 +394,12 @@ def get_nft_market_status() -> pd.DataFrame:
|
||||
"""
|
||||
|
||||
url = "https://www.coingecko.com/en/nft"
|
||||
rows = scrape_gecko_data(url).find_all(
|
||||
"span", class_="overview-box d-inline-block p-3 mr-2"
|
||||
)
|
||||
try:
|
||||
scraped_data = scrape_gecko_data(url)
|
||||
except RetryError as e:
|
||||
print(e)
|
||||
return pd.DataFrame()
|
||||
rows = scraped_data.find_all("span", class_="overview-box d-inline-block p-3 mr-2")
|
||||
kpis = {}
|
||||
for row in rows:
|
||||
value, *kpi = clean_row(row)
|
||||
|
||||
@@ -143,27 +143,31 @@ def display_nft_market_status(export: str) -> None:
|
||||
"""
|
||||
|
||||
df = gecko.get_nft_market_status()
|
||||
if not df.empty:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"nft",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"nft",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_exchange_rates(sortby: str, descend: bool, top: int, export: str) -> None:
|
||||
@@ -183,26 +187,31 @@ def display_exchange_rates(sortby: str, descend: bool, top: int, export: str) ->
|
||||
|
||||
df = gecko.get_exchange_rates().sort_values(by=sortby, ascending=descend)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if not df.empty:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"exrates",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"exrates",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_global_market_info(export: str) -> None:
|
||||
@@ -221,26 +230,31 @@ def display_global_market_info(export: str) -> None:
|
||||
|
||||
df = gecko.get_global_info()
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if not df.empty:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"global",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"global",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_global_defi_info(export: str) -> None:
|
||||
@@ -254,26 +268,31 @@ def display_global_defi_info(export: str) -> None:
|
||||
|
||||
df = gecko.get_global_defi_info()
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
floatfmt=".1f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if not df.empty:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
floatfmt=".1f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"defi",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"defi",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_stablecoins(
|
||||
@@ -295,35 +314,41 @@ def display_stablecoins(
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = gecko.get_stable_coins().sort_values(by=sortby, ascending=descend)
|
||||
df = gecko.get_stable_coins()
|
||||
|
||||
df_data = df.copy()
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
df_data = df.copy()
|
||||
|
||||
if links is True:
|
||||
df = df[["Rank", "Name", "Symbol", "Url"]]
|
||||
else:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
if links is True:
|
||||
df = df[["Rank", "Name", "Symbol", "Url"]]
|
||||
else:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".0f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".0f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"stables",
|
||||
df_data,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"stables",
|
||||
df_data,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_news(
|
||||
@@ -345,39 +370,45 @@ def display_news(
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = gecko.get_news(n=top).sort_values(by=sortby, ascending=descend)
|
||||
df = gecko.get_news(n=top)
|
||||
|
||||
df_data = df.copy()
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
df_data = df.copy()
|
||||
|
||||
df["Title"] = df["Title"].apply(
|
||||
lambda x: "\n".join(textwrap.wrap(x, width=65)) if isinstance(x, str) else x
|
||||
)
|
||||
df["Title"] = df["Title"].apply(
|
||||
lambda x: "\n".join(textwrap.wrap(x, width=65)) if isinstance(x, str) else x
|
||||
)
|
||||
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
else:
|
||||
df = df[["Index", "Url"]]
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
else:
|
||||
df = df[["Index", "Url"]]
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
floatfmt=".0f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
floatfmt=".0f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"news",
|
||||
df_data,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"news",
|
||||
df_data,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_categories(
|
||||
@@ -401,35 +432,41 @@ def display_categories(
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = gecko.get_top_crypto_categories().sort_values(by=sortby, ascending=descend)
|
||||
df = gecko.get_top_crypto_categories()
|
||||
|
||||
df_data = df.copy()
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
df_data = df.copy()
|
||||
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
else:
|
||||
df = df[["Rank", "Name", "Url"]]
|
||||
if not links:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
else:
|
||||
df = df[["Rank", "Name", "Url"]]
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".0f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".0f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"categories",
|
||||
df_data,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"categories",
|
||||
df_data,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_exchanges(
|
||||
@@ -451,33 +488,40 @@ def display_exchanges(
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = gecko.get_exchanges().sort_values(by=sortby, ascending=descend)
|
||||
df = gecko.get_exchanges()
|
||||
|
||||
if links is True:
|
||||
df = df[["Rank", "Name", "Url"]]
|
||||
else:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".1f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if links is True:
|
||||
df = df[["Rank", "Name", "Url"]]
|
||||
else:
|
||||
df.drop("Url", axis=1, inplace=True)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".1f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"exchanges",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"exchanges",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_platforms(sortby: str, descend: bool, top: int, export: str) -> None:
|
||||
@@ -495,28 +539,35 @@ def display_platforms(sortby: str, descend: bool, top: int, export: str) -> None
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = gecko.get_financial_platforms().sort_values(by=sortby, ascending=descend)
|
||||
df = gecko.get_financial_platforms()
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"platforms",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"platforms",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_products(sortby: str, descend: bool, top: int, export: str) -> None:
|
||||
@@ -534,28 +585,35 @@ def display_products(sortby: str, descend: bool, top: int, export: str) -> None:
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = gecko.get_finance_products().sort_values(by=sortby, ascending=descend)
|
||||
df = gecko.get_finance_products()
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"products",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"products",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_indexes(sortby: str, descend: bool, top: int, export: str) -> None:
|
||||
@@ -573,28 +631,34 @@ def display_indexes(sortby: str, descend: bool, top: int, export: str) -> None:
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = gecko.get_indexes().sort_values(by=sortby, ascending=descend)
|
||||
df = gecko.get_indexes()
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"indexes",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"indexes",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
|
||||
def display_derivatives(sortby: str, descend: bool, top: int, export: str) -> None:
|
||||
@@ -612,25 +676,32 @@ def display_derivatives(sortby: str, descend: bool, top: int, export: str) -> No
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
|
||||
df = gecko.get_derivatives().sort_values(by=sortby, ascending=descend)
|
||||
df = gecko.get_derivatives()
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
if not df.empty:
|
||||
df = df.sort_values(by=sortby, ascending=descend)
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(top),
|
||||
headers=df.columns,
|
||||
floatfmt=".4f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"derivatives",
|
||||
df,
|
||||
)
|
||||
else:
|
||||
print(df.to_string, "\n")
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"derivatives",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
print("Unable to retrieve data from CoinGecko.")
|
||||
print("")
|
||||
|
||||
@@ -3,11 +3,10 @@ __docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
import matplotlib.pyplot as plt
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
@@ -23,9 +22,12 @@ from gamestonk_terminal.helper_funcs import (
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.common.prediction_techniques import (
|
||||
arima_model,
|
||||
arima_view,
|
||||
ets_model,
|
||||
ets_view,
|
||||
knn_view,
|
||||
mc_model,
|
||||
neural_networks_view,
|
||||
regression_view,
|
||||
pred_helper,
|
||||
@@ -38,9 +40,23 @@ class PredictionTechniquesController:
|
||||
"""Prediction Techniques Controller class"""
|
||||
|
||||
# Command choices
|
||||
CHOICES = ["cls", "?", "help", "q", "quit", "load", "pick"]
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_MODELS = [
|
||||
CHOICES_COMMANDS = [
|
||||
"pick",
|
||||
"load",
|
||||
"ets",
|
||||
"knn",
|
||||
"regression",
|
||||
@@ -51,15 +67,21 @@ class PredictionTechniquesController:
|
||||
"conv1d",
|
||||
"mc",
|
||||
]
|
||||
CHOICES += CHOICES_MODELS
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
sampling_map = {"H": "Hour", "D": "Day"}
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
coin: str,
|
||||
data: pd.DataFrame,
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Constructor"""
|
||||
self.pred_parser = argparse.ArgumentParser(add_help=False, prog="pred")
|
||||
self.pred_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
data["Returns"] = data["Close"].pct_change()
|
||||
data["LogRet"] = np.log(data["Close"]) - np.log(data["Close"].shift(1))
|
||||
data = data.dropna()
|
||||
@@ -68,28 +90,35 @@ class PredictionTechniquesController:
|
||||
self.coin = coin
|
||||
self.resolution = "1D"
|
||||
self.target = "Close"
|
||||
self.pred_parser = argparse.ArgumentParser(add_help=False, prog="pred")
|
||||
self.pred_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["load"]["-r"] = {c: {} for c in c_help.INTERVALS}
|
||||
choices["pick"] = {c: {} for c in self.data.columns}
|
||||
choices["ets"]["-t"] = {c: {} for c in ets_model.TRENDS}
|
||||
choices["ets"]["-s"] = {c: {} for c in ets_model.SEASONS}
|
||||
choices["arima"]["-i"] = {c: {} for c in arima_model.ICS}
|
||||
choices["mc"]["--dist"] = {c: {} for c in mc_model.DISTRIBUTIONS}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
|
||||
help_string = f"""
|
||||
Prediction Techniques:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
Prediction Techniques Menu:
|
||||
|
||||
load load new ticker
|
||||
pick pick new target variable
|
||||
|
||||
Coin Loaded: {self.coin}
|
||||
Target Column: {self.target}
|
||||
|
||||
|
||||
Models:
|
||||
ets exponential smoothing (e.g. Holt-Winters)
|
||||
knn k-Nearest Neighbors
|
||||
@@ -106,30 +135,48 @@ Models:
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Parameters
|
||||
-------
|
||||
an_input : str
|
||||
string with input arguments
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.pred_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -137,17 +184,50 @@ Models:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "pred")
|
||||
self.queue.insert(0, "crypto")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "reset", "crypto", "pred"]
|
||||
|
||||
@try_except
|
||||
def call_load(self, other_args: List[str]):
|
||||
@@ -189,24 +269,23 @@ Models:
|
||||
type=str,
|
||||
dest="resolution",
|
||||
help="How often to resample data.",
|
||||
choices=["1H", "3H", "6H", "1D"],
|
||||
choices=c_help.INTERVALS,
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-c")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
# TODO: improve loading from this submenu. Currently would recommend using this menu after using main load
|
||||
|
||||
self.coin = ns_parser.coin
|
||||
self.data = c_help.load_cg_coin_data(
|
||||
ns_parser.coin, ns_parser.currency, ns_parser.days, ns_parser.resolution
|
||||
)
|
||||
res = ns_parser.resolution if ns_parser.days < 90 else "1D"
|
||||
self.resolution = res
|
||||
print(
|
||||
f"{ns_parser.days} Days of {self.coin} vs {ns_parser.currency} loaded with {res} resolution.\n"
|
||||
)
|
||||
if ns_parser:
|
||||
self.coin = ns_parser.coin
|
||||
self.data = c_help.load_cg_coin_data(
|
||||
ns_parser.coin, ns_parser.currency, ns_parser.days, ns_parser.resolution
|
||||
)
|
||||
res = ns_parser.resolution if ns_parser.days < 90 else "1D"
|
||||
self.resolution = res
|
||||
print(
|
||||
f"{ns_parser.days} Days of {self.coin} vs {ns_parser.currency} loaded with {res} resolution.\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_pick(self, other_args: List[str]):
|
||||
@@ -230,10 +309,10 @@ Models:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
self.target = ns_parser.target
|
||||
print("")
|
||||
if ns_parser:
|
||||
self.target = ns_parser.target
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ets(self, other_args: List[str]):
|
||||
@@ -272,7 +351,7 @@ Models:
|
||||
"--trend",
|
||||
action="store",
|
||||
dest="trend",
|
||||
choices=["N", "A", "Ad"],
|
||||
choices=ets_model.TRENDS,
|
||||
default="N",
|
||||
help="Trend component: N: None, A: Additive, Ad: Additive Damped.",
|
||||
)
|
||||
@@ -281,7 +360,7 @@ Models:
|
||||
"--seasonal",
|
||||
action="store",
|
||||
dest="seasonal",
|
||||
choices=["N", "A", "M"],
|
||||
choices=ets_model.SEASONS,
|
||||
default="N",
|
||||
help="Seasonality component: N: None, A: Additive, M: Multiplicative.",
|
||||
)
|
||||
@@ -306,37 +385,35 @@ Models:
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if ns_parser.s_end_date:
|
||||
if ns_parser.s_end_date < self.data.index[0]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is older than Start Date of historical data\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
if ns_parser.s_end_date:
|
||||
if ns_parser.s_end_date < get_next_stock_market_days(
|
||||
last_stock_day=self.data.index[0],
|
||||
n_next_days=5 + ns_parser.n_days,
|
||||
)[-1]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is too close to Start Date to train model\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
if ns_parser.s_end_date < self.data.index[0]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is older than Start Date of historical data\n"
|
||||
)
|
||||
return
|
||||
|
||||
if ns_parser.s_end_date < get_next_stock_market_days(
|
||||
last_stock_day=self.data.index[0],
|
||||
n_next_days=5 + ns_parser.n_days,
|
||||
)[-1]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is too close to Start Date to train model\n"
|
||||
)
|
||||
return
|
||||
|
||||
ets_view.display_exponential_smoothing(
|
||||
ticker=self.coin,
|
||||
values=self.data[self.target],
|
||||
n_predict=ns_parser.n_days,
|
||||
trend=ns_parser.trend,
|
||||
seasonal=ns_parser.seasonal,
|
||||
seasonal_periods=ns_parser.seasonal_periods,
|
||||
s_end_date=ns_parser.s_end_date,
|
||||
export=ns_parser.export,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
ets_view.display_exponential_smoothing(
|
||||
ticker=self.coin,
|
||||
values=self.data[self.target],
|
||||
n_predict=ns_parser.n_days,
|
||||
trend=ns_parser.trend,
|
||||
seasonal=ns_parser.seasonal,
|
||||
seasonal_periods=ns_parser.seasonal_periods,
|
||||
s_end_date=ns_parser.s_end_date,
|
||||
export=ns_parser.export,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_knn(self, other_args: List[str]):
|
||||
@@ -411,21 +488,22 @@ Models:
|
||||
default=True,
|
||||
help="Specify if shuffling validation inputs.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
knn_view.display_k_nearest_neighbors(
|
||||
ticker=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_neighbors=ns_parser.n_neighbors,
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
test_size=ns_parser.valid_split,
|
||||
end_date=ns_parser.s_end_date,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_FIGURES_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
knn_view.display_k_nearest_neighbors(
|
||||
ticker=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_neighbors=ns_parser.n_neighbors,
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
test_size=ns_parser.valid_split,
|
||||
end_date=ns_parser.s_end_date,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_regression(self, other_args: List[str]):
|
||||
@@ -495,36 +573,36 @@ Models:
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
# BACKTESTING CHECK
|
||||
if ns_parser.s_end_date:
|
||||
if ns_parser.s_end_date < self.data.index[0]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is older than Start Date of historical data\n"
|
||||
)
|
||||
return
|
||||
if ns_parser:
|
||||
# BACKTESTING CHECK
|
||||
if ns_parser.s_end_date:
|
||||
if ns_parser.s_end_date < self.data.index[0]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is older than Start Date of historical data\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
if ns_parser.s_end_date < get_next_stock_market_days(
|
||||
last_stock_day=self.data.index[0],
|
||||
n_next_days=5 + ns_parser.n_days,
|
||||
)[-1]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is too close to Start Date to train model\n"
|
||||
)
|
||||
return
|
||||
if ns_parser.s_end_date < get_next_stock_market_days(
|
||||
last_stock_day=self.data.index[0],
|
||||
n_next_days=5 + ns_parser.n_days,
|
||||
)[-1]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is too close to Start Date to train model\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
regression_view.display_regression(
|
||||
dataset=self.coin,
|
||||
values=self.data[self.target],
|
||||
poly_order=ns_parser.n_polynomial,
|
||||
n_input=ns_parser.n_inputs,
|
||||
n_predict=ns_parser.n_days,
|
||||
n_jumps=ns_parser.n_jumps,
|
||||
s_end_date=ns_parser.s_end_date,
|
||||
export=ns_parser.export,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
regression_view.display_regression(
|
||||
dataset=self.coin,
|
||||
values=self.data[self.target],
|
||||
poly_order=ns_parser.n_polynomial,
|
||||
n_input=ns_parser.n_inputs,
|
||||
n_predict=ns_parser.n_days,
|
||||
n_jumps=ns_parser.n_jumps,
|
||||
s_end_date=ns_parser.s_end_date,
|
||||
export=ns_parser.export,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_arima(self, other_args: List[str]):
|
||||
@@ -560,7 +638,7 @@ Models:
|
||||
dest="s_ic",
|
||||
type=str,
|
||||
default="aic",
|
||||
choices=["aic", "aicc", "bic", "hqic", "oob"],
|
||||
choices=arima_model.ICS,
|
||||
help="information criteria.",
|
||||
)
|
||||
parser.add_argument(
|
||||
@@ -600,39 +678,37 @@ Models:
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
# BACKTESTING CHECK
|
||||
if ns_parser.s_end_date:
|
||||
if ns_parser.s_end_date < self.data.index[0]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is older than Start Date of historical data\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
# BACKTESTING CHECK
|
||||
if ns_parser.s_end_date:
|
||||
if ns_parser.s_end_date < get_next_stock_market_days(
|
||||
last_stock_day=self.data.index[0],
|
||||
n_next_days=5 + ns_parser.n_days,
|
||||
)[-1]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is too close to Start Date to train model\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
if ns_parser.s_end_date < self.data.index[0]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is older than Start Date of historical data\n"
|
||||
)
|
||||
return
|
||||
|
||||
if ns_parser.s_end_date < get_next_stock_market_days(
|
||||
last_stock_day=self.data.index[0],
|
||||
n_next_days=5 + ns_parser.n_days,
|
||||
)[-1]:
|
||||
print(
|
||||
"Backtesting not allowed, since End Date is too close to Start Date to train model\n"
|
||||
)
|
||||
return
|
||||
|
||||
arima_view.display_arima(
|
||||
dataset=self.coin,
|
||||
values=self.data[self.target],
|
||||
arima_order=ns_parser.s_order,
|
||||
n_predict=ns_parser.n_days,
|
||||
seasonal=ns_parser.b_seasonal,
|
||||
ic=ns_parser.s_ic,
|
||||
results=ns_parser.b_results,
|
||||
s_end_date=ns_parser.s_end_date,
|
||||
export=ns_parser.export,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
arima_view.display_arima(
|
||||
dataset=self.coin,
|
||||
values=self.data[self.target],
|
||||
arima_order=ns_parser.s_order,
|
||||
n_predict=ns_parser.n_days,
|
||||
seasonal=ns_parser.b_seasonal,
|
||||
ic=ns_parser.s_ic,
|
||||
results=ns_parser.b_results,
|
||||
s_end_date=ns_parser.s_end_date,
|
||||
export=ns_parser.export,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_mlp(self, other_args: List[str]):
|
||||
@@ -643,27 +719,27 @@ Models:
|
||||
description="""Multi-Layered-Perceptron. """,
|
||||
other_args=other_args,
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
neural_networks_view.display_mlp(
|
||||
dataset=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
learning_rate=ns_parser.lr,
|
||||
epochs=ns_parser.n_epochs,
|
||||
batch_size=ns_parser.n_batch_size,
|
||||
test_size=ns_parser.valid_split,
|
||||
n_loops=ns_parser.n_loops,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
if ns_parser:
|
||||
neural_networks_view.display_mlp(
|
||||
dataset=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
learning_rate=ns_parser.lr,
|
||||
epochs=ns_parser.n_epochs,
|
||||
batch_size=ns_parser.n_batch_size,
|
||||
test_size=ns_parser.valid_split,
|
||||
n_loops=ns_parser.n_loops,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return self.queue
|
||||
|
||||
finally:
|
||||
pred_helper.restore_env()
|
||||
return self.queue
|
||||
|
||||
def call_rnn(self, other_args: List[str]):
|
||||
"""Process rnn command"""
|
||||
@@ -673,28 +749,27 @@ Models:
|
||||
description="""Recurrent Neural Network. """,
|
||||
other_args=other_args,
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
neural_networks_view.display_rnn(
|
||||
dataset=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
learning_rate=ns_parser.lr,
|
||||
epochs=ns_parser.n_epochs,
|
||||
batch_size=ns_parser.n_batch_size,
|
||||
test_size=ns_parser.valid_split,
|
||||
n_loops=ns_parser.n_loops,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
if ns_parser:
|
||||
neural_networks_view.display_rnn(
|
||||
dataset=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
learning_rate=ns_parser.lr,
|
||||
epochs=ns_parser.n_epochs,
|
||||
batch_size=ns_parser.n_batch_size,
|
||||
test_size=ns_parser.valid_split,
|
||||
n_loops=ns_parser.n_loops,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
return self.queue
|
||||
finally:
|
||||
pred_helper.restore_env()
|
||||
return self.queue
|
||||
|
||||
def call_lstm(self, other_args: List[str]):
|
||||
"""Process lstm command"""
|
||||
@@ -704,27 +779,27 @@ Models:
|
||||
description="""Long-Short Term Memory. """,
|
||||
other_args=other_args,
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
neural_networks_view.display_lstm(
|
||||
dataset=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
learning_rate=ns_parser.lr,
|
||||
epochs=ns_parser.n_epochs,
|
||||
batch_size=ns_parser.n_batch_size,
|
||||
test_size=ns_parser.valid_split,
|
||||
n_loops=ns_parser.n_loops,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
if ns_parser:
|
||||
neural_networks_view.display_lstm(
|
||||
dataset=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
learning_rate=ns_parser.lr,
|
||||
epochs=ns_parser.n_epochs,
|
||||
batch_size=ns_parser.n_batch_size,
|
||||
test_size=ns_parser.valid_split,
|
||||
n_loops=ns_parser.n_loops,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
return self.queue
|
||||
finally:
|
||||
pred_helper.restore_env()
|
||||
return self.queue
|
||||
|
||||
def call_conv1d(self, other_args: List[str]):
|
||||
"""Process conv1d command"""
|
||||
@@ -734,28 +809,28 @@ Models:
|
||||
description="""1D CNN.""",
|
||||
other_args=other_args,
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
neural_networks_view.display_conv1d(
|
||||
dataset=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
learning_rate=ns_parser.lr,
|
||||
epochs=ns_parser.n_epochs,
|
||||
batch_size=ns_parser.n_batch_size,
|
||||
test_size=ns_parser.valid_split,
|
||||
n_loops=ns_parser.n_loops,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
if ns_parser:
|
||||
neural_networks_view.display_conv1d(
|
||||
dataset=self.coin,
|
||||
data=self.data[self.target],
|
||||
n_input_days=ns_parser.n_inputs,
|
||||
n_predict_days=ns_parser.n_days,
|
||||
learning_rate=ns_parser.lr,
|
||||
epochs=ns_parser.n_epochs,
|
||||
batch_size=ns_parser.n_batch_size,
|
||||
test_size=ns_parser.valid_split,
|
||||
n_loops=ns_parser.n_loops,
|
||||
no_shuffle=ns_parser.no_shuffle,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
return self.queue
|
||||
|
||||
finally:
|
||||
pred_helper.restore_env()
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_mc(self, other_args: List[str]):
|
||||
@@ -785,7 +860,7 @@ Models:
|
||||
)
|
||||
parser.add_argument(
|
||||
"--dist",
|
||||
choices=["normal", "lognormal"],
|
||||
choices=mc_model.DISTRIBUTIONS,
|
||||
default="lognormal",
|
||||
dest="dist",
|
||||
help="Whether to model returns or log returns",
|
||||
@@ -794,55 +869,94 @@ Models:
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_FIGURES_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
if self.target != "Close":
|
||||
print("MC Prediction designed for AdjClose prices")
|
||||
return
|
||||
print("MC Prediction designed for AdjClose prices\n")
|
||||
return self.queue
|
||||
|
||||
mc_view.display_mc_forecast(
|
||||
data=self.data[self.target],
|
||||
n_future=ns_parser.n_days,
|
||||
n_sims=ns_parser.n_sims,
|
||||
use_log=ns_parser.dist == "lognormal",
|
||||
export=ns_parser.export,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
if ns_parser:
|
||||
mc_view.display_mc_forecast(
|
||||
data=self.data[self.target],
|
||||
n_future=ns_parser.n_days,
|
||||
n_sims=ns_parser.n_sims,
|
||||
use_log=ns_parser.dist == "lognormal",
|
||||
export=ns_parser.export,
|
||||
time_res=self.resolution,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(coin: str, data: pd.DataFrame):
|
||||
def menu(coin: str, data: pd.DataFrame, queue: List[str] = None):
|
||||
"""Prediction Techniques Menu"""
|
||||
|
||||
pred_controller = PredictionTechniquesController(coin, data)
|
||||
pred_controller.call_help(None)
|
||||
pred_controller = PredictionTechniquesController(coin, data, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if pred_controller.queue and len(pred_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if pred_controller.queue[0] in ("q", "..", "quit"):
|
||||
if len(pred_controller.queue) > 1:
|
||||
return pred_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = pred_controller.queue[0]
|
||||
pred_controller.queue = pred_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in pred_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /crypto/pred/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in pred_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (crypto)>(pred)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (crypto)>(pred)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
pred_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and pred_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /crypto/pred/ $ ",
|
||||
completer=pred_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /crypto/pred/ $ ")
|
||||
|
||||
try:
|
||||
plt.close("all")
|
||||
|
||||
process_input = pred_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
pred_controller.queue = pred_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, pred_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
pred_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
else:
|
||||
candidate_input = similar_cmd[0]
|
||||
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
pred_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
pred_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
an_input = ""
|
||||
pred_controller.queue = []
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -3,7 +3,7 @@ __docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
from typing import List
|
||||
from typing import List, Union, Dict
|
||||
|
||||
from colorama import Style
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
@@ -23,34 +23,55 @@ from gamestonk_terminal.menu import session
|
||||
|
||||
|
||||
class FredController:
|
||||
CHOICES = ["cls", "?", "help", "q", "quit"]
|
||||
"""FRED Controller Class"""
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"cd",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
"home",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS = ["search", "add", "rmv", "plot"]
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Constructor"""
|
||||
self.fred_parser = argparse.ArgumentParser(add_help=False, prog="fred")
|
||||
self.fred_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.current_series = dict()
|
||||
self.current_series: Dict = dict()
|
||||
self.current_long_id = 0
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
id_string = ""
|
||||
for s_id, sub_dict in self.current_series.items():
|
||||
id_string += f" {s_id.upper()}{(self.current_long_id-len(s_id)) * ' '} : {sub_dict['title']}\n"
|
||||
help_text = f"""
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
|
||||
search search FRED series notes
|
||||
add add series ID to list
|
||||
rmv remove series ID from list
|
||||
@@ -66,28 +87,40 @@ Current Series IDs:
|
||||
|
||||
Returns
|
||||
-------
|
||||
MENU_GO_BACK, MENU_QUIT, MENU_RESET
|
||||
MENU_GO_BACK - Show main context menu again
|
||||
MENU_QUIT - Quit terminal
|
||||
MENU_RESET - Reset terminal and go back to same previous menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.fred_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -95,17 +128,49 @@ Current Series IDs:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - exit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "fred")
|
||||
self.queue.insert(0, "economy")
|
||||
self.queue.insert(0, "r")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "r", "economy", "fred"]
|
||||
|
||||
@try_except
|
||||
def call_search(self, other_args: List[str]):
|
||||
@@ -134,17 +199,17 @@ Current Series IDs:
|
||||
default=5,
|
||||
help="Maximum number of series notes to display.",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-s")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
|
||||
fred_view.notes(
|
||||
series_term=ns_parser.series_term,
|
||||
num=ns_parser.num,
|
||||
)
|
||||
fred_view.notes(
|
||||
series_term=ns_parser.series_term,
|
||||
num=ns_parser.num,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_add(self, other_args: List[str]):
|
||||
@@ -162,25 +227,24 @@ Current Series IDs:
|
||||
type=str,
|
||||
help="FRED Series from https://fred.stlouisfed.org. For multiple series use: series1,series2,series3",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-i")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
# Loop through entries. If it exists, save title in dictionary
|
||||
for s_id in ns_parser.series_id.split(","):
|
||||
exists, information = fred_model.check_series_id(s_id)
|
||||
if exists:
|
||||
self.current_series[s_id] = {
|
||||
"title": information["seriess"][0]["title"],
|
||||
"units": information["seriess"][0]["units_short"],
|
||||
}
|
||||
self.current_long_id = max(self.current_long_id, len(s_id))
|
||||
|
||||
# Loop through entries. If it exists, save title in dictionary
|
||||
for s_id in ns_parser.series_id.split(","):
|
||||
exists, information = fred_model.check_series_id(s_id)
|
||||
if exists:
|
||||
self.current_series[s_id] = {
|
||||
"title": information["seriess"][0]["title"],
|
||||
"units": information["seriess"][0]["units_short"],
|
||||
}
|
||||
self.current_long_id = max(self.current_long_id, len(s_id))
|
||||
|
||||
print(
|
||||
f"Current Series: {', '.join(self.current_series.keys()) .upper() or None}\n"
|
||||
)
|
||||
print(
|
||||
f"Current Series: {', '.join(self.current_series.keys()) .upper() or None}\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_rmv(self, other_args: List[str]):
|
||||
@@ -220,17 +284,17 @@ Current Series IDs:
|
||||
):
|
||||
other_args.insert(0, "-i")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if ns_parser.all:
|
||||
self.current_series = {}
|
||||
self.current_long_id = 0
|
||||
print("")
|
||||
return
|
||||
|
||||
self.current_series.pop(ns_parser.series_id)
|
||||
print(f"Current Series Ids: {', '.join(self.current_series.keys()) or None}\n")
|
||||
if ns_parser:
|
||||
if ns_parser.all:
|
||||
self.current_series = {}
|
||||
self.current_long_id = 0
|
||||
print("")
|
||||
return self.queue
|
||||
self.current_series.pop(ns_parser.series_id)
|
||||
print(
|
||||
f"Current Series Ids: {', '.join(self.current_series.keys()) or None}\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_plot(self, other_args):
|
||||
@@ -258,46 +322,87 @@ Current Series IDs:
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
fred_view.display_fred_series(
|
||||
self.current_series, ns_parser.start_date, ns_parser.raw, ns_parser.export
|
||||
)
|
||||
if ns_parser:
|
||||
fred_view.display_fred_series(
|
||||
self.current_series,
|
||||
ns_parser.start_date,
|
||||
ns_parser.raw,
|
||||
ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
def menu(queue: List[str] = None):
|
||||
"""Fred Menu"""
|
||||
fred_controller = FredController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
fred_controller = FredController()
|
||||
fred_controller.print_help()
|
||||
|
||||
# Loop forever and ever
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if fred_controller.queue and len(fred_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if fred_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(fred_controller.queue) > 1:
|
||||
return fred_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = fred_controller.queue[0]
|
||||
fred_controller.queue = fred_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in fred_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /economy/fred/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in fred_controller.CHOICES}
|
||||
)
|
||||
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (economy)(fred)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (economy)(fred)> ")
|
||||
try:
|
||||
process_input = fred_controller.switch(an_input)
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
fred_controller.print_help()
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and fred_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /economy/fred/ $ ",
|
||||
completer=fred_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /economy/fred/ $ ")
|
||||
|
||||
try:
|
||||
# Process the input command
|
||||
fred_controller.queue = fred_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, fred_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /economy/fred menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
fred_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
fred_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
fred_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -5,9 +5,8 @@ import argparse
|
||||
import difflib
|
||||
import os
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from thepassiveinvestor import create_ETF_report
|
||||
|
||||
@@ -19,11 +18,9 @@ from gamestonk_terminal.etf import (
|
||||
financedatabase_view,
|
||||
)
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||||
get_flair,
|
||||
parse_known_args_and_warn,
|
||||
MENU_GO_BACK,
|
||||
MENU_QUIT,
|
||||
MENU_RESET,
|
||||
try_except,
|
||||
system_clear,
|
||||
)
|
||||
@@ -35,10 +32,15 @@ class ETFController:
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
@@ -56,22 +58,33 @@ class ETFController:
|
||||
]
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
preset_options = [
|
||||
file.strip(".ini")
|
||||
for file in os.listdir(
|
||||
os.path.join(os.path.abspath(os.path.dirname(__file__)), "presets/")
|
||||
)
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Constructor"""
|
||||
self.etf_parser = argparse.ArgumentParser(add_help=False, prog="etf")
|
||||
self.etf_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["screener"]["--preset"] = {c: None for c in self.preset_options}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_str = """
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
reset reset terminal and reload configs
|
||||
|
||||
StockAnalysis.com:
|
||||
search search ETFs matching name (i.e. BlackRock or Invesco)
|
||||
overview get overview of ETF symbol
|
||||
@@ -94,28 +107,40 @@ Finance Database:
|
||||
|
||||
Returns
|
||||
-------
|
||||
MENU_GO_BACK, MENU_QUIT, MENU_RESET
|
||||
MENU_GO_BACK - Show main context menu again
|
||||
MENU_QUIT - Quit terminal
|
||||
MENU_RESET - Reset terminal and go back to same previous menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.etf_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -123,21 +148,47 @@ Finance Database:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return MENU_GO_BACK
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
print("")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - exit the program"""
|
||||
return MENU_QUIT
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process Reset command - reset the program"""
|
||||
return MENU_RESET
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "etf")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "reset", "etf"]
|
||||
|
||||
@try_except
|
||||
def call_search(self, other_args: List[str]):
|
||||
@@ -157,29 +208,23 @@ Finance Database:
|
||||
help="String to search for",
|
||||
required="-h" not in other_args,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
search_string = " ".join(ns_parser.search_str)
|
||||
stockanalysis_view.view_search(to_match=search_string, export=ns_parser.export)
|
||||
if ns_parser:
|
||||
search_string = " ".join(ns_parser.search_str)
|
||||
stockanalysis_view.view_search(
|
||||
to_match=search_string, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_overview(self, other_args: List[str]):
|
||||
"""Process overview command"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
@@ -195,24 +240,18 @@ Finance Database:
|
||||
required="-h" not in other_args,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
stockanalysis_view.view_overview(symbol=ns_parser.name, export=ns_parser.export)
|
||||
if ns_parser:
|
||||
stockanalysis_view.view_overview(
|
||||
symbol=ns_parser.name, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_holdings(self, other_args: List[str]):
|
||||
@@ -239,27 +278,19 @@ Finance Database:
|
||||
help="Number of holdings to get",
|
||||
default=20,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
stockanalysis_view.view_holdings(
|
||||
symbol=ns_parser.name,
|
||||
num_to_show=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
stockanalysis_view.view_holdings(
|
||||
symbol=ns_parser.name,
|
||||
num_to_show=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_compare(self, other_args):
|
||||
@@ -278,24 +309,17 @@ Finance Database:
|
||||
help="Symbols to compare",
|
||||
required="-h" not in other_args,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
etf_list = ns_parser.names.upper().split(",")
|
||||
stockanalysis_view.view_comparisons(etf_list, export=ns_parser.export)
|
||||
if ns_parser:
|
||||
etf_list = ns_parser.names.upper().split(",")
|
||||
stockanalysis_view.view_comparisons(etf_list, export=ns_parser.export)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_screener(self, other_args):
|
||||
@@ -309,56 +333,86 @@ Finance Database:
|
||||
description="Screens ETFS from a personal scraping github repository. Data scraped from stockanalysis.com",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
type=int,
|
||||
help="Number of etfs to show",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
default=20,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"--preset",
|
||||
choices=[
|
||||
file.strip(".ini")
|
||||
for file in os.listdir(
|
||||
os.path.join(os.path.abspath(os.path.dirname(__file__)), "presets/")
|
||||
)
|
||||
],
|
||||
choices=self.preset_options,
|
||||
default="etf_config",
|
||||
help="Preset to use",
|
||||
dest="preset",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
screener_view.view_screener(
|
||||
num_to_show=ns_parser.num,
|
||||
preset=ns_parser.preset,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
|
||||
screener_view.view_screener(
|
||||
num_to_show=ns_parser.limit,
|
||||
preset=ns_parser.preset,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_gainers(self, other_args):
|
||||
"""Process gainers command"""
|
||||
wsj_view.show_top_mover("gainers", other_args)
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="gainers",
|
||||
description="Displays top ETF/Mutual fund gainers from wsj.com/market-data",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l", "--limit", help="Number to show", type=int, default=25, dest="limit"
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
wsj_view.show_top_mover("gainers", ns_parser.limit, ns_parser.export)
|
||||
|
||||
return self.queue
|
||||
|
||||
def call_decliners(self, other_args):
|
||||
"""Process decliners command"""
|
||||
wsj_view.show_top_mover("decliners", other_args)
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="decliners",
|
||||
description="Displays top ETF/Mutual fund decliners from wsj.com/market-data",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l", "--limit", help="Number to show", type=int, default=25, dest="limit"
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
wsj_view.show_top_mover("decliners", ns_parser.limit, ns_parser.export)
|
||||
return self.queue
|
||||
|
||||
def call_active(self, other_args):
|
||||
"""Process gainers command"""
|
||||
wsj_view.show_top_mover("active", other_args)
|
||||
parser = argparse.ArgumentParser(
|
||||
prog="active",
|
||||
description="Displays most active ETF/Mutual funds from wsj.com/market-data",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l", "--limit", help="Number to show", type=int, default=25, dest="limit"
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
wsj_view.show_top_mover("active", ns_parser.limit, ns_parser.export)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_pir(self, other_args):
|
||||
@@ -392,21 +446,19 @@ Finance Database:
|
||||
help="Folder where the ETF report will be saved",
|
||||
)
|
||||
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-e")
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-e")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
etf_list = ns_parser.names.upper().split(",")
|
||||
create_ETF_report(
|
||||
etf_list, filename=ns_parser.filename, folder=ns_parser.folder
|
||||
)
|
||||
print(
|
||||
f"Created ETF report as {ns_parser.filename} in folder {ns_parser.folder} \n"
|
||||
)
|
||||
if ns_parser:
|
||||
etf_list = ns_parser.names.upper().split(",")
|
||||
create_ETF_report(
|
||||
etf_list, filename=ns_parser.filename, folder=ns_parser.folder
|
||||
)
|
||||
print(
|
||||
f"Created ETF report as {ns_parser.filename} in folder {ns_parser.folder} \n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_fds(self, other_args):
|
||||
@@ -416,7 +468,6 @@ Finance Database:
|
||||
"assets. Returns the top ETFs when no argument is given. [Source: Finance Database]",
|
||||
add_help=False,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--category",
|
||||
@@ -425,7 +476,6 @@ Finance Database:
|
||||
dest="category",
|
||||
help="Specify the ETF selection based on a category",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--name",
|
||||
@@ -434,7 +484,6 @@ Finance Database:
|
||||
dest="name",
|
||||
help="Specify the ETF selection based on the name",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--description",
|
||||
@@ -443,7 +492,6 @@ Finance Database:
|
||||
dest="description",
|
||||
help="Specify the ETF selection based on the description (not shown in table)",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-ie",
|
||||
"--include_exchanges",
|
||||
@@ -451,16 +499,14 @@ Finance Database:
|
||||
help="When used, data from different exchanges is also included. This leads to a much larger "
|
||||
"pool of data due to the same ETF being listed on multiple exchanges",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--amount",
|
||||
"-l",
|
||||
"--limit",
|
||||
default=10,
|
||||
type=int,
|
||||
dest="amount",
|
||||
dest="limit",
|
||||
help="Enter the number of ETFs you wish to see in the Tabulate window",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--options",
|
||||
@@ -469,48 +515,85 @@ Finance Database:
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
financedatabase_view.show_etfs(
|
||||
category=ns_parser.category,
|
||||
name=ns_parser.name,
|
||||
description=ns_parser.description,
|
||||
include_exchanges=ns_parser.include_exchanges,
|
||||
amount=ns_parser.amount,
|
||||
options=ns_parser.options,
|
||||
)
|
||||
if ns_parser:
|
||||
financedatabase_view.show_etfs(
|
||||
category=ns_parser.category,
|
||||
name=ns_parser.name,
|
||||
description=ns_parser.description,
|
||||
include_exchanges=ns_parser.include_exchanges,
|
||||
amount=ns_parser.limit,
|
||||
options=ns_parser.options,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
etf_controller = ETFController()
|
||||
etf_controller.print_help()
|
||||
plt.close("all")
|
||||
def menu(queue: List[str] = None):
|
||||
etf_controller = ETFController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if etf_controller.queue and len(etf_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if etf_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(etf_controller.queue) > 1:
|
||||
return etf_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = etf_controller.queue[0]
|
||||
etf_controller.queue = etf_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in etf_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /etf/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in etf_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (etf)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (etf)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
etf_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and etf_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /etf/ $ ",
|
||||
completer=etf_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /etf/ $ ")
|
||||
|
||||
try:
|
||||
process_input = etf_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
etf_controller.queue = etf_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
print(f"\nThe command '{an_input}' doesn't exist on the /etf menu.", end="")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, etf_controller.CHOICES, n=1, cutoff=0.7
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
etf_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
etf_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
etf_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -1,55 +1,32 @@
|
||||
"""WSJ view"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
from tabulate import tabulate
|
||||
|
||||
from gamestonk_terminal.etf import wsj_model
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
export_data,
|
||||
parse_known_args_and_warn,
|
||||
try_except,
|
||||
)
|
||||
|
||||
|
||||
@try_except
|
||||
def show_top_mover(sort_type: str, other_args: List[str]):
|
||||
def show_top_mover(sort_type: str, limit: int = 20, export=""):
|
||||
"""
|
||||
Show top ETF movers from wsj.com
|
||||
Parameters
|
||||
----------
|
||||
sort_type: str
|
||||
What to show. Either Gainers, Decliners or Activity
|
||||
other_args: List[str]
|
||||
Argparse arguments
|
||||
|
||||
Show top ETF movers from wsj.com
|
||||
Parameters
|
||||
----------
|
||||
sort_type: str
|
||||
What to show. Either Gainers, Decliners or Activity
|
||||
limit: int
|
||||
Number of etfs to show
|
||||
export: str
|
||||
Format to export data
|
||||
"""
|
||||
|
||||
parser = argparse.ArgumentParser(
|
||||
prog=sort_type,
|
||||
description="Displays top ETF/Mutual fund movers from wsj.com/market-data",
|
||||
add_help=False,
|
||||
)
|
||||
parser.add_argument("-n", help="Number to show", type=int, default=25, dest="num")
|
||||
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
data = wsj_model.etf_movers(sort_type)
|
||||
print(
|
||||
tabulate(
|
||||
data.iloc[: ns_parser.num],
|
||||
data.iloc[:limit],
|
||||
showindex=False,
|
||||
headers=data.columns,
|
||||
floatfmt=".2f",
|
||||
@@ -57,7 +34,7 @@ def show_top_mover(sort_type: str, other_args: List[str]):
|
||||
)
|
||||
)
|
||||
export_data(
|
||||
ns_parser.export,
|
||||
export,
|
||||
os.path.join(os.path.dirname(os.path.abspath(__file__)), "movers"),
|
||||
sort_type,
|
||||
data,
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
# flake8: noqa
|
||||
from . import (
|
||||
behavioural_analysis,
|
||||
exploratory_data_analysis,
|
||||
)
|
||||
|
||||
@@ -1,15 +1,24 @@
|
||||
"""AlphaVantage FX Model"""
|
||||
"""AlphaVantage FX Model."""
|
||||
|
||||
import argparse
|
||||
from typing import Dict
|
||||
from typing import Dict, List
|
||||
import os
|
||||
import pandas as pd
|
||||
import requests
|
||||
from gamestonk_terminal import config_terminal as cfg
|
||||
|
||||
|
||||
def get_currency_list() -> List:
|
||||
"""Load AV currency codes from a local file."""
|
||||
path = os.path.join(os.path.dirname(__file__), "av_forex_currencies.csv")
|
||||
return list(pd.read_csv(path)["currency code"])
|
||||
|
||||
|
||||
CURRENCY_LIST = get_currency_list()
|
||||
|
||||
|
||||
def check_valid_forex_currency(fx_symbol: str) -> str:
|
||||
"""Check if given symbol is supported on alphavantage
|
||||
"""Check if given symbol is supported on alphavantage.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -26,8 +35,7 @@ def check_valid_forex_currency(fx_symbol: str) -> str:
|
||||
argparse.ArgumentTypeError
|
||||
Symbol not valid on alphavantage
|
||||
"""
|
||||
path = os.path.join(os.path.dirname(__file__), "av_forex_currencies.csv")
|
||||
if fx_symbol.upper() in list(pd.read_csv(path)["currency code"]):
|
||||
if fx_symbol.upper() in CURRENCY_LIST:
|
||||
return fx_symbol.upper()
|
||||
|
||||
raise argparse.ArgumentTypeError(
|
||||
@@ -36,7 +44,7 @@ def check_valid_forex_currency(fx_symbol: str) -> str:
|
||||
|
||||
|
||||
def get_quote(to_symbol: str, from_symbol: str) -> Dict:
|
||||
"""Get current exchange rate quote from alpha vantage
|
||||
"""Get current exchange rate quote from alpha vantage.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -50,8 +58,12 @@ def get_quote(to_symbol: str, from_symbol: str) -> Dict:
|
||||
Dict
|
||||
Dictionary of exchange rate
|
||||
"""
|
||||
url = "https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RAT"
|
||||
url += f"E&from_currency={from_symbol}&to_currency={to_symbol}&apikey={cfg.API_KEY_ALPHAVANTAGE}"
|
||||
url = (
|
||||
"https://www.alphavantage.co/query?function=CURRENCY_EXCHANGE_RATE"
|
||||
+ f"&from_currency={from_symbol}"
|
||||
+ f"&to_currency={to_symbol}"
|
||||
+ f"&apikey={cfg.API_KEY_ALPHAVANTAGE}"
|
||||
)
|
||||
r = requests.get(url)
|
||||
if r.status_code != 200:
|
||||
return {}
|
||||
@@ -65,7 +77,7 @@ def get_historical(
|
||||
interval: int = 5,
|
||||
start_date: str = "",
|
||||
) -> pd.DataFrame:
|
||||
"""Get historical forex data
|
||||
"""Get historical forex data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""AlphaVantage Forex View"""
|
||||
"""AlphaVantage Forex View."""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import pandas as pd
|
||||
@@ -11,7 +11,7 @@ from gamestonk_terminal.helper_funcs import plot_autoscale
|
||||
|
||||
|
||||
def display_quote(to_symbol: str, from_symbol: str):
|
||||
"""Display current forex pair exchange rate
|
||||
"""Display current forex pair exchange rate.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -37,7 +37,7 @@ def display_quote(to_symbol: str, from_symbol: str):
|
||||
|
||||
|
||||
def display_candle(data: pd.DataFrame, to_symbol: str, from_symbol: str):
|
||||
"""Show candle plot for fx data
|
||||
"""Show candle plot for fx data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
@@ -58,6 +58,10 @@ def display_candle(data: pd.DataFrame, to_symbol: str, from_symbol: str):
|
||||
)
|
||||
|
||||
s = mpf.make_mpf_style(marketcolors=mc, gridstyle=":", y_on_right=True)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
mpf.plot(
|
||||
data,
|
||||
type="candle",
|
||||
@@ -70,11 +74,9 @@ def display_candle(data: pd.DataFrame, to_symbol: str, from_symbol: str):
|
||||
figscale=1.10,
|
||||
figsize=(plot_autoscale()),
|
||||
update_width_config=dict(
|
||||
candle_linewidth=1.0,
|
||||
candle_linewidth=0.7,
|
||||
candle_width=0.8,
|
||||
),
|
||||
)
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
|
||||
print("")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,202 +0,0 @@
|
||||
"""Exploratory Data Analysis Controller Module"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
from datetime import datetime
|
||||
import pandas as pd
|
||||
from matplotlib import pyplot as plt
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal.common.exploratory_data_analysis import eda_api
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import get_flair, system_clear
|
||||
from gamestonk_terminal.menu import session
|
||||
|
||||
|
||||
class EdaController:
|
||||
"""EDA Controller class"""
|
||||
|
||||
# Command choices
|
||||
CHOICES = [
|
||||
"?",
|
||||
"cls",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"summary",
|
||||
"hist",
|
||||
"cdf",
|
||||
"bwy",
|
||||
"bwm",
|
||||
"rolling",
|
||||
"decompose",
|
||||
"cusum",
|
||||
"acf",
|
||||
]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ticker: str,
|
||||
start: datetime,
|
||||
interval: str,
|
||||
stock: pd.DataFrame,
|
||||
):
|
||||
"""Constructor"""
|
||||
self.stock = stock
|
||||
self.ticker = ticker
|
||||
self.start = start
|
||||
self.interval = interval
|
||||
self.stats_parser = argparse.ArgumentParser(add_help=False, prog="stats")
|
||||
self.stats_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
print(
|
||||
"https://github.com/GamestonkTerminal/"
|
||||
"GamestonkTerminal/tree/main/gamestonk_terminal/exploratory_data_analysis"
|
||||
)
|
||||
s_intraday = (f"Intraday {self.interval}", "Daily")[self.interval == "1440min"]
|
||||
|
||||
if self.start:
|
||||
print(
|
||||
f"\n{s_intraday} Stock: {self.ticker} (from {self.start.strftime('%Y-%m-%d')})"
|
||||
)
|
||||
else:
|
||||
print(f"\n{s_intraday} Stock: {self.ticker}")
|
||||
|
||||
print("\nExploratory Data Analysis:")
|
||||
print(" cls clear screen")
|
||||
print(" ?/help show this menu again")
|
||||
print(" q quit this menu, and shows back to main menu")
|
||||
print(" quit quit to abandon program")
|
||||
print("")
|
||||
print(" summary brief summary statistics")
|
||||
print(" hist histogram with density plot")
|
||||
print(" cdf cumulative distribution function")
|
||||
print(" bwy box and whisker yearly plot")
|
||||
print(" bwm box and whisker monthly plot")
|
||||
print(" rolling rolling mean and std deviation")
|
||||
print(" decompose decomposition in cyclic-trend, season, and residuals")
|
||||
print(" cusum detects abrupt changes using cumulative sum algorithm")
|
||||
print(" acf (partial) auto-correlation function differentials")
|
||||
print("")
|
||||
return
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
|
||||
(known_args, other_args) = self.stats_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
"call_" + known_args.cmd,
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
|
||||
def call_summary(self, other_args: List[str]):
|
||||
"""Process summary command"""
|
||||
eda_api.summary(other_args, self.stock)
|
||||
|
||||
def call_hist(self, other_args: List[str]):
|
||||
"""Process hist command"""
|
||||
eda_api.hist(other_args, self.ticker, self.stock, self.start)
|
||||
|
||||
def call_cdf(self, other_args: List[str]):
|
||||
"""Process cdf command"""
|
||||
eda_api.cumulative_distribution_function(
|
||||
other_args, self.ticker, self.stock, self.start
|
||||
)
|
||||
|
||||
def call_bwy(self, other_args: List[str]):
|
||||
"""Process bwy command"""
|
||||
eda_api.bwy(other_args, self.ticker, self.stock, self.start)
|
||||
|
||||
def call_bwm(self, other_args: List[str]):
|
||||
"""Process bwm command"""
|
||||
eda_api.bwm(other_args, self.ticker, self.stock, self.start)
|
||||
|
||||
def call_rolling(self, other_args: List[str]):
|
||||
"""Process rolling command"""
|
||||
eda_api.rolling(other_args, self.ticker, self.stock)
|
||||
|
||||
def call_decompose(self, other_args: List[str]):
|
||||
"""Process decompose command"""
|
||||
eda_api.decompose(other_args, self.ticker, self.stock)
|
||||
|
||||
def call_cusum(self, other_args: List[str]):
|
||||
"""Process cusum command"""
|
||||
eda_api.cusum(other_args, self.stock)
|
||||
|
||||
def call_acf(self, other_args: List[str]):
|
||||
"""Process acf command"""
|
||||
eda_api.acf(other_args, self.ticker, self.stock, self.start)
|
||||
|
||||
|
||||
def menu(ticker: str, start: datetime, interval: str, stock: pd.DataFrame):
|
||||
"""Statistics Menu"""
|
||||
|
||||
eda_controller = EdaController(ticker, start, interval, stock)
|
||||
eda_controller.call_help(None)
|
||||
|
||||
while True:
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in eda_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (forex)>(eda)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (forex)>(eda)> ")
|
||||
|
||||
try:
|
||||
plt.close("all")
|
||||
|
||||
process_input = eda_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
@@ -1,74 +1,87 @@
|
||||
"""Forex Controller"""
|
||||
"""Forex Controller."""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
from datetime import timedelta, datetime
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
|
||||
import pandas as pd
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from colorama import Style
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.forex.oanda import oanda_controller
|
||||
from gamestonk_terminal.forex import av_view, av_model
|
||||
|
||||
|
||||
# from gamestonk_terminal.forex.exploratory_data_analysis import eda_controller
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
get_flair,
|
||||
system_clear,
|
||||
MENU_GO_BACK,
|
||||
MENU_QUIT,
|
||||
MENU_RESET,
|
||||
parse_known_args_and_warn,
|
||||
try_except,
|
||||
valid_date,
|
||||
try_except,
|
||||
system_clear,
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
|
||||
# pylint: disable=R1710,import-outside-toplevel
|
||||
|
||||
|
||||
class ForexController:
|
||||
"""Forex Controller class"""
|
||||
"""Forex Controller class."""
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS = ["select", "load", "quote", "candle"]
|
||||
CHOICES_COMMANDS = ["to", "from", "load", "quote", "candle"]
|
||||
|
||||
CHOICES_MENUS = ["oanda"]
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
CHOICES += CHOICES_MENUS
|
||||
|
||||
def __init__(self):
|
||||
"""Construct Data"""
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Construct Data."""
|
||||
self.fx_parser = argparse.ArgumentParser(add_help=False, prog="forex")
|
||||
self.fx_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
|
||||
choices["to"] = {c: None for c in av_model.CURRENCY_LIST}
|
||||
choices["from"] = {c: None for c in av_model.CURRENCY_LIST}
|
||||
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
self.from_symbol = "USD"
|
||||
self.to_symbol = ""
|
||||
self.data = pd.DataFrame()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
dim_bool = self.from_symbol and self.to_symbol
|
||||
help_str = f"""
|
||||
What would you like to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu and goes back to main menu
|
||||
quit quit to abandon program
|
||||
reset reset terminal and reload configs
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
select select fx pair
|
||||
def print_help(self):
|
||||
"""Print help."""
|
||||
dim_bool = self.from_symbol and self.to_symbol
|
||||
help_text = f"""
|
||||
from select the "from" currency in a forex pair
|
||||
to select the "to" currency in a forex pair
|
||||
|
||||
From: {None or self.from_symbol}
|
||||
To: {None or self.to_symbol}
|
||||
@@ -78,38 +91,50 @@ AlphaVantage:
|
||||
load get historical data
|
||||
candle show candle plot for loaded data
|
||||
{Style.RESET_ALL}
|
||||
Brokerages:
|
||||
> oanda access oanda menu
|
||||
Forex brokerages:
|
||||
> oanda Oanda menu
|
||||
"""
|
||||
print(help_str)
|
||||
print(help_text)
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
"""Process and dispatch input.
|
||||
|
||||
Returns
|
||||
-------
|
||||
MENU_GO_BACK, MENU_QUIT, MENU_RESET
|
||||
MENU_GO_BACK - Show main context menu again
|
||||
MENU_QUIT - Quit terminal
|
||||
MENU_RESET - Reset terminal and go back to same previous menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.fx_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -117,67 +142,117 @@ Brokerages:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help Command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command."""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return MENU_GO_BACK
|
||||
def call_home(self, _):
|
||||
"""Process home command."""
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command."""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - exit the program"""
|
||||
return MENU_QUIT
|
||||
"""Process quit menu command."""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command."""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process Reset command - reset the program"""
|
||||
return MENU_RESET
|
||||
"""Process reset command."""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "forex")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "reset", "forex"]
|
||||
|
||||
# COMMANDS
|
||||
@try_except
|
||||
def call_select(self, other_args: List[str]):
|
||||
"""Process select command"""
|
||||
def call_to(self, other_args: List[str]):
|
||||
"""Process 'to' command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="select",
|
||||
description="Select Forex pair in the form of TO -f FROM",
|
||||
prog="to",
|
||||
description='Select the "to" currency symbol in a forex pair',
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--to",
|
||||
"-n",
|
||||
"--name",
|
||||
help="To currency",
|
||||
type=av_model.check_valid_forex_currency,
|
||||
dest="to_symbol",
|
||||
)
|
||||
|
||||
if (
|
||||
other_args
|
||||
and "-n" not in other_args[0]
|
||||
and "--name" not in other_args[0]
|
||||
and "-h" not in other_args
|
||||
):
|
||||
other_args.insert(0, "-n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
self.to_symbol = ns_parser.to_symbol
|
||||
|
||||
print(
|
||||
f"\nSelected pair\nFrom: {self.from_symbol}\nTo: {self.to_symbol}\n\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_from(self, other_args: List[str]):
|
||||
"""Process 'from' command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="to",
|
||||
description='Select the "from" currency symbol in a forex pair',
|
||||
)
|
||||
parser.add_argument(
|
||||
"-f",
|
||||
"--from",
|
||||
"-n",
|
||||
"--name",
|
||||
help="From currency",
|
||||
type=av_model.check_valid_forex_currency,
|
||||
dest="from_symbol",
|
||||
default=None,
|
||||
)
|
||||
|
||||
if (
|
||||
other_args
|
||||
and "-f" not in other_args[0]
|
||||
and "--from" not in other_args[0]
|
||||
and "-t" not in other_args
|
||||
and "-n" not in other_args[0]
|
||||
and "--name" not in other_args[0]
|
||||
and "-h" not in other_args
|
||||
):
|
||||
other_args.insert(0, "-t")
|
||||
other_args.insert(0, "-n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
self.to_symbol = ns_parser.to_symbol
|
||||
if ns_parser.from_symbol:
|
||||
if ns_parser:
|
||||
self.from_symbol = ns_parser.from_symbol
|
||||
print("")
|
||||
print(
|
||||
f"\nSelected pair\nFrom: {self.from_symbol}\nTo: {self.to_symbol}\n\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_load(self, other_args: List[str]):
|
||||
"""Process select command"""
|
||||
"""Process select command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
@@ -208,29 +283,29 @@ Brokerages:
|
||||
help="Start date of data.",
|
||||
dest="start_date",
|
||||
)
|
||||
|
||||
if other_args and "-t" not in other_args and "-h" not in other_args:
|
||||
other_args.insert(0, "-t")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if self.to_symbol and self.from_symbol:
|
||||
self.data = av_model.get_historical(
|
||||
to_symbol=self.to_symbol,
|
||||
from_symbol=self.from_symbol,
|
||||
resolution=ns_parser.resolution,
|
||||
interval=ns_parser.interval,
|
||||
start_date=ns_parser.start_date.strftime("%Y-%m-%d"),
|
||||
)
|
||||
print(
|
||||
f"Loaded historic data from {self.from_symbol} to {self.to_symbol}"
|
||||
)
|
||||
else:
|
||||
print("\nMake sure both a to symbol and a from symbol are supplied\n")
|
||||
|
||||
if not self.to_symbol or not self.from_symbol:
|
||||
print(
|
||||
"Make sure both a to symbol and a from symbol are supplied using <select> \n"
|
||||
)
|
||||
return
|
||||
|
||||
self.data = av_model.get_historical(
|
||||
to_symbol=self.to_symbol,
|
||||
from_symbol=self.from_symbol,
|
||||
resolution=ns_parser.resolution,
|
||||
interval=ns_parser.interval,
|
||||
start_date=ns_parser.start_date.strftime("%Y-%m-%d"),
|
||||
)
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_candle(self, other_args: List[str]):
|
||||
"""Process quote command"""
|
||||
"""Process quote command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
@@ -238,95 +313,117 @@ Brokerages:
|
||||
description="Show candle for loaded fx data",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if self.data.empty:
|
||||
print("No forex historical data loaded. Load first using <load>.")
|
||||
return
|
||||
|
||||
av_view.display_candle(self.data, self.to_symbol, self.from_symbol)
|
||||
if ns_parser:
|
||||
if not self.data.empty:
|
||||
av_view.display_candle(self.data, self.to_symbol, self.from_symbol)
|
||||
else:
|
||||
print("No forex historical data loaded. Load first using <load>.")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_quote(self, other_args: List[str]):
|
||||
"""Process quote command"""
|
||||
"""Process quote command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="quote",
|
||||
description="Get current exchange rate quote",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if self.to_symbol and self.from_symbol:
|
||||
av_view.display_quote(self.to_symbol, self.from_symbol)
|
||||
else:
|
||||
print('Make sure both a "to" symbol and a "from" symbol are selected\n')
|
||||
|
||||
if not self.to_symbol or not self.from_symbol:
|
||||
print(
|
||||
"Make sure both a to symbol and a from symbol are supplied using <select> \n"
|
||||
)
|
||||
return
|
||||
return self.queue
|
||||
|
||||
av_view.display_quote(self.to_symbol, self.from_symbol)
|
||||
|
||||
# pylint: disable=inconsistent-return-statements
|
||||
# MENUS
|
||||
def call_oanda(self, _):
|
||||
ret = oanda_controller.menu()
|
||||
if ret:
|
||||
return True
|
||||
self.print_help()
|
||||
"""Enter Oanda menu."""
|
||||
from gamestonk_terminal.forex.oanda import oanda_controller
|
||||
|
||||
return oanda_controller.menu(self.queue)
|
||||
|
||||
# HELP WANTED!
|
||||
# TODO: Add news and reddit commands back
|
||||
|
||||
# def call_eda(self, _):
|
||||
# try:
|
||||
# df = fx_view.get_candles_dataframe(account, self.instrument, None)
|
||||
# df = df.rename(columns={"Close": "Adj Close"})
|
||||
# instrument = self.instrument
|
||||
# s_start = pd.to_datetime(df.index.values[0])
|
||||
# s_interval = "1440min"
|
||||
# eda_controller.menu(df, instrument, s_start, s_interval)
|
||||
# except AttributeError:
|
||||
# print("No data found, do you have your oanda API keys set?")
|
||||
|
||||
# def call_ba(self, _):
|
||||
# instrument = fx_view.format_instrument(self.instrument, " ")
|
||||
# try:
|
||||
# df = fx_view.get_candles_dataframe(account, self.instrument, None)
|
||||
# s_start = pd.to_datetime(df.index.values[0])
|
||||
# ba_controller.menu(instrument, s_start)
|
||||
# except AttributeError:
|
||||
# print("No data found, do you have your oanda API keys set?")
|
||||
# behavioural analysis and exploratory data analysis would be useful in the
|
||||
# forex menu. The examples of integration of the common ba and eda components
|
||||
# into the stocks context can provide an insight on how this can be done.
|
||||
# The earlier implementation did not work and was deleted in commit
|
||||
# d0e51033f7d5d4da6386b9e0b787892979924dce
|
||||
|
||||
|
||||
def menu():
|
||||
"""Forex Menu"""
|
||||
fx_controller = ForexController()
|
||||
fx_controller.call_help(None)
|
||||
def menu(queue: List[str] = None):
|
||||
"""Forex Menu."""
|
||||
forex_controller = ForexController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in fx_controller.CHOICES}
|
||||
)
|
||||
# There is a command in the queue
|
||||
if forex_controller.queue and len(forex_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if forex_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(forex_controller.queue) > 1:
|
||||
return forex_controller.queue[1:]
|
||||
return []
|
||||
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (forex)> ",
|
||||
completer=completer,
|
||||
)
|
||||
# Consume 1 element from the queue
|
||||
an_input = forex_controller.queue[0]
|
||||
forex_controller.queue = forex_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in forex_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /forex/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (forex)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
forex_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and forex_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /forex/ $ ",
|
||||
completer=forex_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /forex/ $ ")
|
||||
|
||||
try:
|
||||
process_input = fx_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
forex_controller.queue = forex_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exit\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, fx_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /forex menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
forex_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
forex_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
forex_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -1,911 +0,0 @@
|
||||
import argparse
|
||||
from datetime import datetime
|
||||
from typing import List
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import matplotlib.ticker as mticker
|
||||
import mplfinance as mpf
|
||||
import pandas as pd
|
||||
import pandas_ta as ta
|
||||
import seaborn as sns
|
||||
from oandapyV20 import API
|
||||
from oandapyV20.endpoints import (
|
||||
accounts,
|
||||
forexlabs,
|
||||
instruments,
|
||||
orders,
|
||||
positions,
|
||||
pricing,
|
||||
trades,
|
||||
)
|
||||
from oandapyV20.exceptions import V20Error
|
||||
|
||||
from gamestonk_terminal import config_plot as cfgPlot
|
||||
from gamestonk_terminal import config_terminal as cfg
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
check_non_negative,
|
||||
parse_known_args_and_warn,
|
||||
plot_autoscale,
|
||||
try_except,
|
||||
)
|
||||
|
||||
client = API(access_token=cfg.OANDA_TOKEN, environment=cfg.OANDA_ACCOUNT_TYPE)
|
||||
account = cfg.OANDA_ACCOUNT
|
||||
|
||||
|
||||
def get_fx_price(accountID, instrument, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="price",
|
||||
description="Get price for selected instrument.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
try:
|
||||
parameters = {"instruments": instrument}
|
||||
request = pricing.PricingInfo(accountID=accountID, params=parameters)
|
||||
response = client.request(request)
|
||||
bid = response["prices"][0]["bids"][0]["price"]
|
||||
ask = response["prices"][0]["asks"][0]["price"]
|
||||
print(instrument + " Bid: " + bid)
|
||||
print(instrument + " Ask: " + ask)
|
||||
print("")
|
||||
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
def get_account_summary(accountID, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="summary",
|
||||
description="Print some information about your account.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
try:
|
||||
request = accounts.AccountSummary(accountID=accountID)
|
||||
response = client.request(request)
|
||||
|
||||
df_summary = pd.DataFrame(
|
||||
[
|
||||
{"Type": "Balance", "Value": response["account"]["balance"]},
|
||||
{"Type": "NAV", "Value": response["account"]["NAV"]},
|
||||
{
|
||||
"Type": "Unrealized P/L",
|
||||
"Value": response["account"]["unrealizedPL"],
|
||||
},
|
||||
{"Type": "Total P/L", "Value": response["account"]["pl"]},
|
||||
{
|
||||
"Type": "Open Trade Count",
|
||||
"Value": response["account"]["openTradeCount"],
|
||||
},
|
||||
{
|
||||
"Type": "Margin Available",
|
||||
"Value": response["account"]["marginAvailable"],
|
||||
},
|
||||
{"Type": "Margin Used", "Value": response["account"]["marginUsed"]},
|
||||
{
|
||||
"Type": "Margin Closeout",
|
||||
"Value": response["account"]["marginCloseoutNAV"],
|
||||
},
|
||||
{
|
||||
"Type": "Margin Closeout Percent",
|
||||
"Value": response["account"]["marginCloseoutPercent"],
|
||||
},
|
||||
{
|
||||
"Type": "Margin Closeout Position Value",
|
||||
"Value": response["account"]["marginCloseoutPositionValue"],
|
||||
},
|
||||
]
|
||||
)
|
||||
|
||||
print(df_summary.to_string(index=False, header=False))
|
||||
|
||||
print("")
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
def list_orders(accountID, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="list",
|
||||
description="List order history",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--state",
|
||||
dest="state",
|
||||
action="store",
|
||||
default="ALL",
|
||||
required=False,
|
||||
help="Select state for order list. ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--count",
|
||||
dest="count",
|
||||
action="store",
|
||||
default=20,
|
||||
required=False,
|
||||
help="the number of orders to retrieve ",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
parameters = {}
|
||||
parameters["state"] = ns_parser.state.upper()
|
||||
parameters["count"] = ns_parser.count
|
||||
|
||||
try:
|
||||
request = orders.OrderList(accountID, parameters)
|
||||
response = client.request(request)
|
||||
|
||||
df = pd.DataFrame.from_dict(response["orders"])
|
||||
df = df[["id", "instrument", "units", "price", "state", "type"]]
|
||||
print(df)
|
||||
print("")
|
||||
|
||||
except KeyError:
|
||||
print("No orders were found\n")
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
def get_order_book(instrument, other_args):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="orderbook",
|
||||
description="Plot the orderbook for the instrument if Oanda provides one.",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
parameters = {"bucketWidth": "1"}
|
||||
try:
|
||||
instrument = format_instrument(instrument, "_")
|
||||
request = instruments.InstrumentsOrderBook(
|
||||
instrument=instrument, params=parameters
|
||||
)
|
||||
response = client.request(request)
|
||||
df = pd.DataFrame.from_dict(response["orderBook"]["buckets"])
|
||||
pd.set_option("display.max_rows", None)
|
||||
df = df.take(range(527, 727, 1))
|
||||
book_plot(df, instrument, "Order Book")
|
||||
print("")
|
||||
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
def get_position_book(instrument, other_args):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="positionbook",
|
||||
description="Plot the positionbook for the instrument if Oanda provides one.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
try:
|
||||
instrument = format_instrument(instrument, "_")
|
||||
request = instruments.InstrumentsPositionBook(instrument=instrument)
|
||||
response = client.request(request)
|
||||
df = pd.DataFrame.from_dict(response["positionBook"]["buckets"])
|
||||
pd.set_option("display.max_rows", None)
|
||||
df = df.take(range(219, 415, 1))
|
||||
book_plot(df, instrument, "Position Book")
|
||||
print("")
|
||||
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
def create_order(accountID, instrument, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="order",
|
||||
description="Create order",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-u",
|
||||
"--unit",
|
||||
dest="units",
|
||||
action="store",
|
||||
type=int,
|
||||
default=0,
|
||||
required=True,
|
||||
help="The number of units to place in the order request. Positive for "
|
||||
+ "a long position and negative for a short position ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--price",
|
||||
dest="price",
|
||||
action="store",
|
||||
type=check_non_negative,
|
||||
required=True,
|
||||
help="The price to set for the limit order. ",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if "JPY" in instrument or "THB" in instrument or "HUF" in instrument:
|
||||
ns_parser.price = round(ns_parser.price, 3)
|
||||
else:
|
||||
ns_parser.price = round(ns_parser.price, 5)
|
||||
|
||||
data = {
|
||||
"order": {
|
||||
"price": ns_parser.price,
|
||||
"instrument": instrument,
|
||||
"units": ns_parser.units,
|
||||
"type": "LIMIT",
|
||||
"timeInForce": "GTC",
|
||||
"positionFill": "DEFAULT",
|
||||
}
|
||||
}
|
||||
try:
|
||||
request = orders.OrderCreate(accountID, data)
|
||||
response = client.request(request)
|
||||
order_data = []
|
||||
order_data.append(
|
||||
{
|
||||
"Order ID": response["orderCreateTransaction"]["id"],
|
||||
"Instrument": response["orderCreateTransaction"]["instrument"],
|
||||
"Price": response["orderCreateTransaction"]["price"],
|
||||
"Units": response["orderCreateTransaction"]["units"],
|
||||
}
|
||||
)
|
||||
|
||||
df = pd.DataFrame.from_dict(order_data)
|
||||
print(df.to_string(index=False))
|
||||
print("")
|
||||
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def cancel_pending_order(accountID, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="cancel",
|
||||
description="Cancel Pending Order ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--id",
|
||||
dest="orderID",
|
||||
action="store",
|
||||
type=str,
|
||||
help="The pending order ID to cancel ",
|
||||
)
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-i")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
try:
|
||||
request = orders.OrderCancel(accountID, ns_parser.orderID)
|
||||
response = client.request(request)
|
||||
order_id = response["orderCancelTransaction"]["orderID"]
|
||||
print(f"Order {order_id} canceled.")
|
||||
print("")
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
def get_pending_orders(accountID, other_args):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="pending",
|
||||
description="Gets information about pending orders.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
try:
|
||||
request = orders.OrdersPending(accountID)
|
||||
response = client.request(request)
|
||||
pending_data = []
|
||||
for i in range(len(response["orders"])):
|
||||
pending_data.append(
|
||||
{
|
||||
"Order ID": response["orders"][i]["id"],
|
||||
"Instrument": response["orders"][i]["instrument"],
|
||||
"Price": response["orders"][i]["price"],
|
||||
"Units": response["orders"][i]["units"],
|
||||
"Time Created": response["orders"][i]["createTime"][:10]
|
||||
+ " "
|
||||
+ response["orders"][i]["createTime"][11:19],
|
||||
"Time In Force": response["orders"][i]["timeInForce"],
|
||||
}
|
||||
)
|
||||
|
||||
df = pd.DataFrame.from_dict(pending_data)
|
||||
if not pending_data:
|
||||
print("No pending orders")
|
||||
else:
|
||||
print(df.to_string(index=False))
|
||||
print("")
|
||||
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
def get_open_positions(accountID, other_args):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="positions",
|
||||
description="Gets information about open positions.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
try:
|
||||
request = positions.OpenPositions(accountID)
|
||||
response = client.request(request)
|
||||
position_data = []
|
||||
for i in range(len(response["positions"])):
|
||||
position_data.append(
|
||||
{
|
||||
"Instrument": response["positions"][i]["instrument"],
|
||||
"Long Units": response["positions"][i]["long"]["units"],
|
||||
"Total Long P/L": response["positions"][i]["long"]["units"],
|
||||
"Unrealized Long P/L": response["positions"][i]["long"][
|
||||
"unrealizedPL"
|
||||
],
|
||||
"Short Units": response["positions"][i]["short"]["units"],
|
||||
"Total Short P/L": response["positions"][i]["short"]["pl"],
|
||||
"Short Unrealized P/L": response["positions"][i]["short"][
|
||||
"unrealizedPL"
|
||||
],
|
||||
}
|
||||
)
|
||||
|
||||
df = pd.DataFrame.from_dict(position_data)
|
||||
print(df.to_string(index=False))
|
||||
print("")
|
||||
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
def get_open_trades(accountID, other_args):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="trades",
|
||||
description="Gets information about open trades.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
try:
|
||||
request = trades.OpenTrades(accountID)
|
||||
response = client.request(request)
|
||||
try:
|
||||
df = pd.DataFrame.from_dict(response["trades"])
|
||||
df = df[
|
||||
[
|
||||
"id",
|
||||
"instrument",
|
||||
"initialUnits",
|
||||
"currentUnits",
|
||||
"price",
|
||||
"unrealizedPL",
|
||||
]
|
||||
]
|
||||
df = df.rename(
|
||||
columns={
|
||||
"id": "ID",
|
||||
"instrument": "Instrument",
|
||||
"initialUnits": "Initial Units",
|
||||
"currentUnits": "Current Units",
|
||||
"price": "Entry Price",
|
||||
"unrealizedPL": "Unrealized P/L",
|
||||
}
|
||||
)
|
||||
|
||||
print(df.to_string(index=False))
|
||||
print("")
|
||||
|
||||
except KeyError:
|
||||
print("No trades were found")
|
||||
print("")
|
||||
except V20Error as e:
|
||||
msg_length = len(e.msg)
|
||||
msg = e.msg[17 : msg_length - 2]
|
||||
print(msg)
|
||||
print("")
|
||||
|
||||
|
||||
def close_trade(accountID, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="closetrade",
|
||||
description="Close a trade by id.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--id",
|
||||
dest="orderID",
|
||||
action="store",
|
||||
type=str,
|
||||
required=False,
|
||||
help="The Trade ID to close. ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-u",
|
||||
"--units",
|
||||
dest="units",
|
||||
action="store",
|
||||
required=False,
|
||||
help="The number of units on the trade to close. If not set it "
|
||||
+ "defaults to all units. ",
|
||||
)
|
||||
if other_args:
|
||||
if "-i" not in other_args[0]:
|
||||
other_args.insert(0, "-i")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
data = {}
|
||||
if ns_parser.units is not None:
|
||||
data["units"] = ns_parser.units
|
||||
try:
|
||||
request = trades.TradeClose(accountID, ns_parser.orderID, data)
|
||||
response = client.request(request)
|
||||
|
||||
close_data = []
|
||||
close_data.append(
|
||||
{
|
||||
"OrderID": response["orderCreateTransaction"]["tradeClose"]["tradeID"],
|
||||
"Instrument": response["orderFillTransaction"]["instrument"],
|
||||
"Units": response["orderCreateTransaction"]["units"],
|
||||
"Price": response["orderFillTransaction"]["price"],
|
||||
"P/L": response["orderFillTransaction"]["pl"],
|
||||
}
|
||||
)
|
||||
df = pd.DataFrame.from_dict(close_data)
|
||||
print(df.to_string(index=False))
|
||||
print("")
|
||||
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
except Exception as e:
|
||||
print(e, "\n")
|
||||
|
||||
|
||||
def show_candles(accountID, instrument, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="candles",
|
||||
description="Display Candle Data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-g",
|
||||
"--granularity",
|
||||
dest="granularity",
|
||||
action="store",
|
||||
type=str,
|
||||
default="D",
|
||||
required=False,
|
||||
help="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",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--count",
|
||||
dest="candlecount",
|
||||
action="store",
|
||||
default=180,
|
||||
type=int,
|
||||
required=False,
|
||||
help="The number of candles to retrieve. Default:180 ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--ad",
|
||||
dest="ad",
|
||||
action="store_true",
|
||||
help="Adds ad (Accumulation/Distribution Indicator) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--bbands",
|
||||
dest="bbands",
|
||||
action="store_true",
|
||||
help="Adds Bollinger Bands to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-C",
|
||||
"--cci",
|
||||
dest="cci",
|
||||
action="store_true",
|
||||
help="Adds cci (Commodity Channel Index) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
"--ema",
|
||||
dest="ema",
|
||||
action="store_true",
|
||||
help="Adds ema (Exponential Moving Average) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--obv",
|
||||
dest="obv",
|
||||
action="store_true",
|
||||
help="Adds obv (On Balance Volume) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--rsi",
|
||||
dest="rsi",
|
||||
action="store_true",
|
||||
help="Adds rsi (Relative Strength Index) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--sma",
|
||||
dest="sma",
|
||||
action="store_true",
|
||||
help="Adds sma (Simple Moving Average) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--vwap",
|
||||
dest="vwap",
|
||||
action="store_true",
|
||||
help="Adds vwap (Volume Weighted Average Price) to the chart",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
parameters = {}
|
||||
parameters["granularity"] = ns_parser.granularity.upper()
|
||||
parameters["count"] = ns_parser.candlecount
|
||||
try:
|
||||
instrument = format_instrument(instrument, "_")
|
||||
df = get_candles_dataframe(accountID, instrument, parameters)
|
||||
|
||||
plots_to_add, legends, subplot_legends = add_plots(df, ns_parser)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
# pylint: disable=W0612
|
||||
fig, ax = mpf.plot(
|
||||
df,
|
||||
type="candle",
|
||||
style="charles",
|
||||
volume=True,
|
||||
title=f"{instrument} {ns_parser.granularity}",
|
||||
returnfig=True,
|
||||
addplot=plots_to_add,
|
||||
)
|
||||
|
||||
ax[0].set_title(f"{instrument} {ns_parser.granularity}")
|
||||
ax[0].legend(legends)
|
||||
# pylint: disable=C0200
|
||||
for i in range(0, len(subplot_legends), 2):
|
||||
ax[subplot_legends[i]].legend(subplot_legends[i + 1])
|
||||
|
||||
print("")
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
except TypeError as e:
|
||||
print(e)
|
||||
|
||||
|
||||
def add_plots(df, ns_parser):
|
||||
panel_number = 2
|
||||
plots_to_add = []
|
||||
legends = []
|
||||
subplot_legends = []
|
||||
|
||||
if ns_parser.ad:
|
||||
ad = ta.ad(df["High"], df["Low"], df["Close"], df["Volume"])
|
||||
ad_plot = mpf.make_addplot(ad, panel=panel_number)
|
||||
plots_to_add.append(ad_plot)
|
||||
subplot_legends.extend([panel_number * 2, ["AD"]])
|
||||
panel_number += 1
|
||||
|
||||
if ns_parser.bbands:
|
||||
bbands = ta.bbands(df["Close"])
|
||||
bbands = bbands.drop("BBB_5_2.0", axis=1)
|
||||
bbands_plot = mpf.make_addplot(bbands, panel=0)
|
||||
plots_to_add.append(bbands_plot)
|
||||
legends.extend(["Lower BBand", "Middle BBand", "Upper BBand"])
|
||||
|
||||
if ns_parser.cci:
|
||||
cci = ta.cci(df["High"], df["Low"], df["Close"])
|
||||
cci_plot = mpf.make_addplot(cci, panel=panel_number)
|
||||
plots_to_add.append(cci_plot)
|
||||
subplot_legends.extend([panel_number * 2, ["CCI"]])
|
||||
panel_number += 1
|
||||
|
||||
if ns_parser.ema:
|
||||
ema = ta.ema(df["Close"])
|
||||
ema_plot = mpf.make_addplot(ema, panel=0)
|
||||
plots_to_add.append(ema_plot)
|
||||
legends.append("10 EMA")
|
||||
|
||||
if ns_parser.rsi:
|
||||
rsi = ta.rsi(df["Close"])
|
||||
rsi_plot = mpf.make_addplot(rsi, panel=panel_number)
|
||||
plots_to_add.append(rsi_plot)
|
||||
subplot_legends.extend([panel_number * 2, ["RSI"]])
|
||||
panel_number += 1
|
||||
|
||||
if ns_parser.obv:
|
||||
obv = ta.obv(df["Close"], df["Volume"])
|
||||
obv_plot = mpf.make_addplot(obv, panel=panel_number)
|
||||
plots_to_add.append(obv_plot)
|
||||
subplot_legends.extend([panel_number * 2, ["OBV"]])
|
||||
panel_number += 1
|
||||
|
||||
if ns_parser.sma:
|
||||
sma_length = [20, 50]
|
||||
for length in sma_length:
|
||||
sma = ta.sma(df["Close"], length=length)
|
||||
sma_plot = mpf.make_addplot(sma, panel=0)
|
||||
plots_to_add.append(sma_plot)
|
||||
legends.append(f"{length} SMA")
|
||||
|
||||
if ns_parser.vwap:
|
||||
vwap = ta.vwap(df["High"], df["Low"], df["Close"], df["Volume"])
|
||||
vwap_plot = mpf.make_addplot(vwap, panel=0)
|
||||
plots_to_add.append(vwap_plot)
|
||||
legends.append("vwap")
|
||||
|
||||
return plots_to_add, legends, subplot_legends
|
||||
|
||||
|
||||
def calendar(instrument, other_args: List[str]):
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="calendar",
|
||||
description="Show Calendar Data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--days",
|
||||
dest="days",
|
||||
action="store",
|
||||
type=int,
|
||||
default=7,
|
||||
required=False,
|
||||
help="The number of days to search for, up to 30 forward or backward "
|
||||
+ "use negative numbers to search back. ",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
parameters = {"instrument": instrument, "period": str(ns_parser.days * 86400 * -1)}
|
||||
try:
|
||||
request = forexlabs.Calendar(params=parameters)
|
||||
response = client.request(request)
|
||||
|
||||
l_data = []
|
||||
# pylint: disable=C0200
|
||||
for i in range(len(response)):
|
||||
if "forecast" in response[i]:
|
||||
forecast = response[i]["forecast"]
|
||||
if response[i]["unit"] != "Index":
|
||||
forecast += response[i]["unit"]
|
||||
else:
|
||||
forecast = ""
|
||||
|
||||
if "market" in response[i]:
|
||||
market = response[i]["market"]
|
||||
if response[i]["unit"] != "Index":
|
||||
market += response[i]["unit"]
|
||||
else:
|
||||
market = ""
|
||||
|
||||
if "actual" in response[i]:
|
||||
actual = response[i]["actual"]
|
||||
if response[i]["unit"] != "Index":
|
||||
actual += response[i]["unit"]
|
||||
else:
|
||||
actual = ""
|
||||
|
||||
if "previous" in response[i]:
|
||||
previous = response[i]["previous"]
|
||||
if response[i]["unit"] != "Index":
|
||||
previous += response[i]["unit"]
|
||||
else:
|
||||
previous = ""
|
||||
|
||||
if "impact" in response[i]:
|
||||
impact = response[i]["impact"]
|
||||
else:
|
||||
impact = ""
|
||||
|
||||
l_data.append(
|
||||
{
|
||||
"Title": response[i]["title"],
|
||||
"Time": datetime.fromtimestamp(response[i]["timestamp"]),
|
||||
"Impact": impact,
|
||||
"Forecast": forecast,
|
||||
"Market Forecast": market,
|
||||
"Currency": response[i]["currency"],
|
||||
"Region": response[i]["region"],
|
||||
"Actual": actual,
|
||||
"Previous": previous,
|
||||
}
|
||||
)
|
||||
|
||||
print(pd.DataFrame(l_data).to_string(index=False))
|
||||
print("")
|
||||
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["message"], "\n")
|
||||
|
||||
|
||||
# pylint: disable=W0613
|
||||
# pylint: disable=R1710
|
||||
def get_candles_dataframe(accountID, instrument, parameters):
|
||||
if not parameters:
|
||||
parameters = {
|
||||
"granularity": "D",
|
||||
"count": "1500",
|
||||
}
|
||||
try:
|
||||
request = instruments.InstrumentsCandles(instrument, params=parameters)
|
||||
response = client.request(request)
|
||||
candles_data = []
|
||||
for i in range(len(response["candles"])):
|
||||
candles_data.append(
|
||||
{
|
||||
"Date": response["candles"][i]["time"][:10]
|
||||
+ " "
|
||||
+ response["candles"][i]["time"][11:19],
|
||||
"Open": float(response["candles"][i]["mid"]["o"]),
|
||||
"High": float(response["candles"][i]["mid"]["h"]),
|
||||
"Low": float(response["candles"][i]["mid"]["l"]),
|
||||
"Close": float(response["candles"][i]["mid"]["c"]),
|
||||
"Volume": response["candles"][i]["volume"],
|
||||
}
|
||||
)
|
||||
df = pd.DataFrame(candles_data)
|
||||
df.set_index("Date", inplace=True)
|
||||
df.index = pd.to_datetime(df.index)
|
||||
return df
|
||||
except V20Error as e:
|
||||
# pylint: disable=W0123
|
||||
d_error = eval(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
|
||||
|
||||
# pylint: disable=R1710
|
||||
@try_except
|
||||
def load(other_args: List[str]):
|
||||
"""Load a forex instrument to use"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="load",
|
||||
description="Forex using oanda",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--instrument",
|
||||
required=True,
|
||||
type=str,
|
||||
dest="instrument",
|
||||
help="Forex pair to use. ",
|
||||
)
|
||||
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-i")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
print("")
|
||||
return ns_parser.instrument.upper()
|
||||
|
||||
|
||||
def book_plot(df, instrument, book_type):
|
||||
_, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfgPlot.PLOT_DPI)
|
||||
df = df.apply(pd.to_numeric)
|
||||
df["shortCountPercent"] = df["shortCountPercent"] * -1
|
||||
axis_origin = max(
|
||||
abs(max(df["longCountPercent"])), abs(max(df["shortCountPercent"]))
|
||||
)
|
||||
ax.set_xlim(-axis_origin, +axis_origin)
|
||||
|
||||
sns.set_style(style="darkgrid")
|
||||
|
||||
sns.barplot(
|
||||
x="longCountPercent",
|
||||
y="price",
|
||||
data=df,
|
||||
label="Count Percent",
|
||||
color="green",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
sns.barplot(
|
||||
x="shortCountPercent",
|
||||
y="price",
|
||||
data=df,
|
||||
label="Prices",
|
||||
color="red",
|
||||
orient="h",
|
||||
)
|
||||
|
||||
ax.invert_yaxis()
|
||||
plt.title(f"{instrument} {book_type}")
|
||||
plt.xlabel("Count Percent")
|
||||
plt.ylabel("Price")
|
||||
sns.despine(left=True, bottom=True)
|
||||
ax.yaxis.set_major_locator(mticker.MultipleLocator(5))
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
|
||||
|
||||
# pylint: disable=R1710
|
||||
def format_instrument(instrument, char):
|
||||
try:
|
||||
if char not in instrument:
|
||||
instrument_list = list(instrument)
|
||||
instrument_list.pop(3)
|
||||
if char == " ":
|
||||
instrument = "".join(map(str, instrument_list))
|
||||
else:
|
||||
instrument_list.insert(3, char)
|
||||
instrument = "".join(map(str, instrument_list))
|
||||
return instrument
|
||||
except TypeError:
|
||||
print("Please load an instrument")
|
||||
print("")
|
||||
@@ -1,21 +1,25 @@
|
||||
"""Oanda Controller"""
|
||||
"""Oanda Controller."""
|
||||
__docformat__ = "numpy"
|
||||
import argparse
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
from typing import List, Union
|
||||
|
||||
from colorama import Style
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from gamestonk_terminal import config_terminal as cfg
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.forex import av_model
|
||||
from gamestonk_terminal.forex.oanda import oanda_view
|
||||
|
||||
# from gamestonk_terminal.forex.exploratory_data_analysis import eda_controller
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
get_flair,
|
||||
MENU_GO_BACK,
|
||||
MENU_QUIT,
|
||||
MENU_RESET,
|
||||
try_except,
|
||||
parse_known_args_and_warn,
|
||||
system_clear,
|
||||
check_non_negative_float,
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
|
||||
@@ -24,24 +28,31 @@ account = cfg.OANDA_ACCOUNT
|
||||
|
||||
|
||||
class OandaController:
|
||||
"""Oanda menu controller."""
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS = [
|
||||
"from",
|
||||
"to",
|
||||
"price",
|
||||
"summary",
|
||||
"list",
|
||||
"orderbook",
|
||||
"positionbook",
|
||||
"order",
|
||||
"load",
|
||||
"cancel",
|
||||
"positions",
|
||||
"closetrade",
|
||||
@@ -54,69 +65,110 @@ class OandaController:
|
||||
]
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self):
|
||||
"""Construct Data"""
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Construct Data."""
|
||||
self.fx_parser = argparse.ArgumentParser(add_help=False, prog="oanda")
|
||||
self.fx_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.instrument = None
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
|
||||
# HELP WANTED!
|
||||
# TODO:
|
||||
# We currently use the Alpha Vantage currency list for autocompletion
|
||||
# This leads to messages like `USD_EUR is not a valid instrument.`
|
||||
# In Oanda they have their own list of available instruments. It would be
|
||||
# Great to fetch these lists and store them locally like it's done for
|
||||
# other currency codes (see ./av_forex_currencies.csv and how it's handled).
|
||||
choices["to"] = {c: None for c in av_model.CURRENCY_LIST}
|
||||
choices["from"] = {c: None for c in av_model.CURRENCY_LIST}
|
||||
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
self.from_symbol = ""
|
||||
self.to_symbol = ""
|
||||
|
||||
self.instrument: Union[str, None] = None
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
print(" ?/help show this menu again")
|
||||
print(" q quit this menu and goes back to main menu")
|
||||
print(" quit quit to abandon program")
|
||||
print(" reset reset terminal and reload configs")
|
||||
print("")
|
||||
print(" summary shows account summary")
|
||||
print(" calendar show calendar")
|
||||
print(" list list order history")
|
||||
print(" pending get information on pending orders")
|
||||
print(" cancel cancel a pending order by ID -i order ID")
|
||||
print(" positions get open positions")
|
||||
print(" trades list open trades")
|
||||
print(" closetrade close a trade by id")
|
||||
print("")
|
||||
print(f"Loaded instrument: {self.instrument if self.instrument else ''}")
|
||||
print("")
|
||||
print(" load load an instrument to use")
|
||||
if self.instrument:
|
||||
print(" candles show candles")
|
||||
print(" price shows price for selected instrument")
|
||||
print(" order place limit order -u # of units -p price")
|
||||
print(" orderbook print orderbook")
|
||||
print(" positionbook print positionbook")
|
||||
print("")
|
||||
"""Print help."""
|
||||
dim_if_no_ticker = Style.DIM if not self.instrument else ""
|
||||
reset_style_if_no_ticker = Style.RESET_ALL if not self.instrument else ""
|
||||
|
||||
help_text = f"""
|
||||
summary shows account summary
|
||||
calendar show calendar
|
||||
list list order history
|
||||
pending get information on pending orders
|
||||
cancel cancel a pending order by ID -i order ID
|
||||
positions get open positions
|
||||
trades list open trades
|
||||
closetrade close a trade by id
|
||||
|
||||
Loaded instrument: {self.instrument if self.instrument else ""}
|
||||
|
||||
from select the "from" currency in a forex pair
|
||||
to select the "to" currency in a forex pair
|
||||
{dim_if_no_ticker}
|
||||
candles show candles
|
||||
price shows price for selected instrument
|
||||
order place limit order -u # of units -p price
|
||||
orderbook print orderbook
|
||||
positionbook print positionbook
|
||||
{reset_style_if_no_ticker}
|
||||
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
"""Process and dispatch input.
|
||||
|
||||
Returns
|
||||
-------
|
||||
MENU_GO_BACK, MENU_QUIT, MENU_RESET
|
||||
MENU_GO_BACK - Show main context menu again
|
||||
MENU_QUIT - Quit terminal
|
||||
MENU_RESET - Reset terminal and go back to same previous menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.fx_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
os.system("cls||clear")
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -124,100 +176,541 @@ class OandaController:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help Command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command."""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return MENU_GO_BACK
|
||||
def call_home(self, _):
|
||||
"""Process home command."""
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command."""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - exit the program"""
|
||||
return MENU_QUIT
|
||||
"""Process quit menu command."""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command."""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process Reset command - reset the program"""
|
||||
return MENU_RESET
|
||||
"""Process reset command."""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "oanda")
|
||||
self.queue.insert(0, "forex")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "reset", "forex", "oanda"]
|
||||
|
||||
# COMMANDS
|
||||
@try_except
|
||||
def call_to(self, other_args: List[str]):
|
||||
"""Process 'to' command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="to",
|
||||
description='Select the "to" currency symbol in a forex pair',
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--name",
|
||||
help="To currency",
|
||||
type=av_model.check_valid_forex_currency,
|
||||
dest="to_symbol",
|
||||
)
|
||||
|
||||
if (
|
||||
other_args
|
||||
and "-n" not in other_args[0]
|
||||
and "--name" not in other_args[0]
|
||||
and "-h" not in other_args
|
||||
):
|
||||
other_args.insert(0, "-n")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
self.to_symbol = ns_parser.to_symbol.upper()
|
||||
self.instrument = f"{self.from_symbol}_{self.to_symbol}"
|
||||
|
||||
print(
|
||||
f"\nSelected pair\nFrom: {self.from_symbol}\nTo: {self.to_symbol}\n\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_from(self, other_args: List[str]):
|
||||
"""Process 'from' command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="to",
|
||||
description='Select the "from" currency symbol in a forex pair',
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--name",
|
||||
help="From currency",
|
||||
type=av_model.check_valid_forex_currency,
|
||||
dest="from_symbol",
|
||||
)
|
||||
|
||||
if (
|
||||
other_args
|
||||
and "-n" not in other_args[0]
|
||||
and "--name" not in other_args[0]
|
||||
and "-h" not in other_args
|
||||
):
|
||||
other_args.insert(0, "-n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
self.from_symbol = ns_parser.from_symbol.upper()
|
||||
self.instrument = f"{self.from_symbol}_{self.to_symbol}"
|
||||
|
||||
print(
|
||||
f"\nSelected pair\nFrom: {self.from_symbol}\nTo: {self.to_symbol}\n\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_price(self, other_args):
|
||||
"""Process Price Command"""
|
||||
oanda_view.get_fx_price(account, self.instrument, other_args)
|
||||
|
||||
def call_load(self, other_args):
|
||||
self.instrument = oanda_view.load(other_args)
|
||||
"""Process Price Command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="price",
|
||||
description="Get price for selected instrument.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
oanda_view.get_fx_price(account, self.instrument)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_summary(self, other_args):
|
||||
"""Process account summary command"""
|
||||
oanda_view.get_account_summary(account, other_args)
|
||||
"""Process account summary command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="summary",
|
||||
description="Print some information about your account.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
oanda_view.get_account_summary(account)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_orderbook(self, other_args):
|
||||
"""Process Oanda Order Book"""
|
||||
oanda_view.get_order_book(self.instrument, other_args)
|
||||
"""Process Oanda Order Book."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="orderbook",
|
||||
description="Plot an orderbook for an instrument if Oanda provides one.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
oanda_view.get_order_book(account, self.instrument)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_positionbook(self, other_args):
|
||||
"""Process Oanda Position Book"""
|
||||
oanda_view.get_position_book(self.instrument, other_args)
|
||||
"""Process Oanda Position Book."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="positionbook",
|
||||
description="Plot a position book for an instrument if Oanda provides one.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
oanda_view.get_position_book(account, self.instrument)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_list(self, other_args: List[str]):
|
||||
"""Process list orders command"""
|
||||
oanda_view.list_orders(account, other_args)
|
||||
"""Process list orders command."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="list",
|
||||
description="List order history",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--state",
|
||||
dest="state",
|
||||
action="store",
|
||||
default="ALL",
|
||||
type=str,
|
||||
required=False,
|
||||
help="List orders that have a specific state.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
action="store",
|
||||
default=20,
|
||||
type=int,
|
||||
required=False,
|
||||
help="Limit the number of orders to retrieve.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
order_state = ns_parser.state.upper()
|
||||
order_count = ns_parser.limit
|
||||
oanda_view.list_orders(account, order_state, order_count)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_order(self, other_args: List[str]):
|
||||
"""Place limit order"""
|
||||
oanda_view.create_order(account, self.instrument, other_args)
|
||||
"""Place limit order."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="order",
|
||||
description="Create order",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-u",
|
||||
"--unit",
|
||||
dest="units",
|
||||
action="store",
|
||||
type=int,
|
||||
required="-h" not in other_args,
|
||||
help="The number of units to place in the order request. Positive for "
|
||||
+ "a long position and negative for a short position.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--price",
|
||||
dest="price",
|
||||
action="store",
|
||||
type=check_non_negative_float,
|
||||
required="-h" not in other_args,
|
||||
help="The price to set for the limit order.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
price = ns_parser.price
|
||||
units = ns_parser.units
|
||||
oanda_view.create_order(account, self.instrument, price, units)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_cancel(self, other_args: List[str]):
|
||||
"""Cancel pending order by ID"""
|
||||
oanda_view.cancel_pending_order(account, other_args)
|
||||
"""Cancel pending order by ID."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="cancel",
|
||||
description="Cancel a Pending Order.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--id",
|
||||
dest="orderID",
|
||||
action="store",
|
||||
required="-h" not in other_args,
|
||||
type=str,
|
||||
help="The pending order ID to cancel.",
|
||||
)
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-i")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
orderID = ns_parser.orderID
|
||||
oanda_view.cancel_pending_order(account, orderID)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_positions(self, other_args):
|
||||
"""Get Open Positions"""
|
||||
oanda_view.get_open_positions(account, other_args)
|
||||
"""Get Open Positions."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="positions",
|
||||
description="Get information about open positions.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
oanda_view.get_open_positions(account)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_pending(self, other_args):
|
||||
"""See up to 25 pending orders"""
|
||||
oanda_view.get_pending_orders(account, other_args)
|
||||
|
||||
def call_closetrade(self, other_args: List[str]):
|
||||
"""Close a trade by id"""
|
||||
oanda_view.close_trade(account, other_args)
|
||||
|
||||
def call_candles(self, other_args: List[str]):
|
||||
oanda_view.show_candles(account, self.instrument, other_args)
|
||||
"""See up to 25 pending orders."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="pending",
|
||||
description="Get information about pending orders.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
oanda_view.get_pending_orders(account)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_trades(self, other_args):
|
||||
"""List open trades"""
|
||||
oanda_view.get_open_trades(account, other_args)
|
||||
"""List open trades."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="trades",
|
||||
description="Get information about open trades.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
oanda_view.get_open_trades(account)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_closetrade(self, other_args: List[str]):
|
||||
"""Close a trade by id."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="closetrade",
|
||||
description="Close a trade by id.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-i",
|
||||
"--id",
|
||||
dest="orderID",
|
||||
action="store",
|
||||
type=str,
|
||||
required=False,
|
||||
help="The Trade ID to close. ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-u",
|
||||
"--units",
|
||||
dest="units",
|
||||
action="store",
|
||||
required=False,
|
||||
help="The number of units on the trade to close. If not set it "
|
||||
+ "defaults to all units. ",
|
||||
)
|
||||
if other_args:
|
||||
if "-i" not in other_args[0]:
|
||||
other_args.insert(0, "-i")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
orderID = ns_parser.orderID
|
||||
units = ns_parser.units
|
||||
oanda_view.close_trade(account, orderID, units)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_candles(self, other_args: List[str]):
|
||||
"""Plot candle chart for a loaded currency pair."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="candles",
|
||||
description="Display Candle Data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-g",
|
||||
"--granularity",
|
||||
dest="granularity",
|
||||
action="store",
|
||||
type=str,
|
||||
default="D",
|
||||
required=False,
|
||||
help="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",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="candlecount",
|
||||
action="store",
|
||||
default=180,
|
||||
type=int,
|
||||
required=False,
|
||||
help="The number of candles to retrieve. Default:180 ",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--ad",
|
||||
dest="ad",
|
||||
action="store_true",
|
||||
help="Adds ad (Accumulation/Distribution Indicator) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-b",
|
||||
"--bbands",
|
||||
dest="bbands",
|
||||
action="store_true",
|
||||
help="Adds Bollinger Bands to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-c",
|
||||
"--cci",
|
||||
dest="cci",
|
||||
action="store_true",
|
||||
help="Adds cci (Commodity Channel Index) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-e",
|
||||
"--ema",
|
||||
dest="ema",
|
||||
action="store_true",
|
||||
help="Adds ema (Exponential Moving Average) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-o",
|
||||
"--obv",
|
||||
dest="obv",
|
||||
action="store_true",
|
||||
help="Adds obv (On Balance Volume) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--rsi",
|
||||
dest="rsi",
|
||||
action="store_true",
|
||||
help="Adds rsi (Relative Strength Index) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
"--sma",
|
||||
dest="sma",
|
||||
action="store_true",
|
||||
help="Adds sma (Simple Moving Average) to the chart",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
"--vwap",
|
||||
dest="vwap",
|
||||
action="store_true",
|
||||
help="Adds vwap (Volume Weighted Average Price) to the chart",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
oanda_view.show_candles(
|
||||
self.instrument,
|
||||
granularity=ns_parser.granularity.upper(),
|
||||
candlecount=ns_parser.candlecount,
|
||||
additional_charts={
|
||||
"ad": ns_parser.ad,
|
||||
"bbands": ns_parser.bbands,
|
||||
"cci": ns_parser.cci,
|
||||
"ema": ns_parser.ema,
|
||||
"obv": ns_parser.obv,
|
||||
"rsi": ns_parser.rsi,
|
||||
"sma": ns_parser.sma,
|
||||
"vwap": ns_parser.vwap,
|
||||
},
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_calendar(self, other_args: List[str]):
|
||||
"""Call calendar"""
|
||||
oanda_view.calendar(self.instrument, other_args)
|
||||
"""Call calendar."""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="calendar",
|
||||
description="Show Calendar Data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-d",
|
||||
"--days",
|
||||
dest="days",
|
||||
action="store",
|
||||
type=int,
|
||||
default=7,
|
||||
required=False,
|
||||
help="The number of days to search for, up to 30 forward or backward "
|
||||
+ "use negative numbers to search back. ",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
days = ns_parser.days
|
||||
oanda_view.calendar(self.instrument, days)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
"""Forex Menu"""
|
||||
oanda_controller = OandaController()
|
||||
oanda_controller.call_help(None)
|
||||
def menu(queue: List[str] = None):
|
||||
"""Oanda Forex Menu."""
|
||||
oanda_controller = OandaController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in oanda_controller.CHOICES}
|
||||
)
|
||||
# There is a command in the queue
|
||||
if oanda_controller.queue and len(oanda_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if oanda_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(oanda_controller.queue) > 1:
|
||||
return oanda_controller.queue[1:]
|
||||
return []
|
||||
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (forex)(oanda)> ",
|
||||
completer=completer,
|
||||
)
|
||||
# Consume 1 element from the queue
|
||||
an_input = oanda_controller.queue[0]
|
||||
oanda_controller.queue = oanda_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in oanda_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /forex/oanda/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (forex)(oanda)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
oanda_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and oanda_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /forex/oanda/ $ ",
|
||||
completer=oanda_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /forex/oanda/ $ ")
|
||||
|
||||
try:
|
||||
process_input = oanda_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
oanda_controller.queue = oanda_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exit\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /forex/oanda menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
oanda_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
oanda_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
oanda_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -1,2 +1,642 @@
|
||||
"""Oanda Model"""
|
||||
"""Oanda Model."""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import json
|
||||
from datetime import datetime
|
||||
from typing import Dict, Union
|
||||
import pandas as pd
|
||||
|
||||
from oandapyV20 import API
|
||||
from oandapyV20.endpoints import (
|
||||
accounts,
|
||||
forexlabs,
|
||||
instruments,
|
||||
orders,
|
||||
positions,
|
||||
pricing,
|
||||
trades,
|
||||
)
|
||||
from oandapyV20.exceptions import V20Error
|
||||
|
||||
from gamestonk_terminal import config_terminal as cfg
|
||||
|
||||
client = API(access_token=cfg.OANDA_TOKEN, environment=cfg.OANDA_ACCOUNT_TYPE)
|
||||
account = cfg.OANDA_ACCOUNT
|
||||
|
||||
|
||||
def fx_price_request(
|
||||
accountID: str = account, instrument: Union[str, None] = None
|
||||
) -> Union[Dict[str, str], bool]:
|
||||
"""Request price for a forex pair.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
instrument : Union[str, None]
|
||||
The loaded currency pair, by default None
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[Dict[str, str], bool]
|
||||
The currency pair price or False
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
if instrument is None:
|
||||
print("Error: An instrument should be loaded before running this command.")
|
||||
return False
|
||||
try:
|
||||
parameters = {"instruments": instrument}
|
||||
request = pricing.PricingInfo(accountID=accountID, params=parameters)
|
||||
response = client.request(request)
|
||||
return response
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def account_summary_request(accountID: str = account) -> Union[pd.DataFrame, bool]:
|
||||
"""Request Oanda account summary.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Account summary data or False
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
try:
|
||||
request = accounts.AccountSummary(accountID=accountID)
|
||||
response = client.request(request)
|
||||
df_summary = pd.DataFrame(
|
||||
[
|
||||
{"Type": "Balance", "Value": response["account"]["balance"]},
|
||||
{"Type": "NAV", "Value": response["account"]["NAV"]},
|
||||
{
|
||||
"Type": "Unrealized P/L",
|
||||
"Value": response["account"]["unrealizedPL"],
|
||||
},
|
||||
{"Type": "Total P/L", "Value": response["account"]["pl"]},
|
||||
{
|
||||
"Type": "Open Trade Count",
|
||||
"Value": response["account"]["openTradeCount"],
|
||||
},
|
||||
{
|
||||
"Type": "Margin Available",
|
||||
"Value": response["account"]["marginAvailable"],
|
||||
},
|
||||
{"Type": "Margin Used", "Value": response["account"]["marginUsed"]},
|
||||
{
|
||||
"Type": "Margin Closeout",
|
||||
"Value": response["account"]["marginCloseoutNAV"],
|
||||
},
|
||||
{
|
||||
"Type": "Margin Closeout Percent",
|
||||
"Value": response["account"]["marginCloseoutPercent"],
|
||||
},
|
||||
{
|
||||
"Type": "Margin Closeout Position Value",
|
||||
"Value": response["account"]["marginCloseoutPositionValue"],
|
||||
},
|
||||
]
|
||||
)
|
||||
return df_summary
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def orderbook_plot_data_request(
|
||||
instrument: Union[str, None] = None, accountID: str = account
|
||||
) -> Union[pd.DataFrame, bool]:
|
||||
"""Request order book data for plotting.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
instrument : Union[str, None]
|
||||
The loaded currency pair, by default None
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Order book data or False
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
if instrument is None:
|
||||
print("Error: An instrument should be loaded before running this command.")
|
||||
return False
|
||||
parameters = {"bucketWidth": "1"}
|
||||
try:
|
||||
request = instruments.InstrumentsOrderBook(
|
||||
instrument=instrument, params=parameters
|
||||
)
|
||||
response = client.request(request)
|
||||
df_orderbook_data = pd.DataFrame.from_dict(response["orderBook"]["buckets"])
|
||||
return df_orderbook_data
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def positionbook_plot_data_request(
|
||||
instrument: Union[str, None] = None, accountID: str = account
|
||||
) -> Union[pd.DataFrame, bool]:
|
||||
"""Request position book data for plotting.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
instrument : Union[str, None]
|
||||
The loaded currency pair, by default None
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Position book data or False
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
if instrument is None:
|
||||
print("Error: An instrument should be loaded before running this command.")
|
||||
return False
|
||||
try:
|
||||
request = instruments.InstrumentsPositionBook(instrument=instrument)
|
||||
response = client.request(request)
|
||||
df_positionbook_data = pd.DataFrame.from_dict(
|
||||
response["positionBook"]["buckets"]
|
||||
)
|
||||
return df_positionbook_data
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def order_history_request(
|
||||
order_state: str, order_count: int, accountID: str = account
|
||||
) -> Union[pd.DataFrame, bool]:
|
||||
"""Request the orders list from Oanda.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
order_state : str
|
||||
Filter orders by a specific state ("PENDING", "CANCELLED", etc.)
|
||||
order_count : int
|
||||
Limit the number of orders to retrieve
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
parameters: Dict[str, Union[str, int]] = {}
|
||||
parameters["state"] = order_state
|
||||
parameters["count"] = order_count
|
||||
|
||||
try:
|
||||
request = orders.OrderList(accountID, parameters)
|
||||
response = client.request(request)
|
||||
|
||||
df_order_list = pd.DataFrame.from_dict(response["orders"])
|
||||
df_order_list = df_order_list[
|
||||
["id", "instrument", "units", "price", "state", "type"]
|
||||
]
|
||||
return df_order_list
|
||||
except KeyError:
|
||||
print("No orders were found\n")
|
||||
return False
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def create_order_request(
|
||||
price: int,
|
||||
units: int,
|
||||
instrument: Union[str, None] = None,
|
||||
accountID: str = account,
|
||||
) -> Union[pd.DataFrame, bool]:
|
||||
"""Request creation of buy/sell trade order.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
instrument : Union[str, None]
|
||||
The loaded currency pair, by default None
|
||||
price : int
|
||||
The price to set for the limit order.
|
||||
units : int
|
||||
The number of units to place in the order request.
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Orders data or False
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
if instrument is None:
|
||||
print("Error: An instrument should be loaded before running this command.")
|
||||
return False
|
||||
if "JPY" in instrument or "THB" in instrument or "HUF" in instrument:
|
||||
price = round(price, 3)
|
||||
else:
|
||||
price = round(price, 5)
|
||||
data = {
|
||||
"order": {
|
||||
"price": price,
|
||||
"instrument": instrument,
|
||||
"units": units,
|
||||
"type": "LIMIT",
|
||||
"timeInForce": "GTC",
|
||||
"positionFill": "DEFAULT",
|
||||
}
|
||||
}
|
||||
try:
|
||||
request = orders.OrderCreate(accountID, data)
|
||||
response = client.request(request)
|
||||
order_data = []
|
||||
order_data.append(
|
||||
{
|
||||
"Order ID": response["orderCreateTransaction"]["id"],
|
||||
"Instrument": response["orderCreateTransaction"]["instrument"],
|
||||
"Price": response["orderCreateTransaction"]["price"],
|
||||
"Units": response["orderCreateTransaction"]["units"],
|
||||
}
|
||||
)
|
||||
df_orders = pd.DataFrame.from_dict(order_data)
|
||||
return df_orders
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
|
||||
def cancel_pending_order_request(
|
||||
orderID: str, accountID: str = account
|
||||
) -> Union[str, bool]:
|
||||
"""Request cancellation of a pending order.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
orderID : str
|
||||
The pending order ID to cancel.
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
try:
|
||||
request = orders.OrderCancel(accountID, orderID)
|
||||
response = client.request(request)
|
||||
order_id = response["orderCancelTransaction"]["orderID"]
|
||||
return order_id
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def open_positions_request(accountID: str = account) -> Union[pd.DataFrame, bool]:
|
||||
"""Request information on open positions.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
try:
|
||||
request = positions.OpenPositions(accountID)
|
||||
response = client.request(request)
|
||||
position_data = []
|
||||
for i in range(len(response["positions"])):
|
||||
position_data.append(
|
||||
{
|
||||
"Instrument": response["positions"][i]["instrument"],
|
||||
"Long Units": response["positions"][i]["long"]["units"],
|
||||
"Total Long P/L": response["positions"][i]["long"]["units"],
|
||||
"Unrealized Long P/L": response["positions"][i]["long"][
|
||||
"unrealizedPL"
|
||||
],
|
||||
"Short Units": response["positions"][i]["short"]["units"],
|
||||
"Total Short P/L": response["positions"][i]["short"]["pl"],
|
||||
"Short Unrealized P/L": response["positions"][i]["short"][
|
||||
"unrealizedPL"
|
||||
],
|
||||
}
|
||||
)
|
||||
df_positions = pd.DataFrame.from_dict(position_data)
|
||||
return df_positions
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def pending_orders_request(accountID: str = account) -> Union[pd.DataFrame, bool]:
|
||||
"""Request information on pending orders.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Pending orders data or False
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
try:
|
||||
request = orders.OrdersPending(accountID)
|
||||
response = client.request(request)
|
||||
pending_data = []
|
||||
for i in range(len(response["orders"])):
|
||||
pending_data.append(
|
||||
{
|
||||
"Order ID": response["orders"][i]["id"],
|
||||
"Instrument": response["orders"][i]["instrument"],
|
||||
"Price": response["orders"][i]["price"],
|
||||
"Units": response["orders"][i]["units"],
|
||||
"Time Created": response["orders"][i]["createTime"][:10]
|
||||
+ " "
|
||||
+ response["orders"][i]["createTime"][11:19],
|
||||
"Time In Force": response["orders"][i]["timeInForce"],
|
||||
}
|
||||
)
|
||||
if len(pending_data) == 0:
|
||||
return pd.DataFrame()
|
||||
df_pending = pd.DataFrame.from_dict(pending_data)
|
||||
return df_pending
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def open_trades_request(accountID: str = account) -> Union[pd.DataFrame, bool]:
|
||||
"""Request open trades data.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Open trades data or False
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
try:
|
||||
request = trades.OpenTrades(accountID)
|
||||
response = client.request(request)
|
||||
if "trades" in response and len(response["trades"]) > 0:
|
||||
df_trades = pd.DataFrame.from_dict(response["trades"])
|
||||
df_trades = df_trades[
|
||||
[
|
||||
"id",
|
||||
"instrument",
|
||||
"initialUnits",
|
||||
"currentUnits",
|
||||
"price",
|
||||
"unrealizedPL",
|
||||
]
|
||||
]
|
||||
df_trades = df_trades.rename(
|
||||
columns={
|
||||
"id": "ID",
|
||||
"instrument": "Instrument",
|
||||
"initialUnits": "Initial Units",
|
||||
"currentUnits": "Current Units",
|
||||
"price": "Entry Price",
|
||||
"unrealizedPL": "Unrealized P/L",
|
||||
}
|
||||
)
|
||||
else:
|
||||
df_trades = pd.DataFrame()
|
||||
return df_trades
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def close_trades_request(
|
||||
orderID: str, units: Union[int, None], accountID: str = account
|
||||
) -> Union[pd.DataFrame, bool]:
|
||||
"""Close a trade.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
orderID : str
|
||||
ID of the order to close
|
||||
units : Union[int, None]
|
||||
Number of units to close. If empty default to all.
|
||||
accountID : str, optional
|
||||
Oanda account ID, by default cfg.OANDA_ACCOUNT
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Close trades data or False
|
||||
"""
|
||||
if accountID == "REPLACE_ME":
|
||||
print("Error: Oanda account credentials are required.")
|
||||
return False
|
||||
data = {}
|
||||
if units is not None:
|
||||
data["units"] = units
|
||||
try:
|
||||
request = trades.TradeClose(accountID, orderID, data)
|
||||
response = client.request(request)
|
||||
|
||||
close_data = []
|
||||
close_data.append(
|
||||
{
|
||||
"OrderID": response["orderCreateTransaction"]["tradeClose"]["tradeID"],
|
||||
"Instrument": response["orderFillTransaction"]["instrument"],
|
||||
"Units": response["orderCreateTransaction"]["units"],
|
||||
"Price": response["orderFillTransaction"]["price"],
|
||||
"P/L": response["orderFillTransaction"]["pl"],
|
||||
}
|
||||
)
|
||||
if len(close_data) == 0:
|
||||
return pd.DataFrame()
|
||||
df_trades = pd.DataFrame.from_dict(close_data)
|
||||
return df_trades
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def get_candles_dataframe(
|
||||
instrument: Union[str, None] = None, granularity: str = "D", candlecount: int = 180
|
||||
) -> Union[pd.DataFrame, bool]:
|
||||
"""Request data for candle chart.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
instrument : str
|
||||
Loaded currency pair code
|
||||
granularity : str, optional
|
||||
Data granularity, by default "D"
|
||||
candlecount : int, optional
|
||||
Limit for the number of data points, by default 180
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Candle chart data or False
|
||||
"""
|
||||
if instrument is None:
|
||||
print("Error: An instrument should be loaded before running this command.")
|
||||
return False
|
||||
parameters = {
|
||||
"granularity": granularity,
|
||||
"count": candlecount,
|
||||
}
|
||||
try:
|
||||
request = instruments.InstrumentsCandles(instrument, params=parameters)
|
||||
response = client.request(request)
|
||||
candles_data = []
|
||||
for i in range(len(response["candles"])):
|
||||
candles_data.append(
|
||||
{
|
||||
"Date": response["candles"][i]["time"][:10]
|
||||
+ " "
|
||||
+ response["candles"][i]["time"][11:19],
|
||||
"Open": float(response["candles"][i]["mid"]["o"]),
|
||||
"High": float(response["candles"][i]["mid"]["h"]),
|
||||
"Low": float(response["candles"][i]["mid"]["l"]),
|
||||
"Close": float(response["candles"][i]["mid"]["c"]),
|
||||
"Volume": response["candles"][i]["volume"],
|
||||
}
|
||||
)
|
||||
if len(candles_data) == 0:
|
||||
df_candles = pd.DataFrame()
|
||||
else:
|
||||
df_candles = pd.DataFrame(candles_data)
|
||||
df_candles.set_index("Date", inplace=True)
|
||||
df_candles.index = pd.to_datetime(df_candles.index)
|
||||
return df_candles
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["errorMessage"], "\n")
|
||||
return False
|
||||
|
||||
|
||||
def get_calendar_request(
|
||||
days: int, instrument: Union[str, None] = None
|
||||
) -> Union[pd.DataFrame, bool]:
|
||||
"""Request data of significant events calendar.
|
||||
|
||||
Parameters
|
||||
----------
|
||||
instrument : Union[str, None]
|
||||
The loaded currency pair, by default None
|
||||
days : int
|
||||
Number of days in advance
|
||||
|
||||
Returns
|
||||
-------
|
||||
Union[pd.DataFrame, bool]
|
||||
Calendar events data or False
|
||||
"""
|
||||
if instrument is None:
|
||||
print("Error: An instrument should be loaded before running this command.")
|
||||
return False
|
||||
parameters = {"instrument": instrument, "period": str(days * 86400 * -1)}
|
||||
try:
|
||||
request = forexlabs.Calendar(params=parameters)
|
||||
response = client.request(request)
|
||||
|
||||
l_data = []
|
||||
for i in enumerate(response):
|
||||
if "forecast" in response[i[0]]:
|
||||
forecast = response[i[0]]["forecast"]
|
||||
if response[i[0]]["unit"] != "Index":
|
||||
forecast += response[i[0]]["unit"]
|
||||
else:
|
||||
forecast = ""
|
||||
|
||||
if "market" in response[i[0]]:
|
||||
market = response[i[0]]["market"]
|
||||
if response[i[0]]["unit"] != "Index":
|
||||
market += response[i[0]]["unit"]
|
||||
else:
|
||||
market = ""
|
||||
|
||||
if "actual" in response[i[0]]:
|
||||
actual = response[i[0]]["actual"]
|
||||
if response[i[0]]["unit"] != "Index":
|
||||
actual += response[i[0]]["unit"]
|
||||
else:
|
||||
actual = ""
|
||||
|
||||
if "previous" in response[i[0]]:
|
||||
previous = response[i[0]]["previous"]
|
||||
if response[i[0]]["unit"] != "Index":
|
||||
previous += response[i[0]]["unit"]
|
||||
else:
|
||||
previous = ""
|
||||
|
||||
if "impact" in response[i[0]]:
|
||||
impact = response[i[0]]["impact"]
|
||||
else:
|
||||
impact = ""
|
||||
|
||||
l_data.append(
|
||||
{
|
||||
"Title": response[i[0]]["title"],
|
||||
"Time": datetime.fromtimestamp(response[i[0]]["timestamp"]),
|
||||
"Impact": impact,
|
||||
"Forecast": forecast,
|
||||
"Market Forecast": market,
|
||||
"Currency": response[i[0]]["currency"],
|
||||
"Region": response[i[0]]["region"],
|
||||
"Actual": actual,
|
||||
"Previous": previous,
|
||||
}
|
||||
)
|
||||
if len(l_data) == 0:
|
||||
df_calendar = pd.DataFrame()
|
||||
else:
|
||||
df_calendar = pd.DataFrame(l_data)
|
||||
return df_calendar
|
||||
except V20Error as e:
|
||||
d_error = json.loads(e.msg)
|
||||
print(d_error["message"], "\n")
|
||||
return False
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -97,6 +97,14 @@ def check_non_negative(value) -> int:
|
||||
return new_value
|
||||
|
||||
|
||||
def check_non_negative_float(value) -> float:
|
||||
"""Argparse type to check non negative int"""
|
||||
new_value = float(value)
|
||||
if new_value < 0:
|
||||
raise argparse.ArgumentTypeError(f"{value} is negative")
|
||||
return new_value
|
||||
|
||||
|
||||
def check_positive_list(value) -> List[int]:
|
||||
"""Argparse type to return list of positive ints"""
|
||||
list_of_nums = value.split(",")
|
||||
@@ -570,7 +578,12 @@ def parse_known_args_and_warn(
|
||||
if gtff.USE_CLEAR_AFTER_CMD:
|
||||
system_clear()
|
||||
|
||||
(ns_parser, l_unknown_args) = parser.parse_known_args(other_args)
|
||||
try:
|
||||
(ns_parser, l_unknown_args) = parser.parse_known_args(other_args)
|
||||
except SystemExit:
|
||||
# In case the command has required argument that isn't specified
|
||||
print("")
|
||||
return None
|
||||
|
||||
if ns_parser.help:
|
||||
parser.print_help()
|
||||
@@ -789,6 +802,7 @@ def try_except(f):
|
||||
return f(*args, **kwargs)
|
||||
except Exception as e:
|
||||
logger.exception("%s", type(e).__name__)
|
||||
return []
|
||||
|
||||
return inner
|
||||
|
||||
|
||||
@@ -2,8 +2,10 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
import difflib
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.portfolio.brokers.ally import ally_view
|
||||
@@ -12,39 +14,55 @@ from gamestonk_terminal.helper_funcs import (
|
||||
parse_known_args_and_warn,
|
||||
try_except,
|
||||
system_clear,
|
||||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||||
)
|
||||
|
||||
|
||||
class AllyController:
|
||||
|
||||
CHOICES = [
|
||||
"?",
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
ALLY_CHOICES = ["holdings", "history", "balances"]
|
||||
ALLY_STOCK_CHOICES = ["quote", "movers"]
|
||||
CHOICES_COMMANDS = ["holdings", "history", "balances", "quote", "movers"]
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
list_choices = [
|
||||
"toplosers",
|
||||
"toppctlosers",
|
||||
"topvolume",
|
||||
"topactive",
|
||||
"topgainers",
|
||||
"toppctgainers",
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""CONSTRUCTOR"""
|
||||
|
||||
self._ally_parser = argparse.ArgumentParser(add_help=False, prog="ally")
|
||||
self.CHOICES.extend(self.ALLY_CHOICES)
|
||||
self.CHOICES.extend(self.ALLY_STOCK_CHOICES)
|
||||
self._ally_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["movers"]["-t"] = {c: None for c in self.list_choices}
|
||||
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
self.queue = queue or list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = """
|
||||
Ally:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
|
||||
holdings show account holdings
|
||||
history show history of your account
|
||||
balances show balance details of account
|
||||
@@ -61,28 +79,40 @@ Stock Information:
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self._ally_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -90,17 +120,53 @@ Stock Information:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu."""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program."""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "ally")
|
||||
self.queue.insert(0, "bro")
|
||||
self.queue.insert(0, "portfolio")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "reset", "portfolio", "bro", "ally"]
|
||||
|
||||
@try_except
|
||||
def call_holdings(self, other_args: List[str]):
|
||||
@@ -111,18 +177,12 @@ Stock Information:
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
description="Display info about your trading accounts on Ally",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
ally_view.display_holdings(export=ns_parser.export)
|
||||
if ns_parser:
|
||||
ally_view.display_holdings(export=ns_parser.export)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_history(self, other_args: List[str]):
|
||||
@@ -134,25 +194,21 @@ Stock Information:
|
||||
description="""Account transaction history""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
dest="num",
|
||||
"-l",
|
||||
"--limit",
|
||||
dest="limit",
|
||||
default=15,
|
||||
type=int,
|
||||
help="Number of recent transactions to show",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
ally_view.display_history(n_to_show=ns_parser.num, export=ns_parser.export)
|
||||
if ns_parser:
|
||||
ally_view.display_history(
|
||||
n_to_show=ns_parser.limit, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_balances(self, other_args: List[str]):
|
||||
@@ -163,18 +219,12 @@ Stock Information:
|
||||
prog="balances",
|
||||
description="""Account balance details""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
ally_view.display_balances(export=ns_parser.export)
|
||||
if ns_parser:
|
||||
ally_view.display_balances(export=ns_parser.export)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_quote(self, other_args: List[str]):
|
||||
@@ -185,7 +235,7 @@ Stock Information:
|
||||
prog="quote",
|
||||
description="""Get stock quote""",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-t")
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
@@ -196,9 +246,9 @@ Stock Information:
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
ally_view.display_stock_quote(ns_parser.ticker)
|
||||
if ns_parser:
|
||||
ally_view.display_stock_quote(ns_parser.ticker)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_movers(self, other_args: List[str]):
|
||||
@@ -210,17 +260,10 @@ Stock Information:
|
||||
description="""Get stock movers""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--list",
|
||||
"-t",
|
||||
"--type",
|
||||
help="List to get movers of",
|
||||
choices=[
|
||||
"toplosers",
|
||||
"toppctlosers",
|
||||
"topvolume",
|
||||
"topactive",
|
||||
"topgainers",
|
||||
"toppctgainers",
|
||||
],
|
||||
choices=self.list_choices,
|
||||
default="topactive",
|
||||
dest="list_type",
|
||||
)
|
||||
@@ -238,51 +281,93 @@ Stock Information:
|
||||
dest="exchange",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n", "--num", help="Number to show", type=int, default=15, dest="num"
|
||||
"-l", "--limit", help="Number to show", type=int, default=15, dest="limit"
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
ally_view.display_top_lists(
|
||||
list_type=ns_parser.list_type,
|
||||
exchange=ns_parser.exchange,
|
||||
num_to_show=ns_parser.num,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
|
||||
ally_view.display_top_lists(
|
||||
list_type=ns_parser.list_type,
|
||||
exchange=ns_parser.exchange,
|
||||
num_to_show=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
|
||||
ally_controller = AllyController()
|
||||
ally_controller.print_help()
|
||||
def menu(queue: List[str] = None):
|
||||
"""Ally Menu"""
|
||||
ally_controller = AllyController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if ally_controller.queue and len(ally_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if ally_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(ally_controller.queue) > 1:
|
||||
return ally_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = ally_controller.queue[0]
|
||||
ally_controller.queue = ally_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in ally_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /portfolio/bro/ally/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in ally_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (bro)>(ally)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (bro)>(ally)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
ally_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and ally_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /portfolio/bro/ally/ $ ",
|
||||
completer=ally_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /portfolio/bro/ally/ $ ")
|
||||
|
||||
try:
|
||||
process_input = ally_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
ally_controller.queue = ally_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /portfolio/bro/ally menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
ally_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
ally_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
ally_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -3,8 +3,11 @@ __docformat__ = "numpy"
|
||||
# pylint: disable=R1710
|
||||
|
||||
import argparse
|
||||
from typing import List, Union, Set
|
||||
import difflib
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import get_flair, system_clear
|
||||
from gamestonk_terminal.menu import session
|
||||
@@ -18,26 +21,43 @@ from gamestonk_terminal.portfolio.brokers.coinbase import coinbase_controller
|
||||
class BrokersController:
|
||||
"""Brokers Controller"""
|
||||
|
||||
CHOICES = ["?", "cls", "help", "q", "quit"]
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
CHOICES_COMMANDS: List = []
|
||||
BROKERS = ["cb", "ally", "rh", "degiro"]
|
||||
|
||||
CHOICES += BROKERS
|
||||
CHOICES += BROKERS + CHOICES_COMMANDS
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
self.bro_parser = argparse.ArgumentParser(add_help=False, prog="bro")
|
||||
self.bro_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
self.broker_list = set()
|
||||
self.broker_list: Set = set()
|
||||
self.merged_holdings = None
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_string = """
|
||||
What would you like to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to >Portfolio, logs out of brokers
|
||||
quit quit to abandon program, logs out of brokers
|
||||
|
||||
Brokers:
|
||||
> ally Ally Invest Menu
|
||||
@@ -54,28 +74,40 @@ Crypto Brokers:
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.bro_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -83,41 +115,65 @@ Crypto Brokers:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process q command - quit the menu"""
|
||||
return False
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "bro")
|
||||
self.queue.insert(0, "portfolio")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "reset", "portfolio", "bro"]
|
||||
|
||||
def call_degiro(self, _):
|
||||
"""Process degiro command."""
|
||||
if degiro_controller.menu():
|
||||
return True
|
||||
print("")
|
||||
return degiro_controller.menu(self.queue)
|
||||
|
||||
def call_ally(self, _):
|
||||
"""Process degiro command."""
|
||||
if ally_controller.menu():
|
||||
return True
|
||||
print("")
|
||||
"""Process ally command."""
|
||||
return ally_controller.menu(self.queue)
|
||||
|
||||
def call_rh(self, _):
|
||||
"""Process degiro command."""
|
||||
if robinhood_controller.menu():
|
||||
return True
|
||||
print("")
|
||||
"""Process rh command."""
|
||||
return robinhood_controller.menu(self.queue)
|
||||
|
||||
def call_cb(self, _):
|
||||
"""Process degiro command."""
|
||||
if coinbase_controller.menu():
|
||||
return True
|
||||
print("")
|
||||
return coinbase_controller.menu(self.queue)
|
||||
|
||||
# TODO: Consistent way of merging across brokers including crypto
|
||||
# def call_login(self, other_args):
|
||||
@@ -145,9 +201,8 @@ Crypto Brokers:
|
||||
# print(f"{broker} not supported")
|
||||
|
||||
|
||||
def menu():
|
||||
def menu(queue: List[str] = None):
|
||||
"""Brokers Menu"""
|
||||
bro_controller = BrokersController()
|
||||
print(
|
||||
"\nUSE THIS MENU AT YOUR OWN DISCRETION\n"
|
||||
" - This menu is the only one in the entire repository that has access to your broker accounts. "
|
||||
@@ -158,27 +213,76 @@ def menu():
|
||||
"is impossible for us to check the coding standards and security of each of these modules. "
|
||||
"Hence why adding this disclaimer here."
|
||||
)
|
||||
bro_controller.print_help()
|
||||
|
||||
bro_controller = BrokersController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if bro_controller.queue and len(bro_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if bro_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(bro_controller.queue) > 1:
|
||||
return bro_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = bro_controller.queue[0]
|
||||
bro_controller.queue = bro_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in bro_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /portfolio/bro/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in bro_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (portfolio)>(bro)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (portfolio)>(bro)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
bro_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and bro_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /portfolio/bro/ $ ",
|
||||
completer=bro_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /portfolio/bro/ $ ")
|
||||
|
||||
try:
|
||||
process_input = bro_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
bro_controller.queue = bro_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /portfolio/bro menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
bro_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
bro_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
bro_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -3,6 +3,9 @@ __docformat__ = "numpy"
|
||||
|
||||
# pylint: disable=R0904, C0302, W0622
|
||||
import argparse
|
||||
from typing import List, Union
|
||||
import difflib
|
||||
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.menu import session
|
||||
@@ -15,38 +18,63 @@ from gamestonk_terminal.helper_funcs import (
|
||||
check_positive,
|
||||
try_except,
|
||||
system_clear,
|
||||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||||
)
|
||||
|
||||
|
||||
class CoinbaseController:
|
||||
|
||||
CHOICES = [
|
||||
"?",
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CB_CHOICES = ["account", "history", "orders", "deposits"]
|
||||
CHOICES_COMMANDS = ["account", "history", "orders", "deposits"]
|
||||
order_sortby = [
|
||||
"product_id",
|
||||
"side",
|
||||
"price",
|
||||
"size",
|
||||
"type",
|
||||
"created_at",
|
||||
"status",
|
||||
]
|
||||
deposit_sort = [
|
||||
"created_at",
|
||||
"amount",
|
||||
]
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""CONSTRUCTOR"""
|
||||
|
||||
self._cb_parser = argparse.ArgumentParser(add_help=False, prog="cb")
|
||||
self.CHOICES.extend(self.CB_CHOICES)
|
||||
self.CHOICES.extend(self.CHOICES_COMMANDS)
|
||||
|
||||
self._cb_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["orders"]["-s"] = {c: None for c in self.order_sortby}
|
||||
choices["orders"]["--sortby"] = {c: None for c in self.order_sortby}
|
||||
choices["deposits"]["-s"] = {c: None for c in self.deposit_sort}
|
||||
choices["deposits"]["--sortby"] = {c: None for c in self.deposit_sort}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
self.queue = queue if queue else list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = """
|
||||
Coinbase:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
|
||||
account show balance of your account
|
||||
history show history of your account
|
||||
deposits show all your deposits or internal transfers
|
||||
@@ -59,28 +87,40 @@ Coinbase:
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self._cb_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -88,17 +128,53 @@ Coinbase:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu."""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program."""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "cb")
|
||||
self.queue.insert(0, "bro")
|
||||
self.queue.insert(0, "portfolio")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "reset", "portfolio", "bro", "cb"]
|
||||
|
||||
@try_except
|
||||
def call_account(self, other_args):
|
||||
@@ -124,28 +200,21 @@ Coinbase:
|
||||
dest="currency",
|
||||
help="Currency to display value in.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
if other_args and other_args[0][0] != "-":
|
||||
other_args.insert(0, "--acc")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
coinbase_view.display_account(
|
||||
currency=ns_parser.currency,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if ns_parser:
|
||||
coinbase_view.display_account(
|
||||
currency=ns_parser.currency,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_history(self, other_args):
|
||||
"""Process account command"""
|
||||
@@ -172,27 +241,20 @@ Coinbase:
|
||||
default=20,
|
||||
type=check_positive,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
if other_args and other_args[0][0] != "-":
|
||||
other_args.insert(0, "--acc")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
coinbase_view.display_history(
|
||||
ns_parser.account, ns_parser.export, ns_parser.limit
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if ns_parser:
|
||||
coinbase_view.display_history(
|
||||
ns_parser.account, ns_parser.export, ns_parser.limit
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_orders(self, other_args):
|
||||
"""Process orders command"""
|
||||
@@ -217,15 +279,7 @@ Coinbase:
|
||||
type=str,
|
||||
help="Sort by given column. Default: created_at",
|
||||
default="created_at",
|
||||
choices=[
|
||||
"product_id",
|
||||
"side",
|
||||
"price",
|
||||
"size",
|
||||
"type",
|
||||
"created_at",
|
||||
"status",
|
||||
],
|
||||
choices=self.order_sortby,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--descend",
|
||||
@@ -234,27 +288,19 @@ Coinbase:
|
||||
dest="descend",
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
|
||||
if other_args and other_args[0][0] != "-":
|
||||
other_args.insert(0, "--acc")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
coinbase_view.display_orders(
|
||||
ns_parser.limit, ns_parser.sortby, ns_parser.descend, ns_parser.export
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
|
||||
if ns_parser:
|
||||
coinbase_view.display_orders(
|
||||
ns_parser.limit, ns_parser.sortby, ns_parser.descend, ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_deposits(self, other_args):
|
||||
"""Process deposits command"""
|
||||
@@ -288,10 +334,7 @@ Coinbase:
|
||||
type=str,
|
||||
help="Sort by given column. Default: created_at",
|
||||
default="created_at",
|
||||
choices=[
|
||||
"created_at",
|
||||
"amount",
|
||||
],
|
||||
choices=self.deposit_sort,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--descend",
|
||||
@@ -300,51 +343,91 @@ Coinbase:
|
||||
dest="descend",
|
||||
default=False,
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
coinbase_view.display_deposits(
|
||||
ns_parser.limit,
|
||||
ns_parser.sortby,
|
||||
ns_parser.type,
|
||||
ns_parser.descend,
|
||||
ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
coinbase_view.display_deposits(
|
||||
ns_parser.limit,
|
||||
ns_parser.sortby,
|
||||
ns_parser.type,
|
||||
ns_parser.descend,
|
||||
ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
|
||||
cb_controller = CoinbaseController()
|
||||
cb_controller.print_help()
|
||||
def menu(queue: List[str] = None):
|
||||
"""Coinbase Menu"""
|
||||
cb_controller = CoinbaseController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if cb_controller.queue and len(cb_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if cb_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(cb_controller.queue) > 1:
|
||||
return cb_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = cb_controller.queue[0]
|
||||
cb_controller.queue = cb_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in cb_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /portfolio/bro/cb/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in cb_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (bro)>(cb)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (bro)>(cb)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
cb_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and cb_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /portfolio/bro/cb/ $ ",
|
||||
completer=cb_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /portfolio/bro/cb/ $ ")
|
||||
|
||||
try:
|
||||
process_input = cb_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
cb_controller.queue = cb_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /portfolio/bro/cb menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
cb_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
cb_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
cb_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
# IMPORTATION STANDARD
|
||||
import argparse
|
||||
from typing import List, Union
|
||||
import difflib
|
||||
|
||||
# IMPORTATION THIRDPARTY
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
@@ -18,9 +20,20 @@ from gamestonk_terminal.portfolio.brokers.degiro.degiro_view import DegiroView
|
||||
|
||||
|
||||
class DegiroController:
|
||||
CHOICES = [
|
||||
"?",
|
||||
CHOICES = CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
CHOICES_COMMANDS = [
|
||||
"cancel",
|
||||
"companynews",
|
||||
"create",
|
||||
@@ -30,22 +43,123 @@ class DegiroController:
|
||||
"logout",
|
||||
"lookup",
|
||||
"pending",
|
||||
"q",
|
||||
"quit",
|
||||
"topnews",
|
||||
"update",
|
||||
"help",
|
||||
]
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
self.__degiro_view = DegiroView()
|
||||
self.__degiro_parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="degiro",
|
||||
)
|
||||
self.__degiro_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
def cancel(self, l_args):
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
self.queue = queue if queue else list()
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Returns
|
||||
-------
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.__degiro_parser.parse_known_args(
|
||||
an_input.split()
|
||||
)
|
||||
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
"call_" + known_args.cmd,
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
DegiroView.help_display()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "degiro")
|
||||
self.queue.insert(0, "bro")
|
||||
self.queue.insert(0, "portfolio")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "reset", "portfolio", "bro", "degiro"]
|
||||
|
||||
def cancel(self, other_args: List[str]):
|
||||
"""Cancel an order using the `id`."""
|
||||
|
||||
# PARSE ARGS
|
||||
@@ -58,11 +172,12 @@ class DegiroController:
|
||||
help="Order's id.",
|
||||
type=str,
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.cancel(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def companynews(self, l_args):
|
||||
def companynews(self, other_args: List[str]):
|
||||
"""Display news related to a company using its ISIN."""
|
||||
|
||||
# PARSE ARGS
|
||||
@@ -75,11 +190,12 @@ class DegiroController:
|
||||
type=str,
|
||||
help="ISIN code of the company.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.companynews(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def create(self, l_args):
|
||||
def create(self, other_args: List[str]):
|
||||
"""Create an order."""
|
||||
|
||||
# PARSE ARGS
|
||||
@@ -153,15 +269,12 @@ class DegiroController:
|
||||
required=False,
|
||||
type=str,
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.create(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def help(self, _):
|
||||
"""Show the help menu."""
|
||||
DegiroView.help_display()
|
||||
|
||||
def hold(self, l_args):
|
||||
def hold(self, other_args):
|
||||
"""Display held products."""
|
||||
|
||||
# PARSE ARGS
|
||||
@@ -169,11 +282,12 @@ class DegiroController:
|
||||
add_help=False,
|
||||
prog="hold",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.hold(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def lastnews(self, l_args):
|
||||
def lastnews(self, other_args: List[str]):
|
||||
"""Display latest news."""
|
||||
|
||||
# PARSE ARGS
|
||||
@@ -189,11 +303,12 @@ class DegiroController:
|
||||
help="Number of news to display.",
|
||||
required=False,
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.lastnews(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def login(self, l_args):
|
||||
def login(self, other_args: List[str]):
|
||||
"""Connect to Degiro's API."""
|
||||
|
||||
# PARSE ARGS
|
||||
@@ -229,11 +344,12 @@ class DegiroController:
|
||||
default=None,
|
||||
help="TOTP SECRET (2FA).",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.login(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def logout(self, l_args):
|
||||
def logout(self, other_args: List[str]):
|
||||
"""Log out from Degiro's API."""
|
||||
|
||||
# PARSE ARGS
|
||||
@@ -241,11 +357,11 @@ class DegiroController:
|
||||
add_help=False,
|
||||
prog="logout",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.logout(ns_parser=ns_parser)
|
||||
|
||||
def lookup(self, l_args):
|
||||
def lookup(self, other_args: List[str]):
|
||||
"""Search for products by their name."""
|
||||
|
||||
# PARSING ARGS
|
||||
@@ -272,11 +388,12 @@ class DegiroController:
|
||||
default=0,
|
||||
help="To use an offset.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.lookup(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def pending(self, l_args):
|
||||
def pending(self, other_args: List[str]):
|
||||
"""Display pending orders."""
|
||||
|
||||
# PARSING ARGS
|
||||
@@ -284,19 +401,12 @@ class DegiroController:
|
||||
add_help=False,
|
||||
prog="pending",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.pending(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def q(self, _):
|
||||
"""Process Q command - quit the menu."""
|
||||
return False
|
||||
|
||||
def quit(self, _):
|
||||
"""Process Quit command - quit the program."""
|
||||
return True
|
||||
|
||||
def topnews(self, l_args):
|
||||
def topnews(self, other_args: List[str]):
|
||||
"""Display top news."""
|
||||
|
||||
# PARSING ARGS
|
||||
@@ -304,11 +414,12 @@ class DegiroController:
|
||||
add_help=False,
|
||||
prog="topnews",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.topnews(ns_parser=ns_parser)
|
||||
return self.queue
|
||||
|
||||
def update(self, l_args):
|
||||
def update(self, other_args: List[str]):
|
||||
"""Update an order."""
|
||||
|
||||
# PARSING ARGS
|
||||
@@ -328,79 +439,86 @@ class DegiroController:
|
||||
required=True,
|
||||
type=float,
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, l_args)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
self.__degiro_view.update(ns_parser=ns_parser)
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
"""
|
||||
|
||||
try:
|
||||
degiro_parser = self.__degiro_parser
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
|
||||
(known_args, other_args) = degiro_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
DegiroView.help_display()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
"call_" + known_args.cmd,
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print("")
|
||||
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
def menu(queue: List[str] = None):
|
||||
"""Degiro Menu"""
|
||||
|
||||
# SETUP CONTROLLER
|
||||
degiro_controller = DegiroController()
|
||||
degiro_controller.help(None)
|
||||
degiro_controller = DegiroController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if degiro_controller.queue and len(degiro_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if degiro_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(degiro_controller.queue) > 1:
|
||||
return degiro_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = degiro_controller.queue[0]
|
||||
degiro_controller.queue = degiro_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if (
|
||||
an_input
|
||||
and an_input.split(" ")[0] in degiro_controller.CHOICES_COMMANDS
|
||||
):
|
||||
print(f"{get_flair()} /portfolio/bro/degiro/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in degiro_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (bro)>(degiro)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (bro)>(degiro)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
DegiroView.help_display()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and degiro_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /portfolio/bro/degiro/ $ ",
|
||||
completer=degiro_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /portfolio/bro/degiro/ $ ")
|
||||
|
||||
try:
|
||||
process_input = degiro_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
degiro_controller.queue = degiro_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /portfolio/bro/degiro menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
degiro_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
degiro_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
degiro_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -44,10 +44,6 @@ class DegiroView:
|
||||
def help_display():
|
||||
print(
|
||||
"\nDegiro:\n"
|
||||
" help show this help menu again\n"
|
||||
" q quit degiro standalone menu\n"
|
||||
" quit quit the app\n"
|
||||
"\n"
|
||||
" login connect to degiro's api\n"
|
||||
" logout disconnect from degiro's api\n"
|
||||
"\n"
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
import difflib
|
||||
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.menu import session
|
||||
@@ -15,37 +17,53 @@ from gamestonk_terminal.helper_funcs import (
|
||||
parse_known_args_and_warn,
|
||||
try_except,
|
||||
system_clear,
|
||||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||||
)
|
||||
|
||||
valid_span = ["day", "week", "month", "3month", "year", "5year", "all"]
|
||||
valid_interval = ["5minute", "10minute", "hour", "day", "week"]
|
||||
|
||||
|
||||
class RobinhoodController:
|
||||
|
||||
CHOICES = ["?", "cls", "help", "q", "quit", "login"]
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
Robinhood_CHOICES = ["holdings", "history", "balances"]
|
||||
CHOICES_COMMANDS = ["holdings", "history", "login"]
|
||||
|
||||
def __init__(self):
|
||||
valid_span = ["day", "week", "month", "3month", "year", "5year", "all"]
|
||||
valid_interval = ["5minute", "10minute", "hour", "day", "week"]
|
||||
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""CONSTRUCTOR"""
|
||||
|
||||
self._robinhood_parser = argparse.ArgumentParser(
|
||||
add_help=False, prog="robinhood"
|
||||
)
|
||||
self.CHOICES.extend(self.Robinhood_CHOICES)
|
||||
self._rh_parser = argparse.ArgumentParser(add_help=False, prog="robinhood")
|
||||
self.CHOICES.extend(self.CHOICES_COMMANDS)
|
||||
|
||||
self._robinhood_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
self._rh_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["history"]["-i"] = {c: None for c in self.valid_interval}
|
||||
choices["history"]["--interval"] = {c: None for c in self.valid_interval}
|
||||
choices["history"]["-s"] = {c: None for c in self.valid_span}
|
||||
choices["history"]["--span"] = {c: None for c in self.valid_span}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
self.queue = queue if queue else list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = """
|
||||
Robinhood:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
|
||||
login login to robinhood
|
||||
|
||||
holdings show account holdings in stocks
|
||||
@@ -59,30 +77,40 @@ Robinhood:
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
(known_args, other_args) = self._robinhood_parser.parse_known_args(
|
||||
an_input.split()
|
||||
)
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self._rh_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -90,31 +118,59 @@ Robinhood:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu."""
|
||||
try:
|
||||
robinhood_model.logoff()
|
||||
except Exception:
|
||||
# Exception would be not logged in - perfectly fine
|
||||
pass
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program."""
|
||||
try:
|
||||
robinhood_model.logoff()
|
||||
except Exception:
|
||||
# Exception would be not logged in - perfectly fine
|
||||
pass
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "rh")
|
||||
self.queue.insert(0, "bro")
|
||||
self.queue.insert(0, "portfolio")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "reset", "portfolio", "bro", "rh"]
|
||||
|
||||
@try_except
|
||||
def call_login(self, _):
|
||||
"""Process login"""
|
||||
robinhood_model.login()
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_holdings(self, other_args: List[str]):
|
||||
@@ -125,18 +181,12 @@ Robinhood:
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
description="Display info about your trading accounts on Robinhood",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
robinhood_view.display_holdings(export=ns_parser.export)
|
||||
if ns_parser:
|
||||
robinhood_view.display_holdings(export=ns_parser.export)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_history(self, other_args: List[str]):
|
||||
@@ -152,7 +202,7 @@ Robinhood:
|
||||
"--span",
|
||||
dest="span",
|
||||
type=str,
|
||||
choices=valid_span,
|
||||
choices=self.valid_span,
|
||||
default="3month",
|
||||
help="Span of historical data",
|
||||
)
|
||||
@@ -161,52 +211,93 @@ Robinhood:
|
||||
"--interval",
|
||||
dest="interval",
|
||||
default="day",
|
||||
choices=valid_interval,
|
||||
choices=self.valid_interval,
|
||||
type=str,
|
||||
help="Interval to look at portfolio",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
robinhood_view.display_historical(
|
||||
interval=ns_parser.interval,
|
||||
span=ns_parser.span,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
robinhood_view.display_historical(
|
||||
interval=ns_parser.interval,
|
||||
span=ns_parser.span,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
|
||||
robinhood_controller = RobinhoodController()
|
||||
robinhood_controller.print_help()
|
||||
def menu(queue: List[str] = None):
|
||||
"""Robinhood Menu"""
|
||||
rh_controller = RobinhoodController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if rh_controller.queue and len(rh_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if rh_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(rh_controller.queue) > 1:
|
||||
return rh_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = rh_controller.queue[0]
|
||||
rh_controller.queue = rh_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in rh_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /portfolio/bro/rh/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in robinhood_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (bro)>(rh)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (bro)>(rh)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
rh_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and rh_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /portfolio/bro/rh/ $ ",
|
||||
completer=rh_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /portfolio/bro/rh/ $ ")
|
||||
|
||||
try:
|
||||
process_input = robinhood_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
rh_controller.queue = rh_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /portfolio/bro/rh menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
rh_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
rh_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
rh_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -159,22 +159,20 @@ Portfolio: {self.portfolio_name or None}
|
||||
dest="path",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-p")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
self.portfolio_name = ns_parser.path
|
||||
self.portfolio = portfolio_model.load_portfolio(
|
||||
full_path=os.path.join(portfolios_path, ns_parser.path),
|
||||
sector=ns_parser.sector,
|
||||
last_price=ns_parser.last_price,
|
||||
show_nan=ns_parser.show_nan,
|
||||
)
|
||||
if not self.portfolio.empty:
|
||||
print(f"Successfully loaded: {self.portfolio_name}\n")
|
||||
if ns_parser:
|
||||
self.portfolio_name = ns_parser.path
|
||||
self.portfolio = portfolio_model.load_portfolio(
|
||||
full_path=os.path.join(portfolios_path, ns_parser.path),
|
||||
sector=ns_parser.sector,
|
||||
last_price=ns_parser.last_price,
|
||||
show_nan=ns_parser.show_nan,
|
||||
)
|
||||
if not self.portfolio.empty:
|
||||
print(f"Successfully loaded: {self.portfolio_name}\n")
|
||||
|
||||
@try_except
|
||||
def call_group(self, other_args):
|
||||
@@ -185,7 +183,7 @@ Portfolio: {self.portfolio_name or None}
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
description="Displays portfolio grouped by a given column",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-g")
|
||||
parser.add_argument(
|
||||
"-g",
|
||||
@@ -213,18 +211,15 @@ Portfolio: {self.portfolio_name or None}
|
||||
# dest="cols")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if "value" not in self.portfolio.columns:
|
||||
print(
|
||||
"'value' column not in portfolio. Either add manually or load without --no_last_price flag\n"
|
||||
)
|
||||
return
|
||||
|
||||
portfolio_view.display_group_holdings(
|
||||
portfolio=self.portfolio, group_column=ns_parser.group
|
||||
)
|
||||
if ns_parser:
|
||||
if "value" in self.portfolio.columns:
|
||||
portfolio_view.display_group_holdings(
|
||||
portfolio=self.portfolio, group_column=ns_parser.group
|
||||
)
|
||||
else:
|
||||
print(
|
||||
"'value' column not in portfolio. Either add manually or load without --no_last_price flag\n"
|
||||
)
|
||||
|
||||
@try_except
|
||||
def call_view(self, other_args):
|
||||
@@ -244,19 +239,19 @@ Portfolio: {self.portfolio_name or None}
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
available_ports = os.listdir(portfolios_path)
|
||||
if ns_parser.file_format != "all":
|
||||
available_ports = [
|
||||
port
|
||||
for port in available_ports
|
||||
if port.endswith(ns_parser.file_format)
|
||||
]
|
||||
|
||||
available_ports = os.listdir(portfolios_path)
|
||||
if ns_parser.file_format != "all":
|
||||
available_ports = [
|
||||
port for port in available_ports if port.endswith(ns_parser.file_format)
|
||||
]
|
||||
|
||||
print("\nAvailable Portfolios:\n")
|
||||
for port in available_ports:
|
||||
print(port)
|
||||
print("")
|
||||
print("\nAvailable Portfolios:\n")
|
||||
for port in available_ports:
|
||||
print(port)
|
||||
print("")
|
||||
|
||||
|
||||
def menu():
|
||||
|
||||
@@ -6,7 +6,7 @@ import difflib
|
||||
import os
|
||||
from os import listdir
|
||||
from os.path import isfile, join
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
from datetime import datetime
|
||||
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
@@ -15,18 +15,13 @@ import pandas as pd
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
get_flair,
|
||||
MENU_GO_BACK,
|
||||
MENU_QUIT,
|
||||
MENU_RESET,
|
||||
try_except,
|
||||
valid_date,
|
||||
check_positive_float,
|
||||
system_clear,
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.portfolio.brokers import bro_controller
|
||||
|
||||
# from gamestonk_terminal.portfolio.portfolio_analysis import pa_controller
|
||||
from gamestonk_terminal.portfolio.portfolio_optimization import po_controller
|
||||
from gamestonk_terminal.portfolio import (
|
||||
portfolio_view,
|
||||
@@ -36,7 +31,7 @@ from gamestonk_terminal.portfolio import (
|
||||
)
|
||||
from gamestonk_terminal.helper_funcs import parse_known_args_and_warn
|
||||
|
||||
# pylint: disable=R1710,E1101
|
||||
# pylint: disable=R1710,E1101,C0415
|
||||
|
||||
|
||||
class PortfolioController:
|
||||
@@ -44,17 +39,23 @@ class PortfolioController:
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_MENUS = [
|
||||
"bro",
|
||||
# "pa",
|
||||
"po",
|
||||
]
|
||||
CHOICES_COMMANDS = [
|
||||
"load",
|
||||
"save",
|
||||
"show",
|
||||
@@ -64,18 +65,26 @@ class PortfolioController:
|
||||
"rmr",
|
||||
]
|
||||
|
||||
CHOICES += CHOICES_MENUS
|
||||
CHOICES += CHOICES_MENUS + CHOICES_COMMANDS
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Constructor"""
|
||||
self.port_parser = argparse.ArgumentParser(add_help=False, prog="portfolio")
|
||||
self.port_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in self.CHOICES}
|
||||
)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
self.portfolio = pd.DataFrame(
|
||||
columns=[
|
||||
"Name",
|
||||
@@ -93,13 +102,8 @@ class PortfolioController:
|
||||
"""Print help"""
|
||||
help_text = """
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
reset reset terminal and reload configs
|
||||
|
||||
> bro brokers holdings, \t\t supports: robinhood, ally, degiro
|
||||
> bro brokers holdings, \t\t supports: robinhood, ally, degiro, coinbase
|
||||
> po portfolio optimization, \t optimal portfolio weights from pyportfolioopt
|
||||
|
||||
Portfolio:
|
||||
@@ -122,28 +126,40 @@ Graphs:
|
||||
|
||||
Returns
|
||||
-------
|
||||
MENU_GO_BACK, MENU_QUIT, MENU_RESET
|
||||
MENU_GO_BACK - Show main context menu again
|
||||
MENU_QUIT - Quit terminal
|
||||
MENU_RESET - Reset terminal and go back to same previous menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.port_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -151,46 +167,57 @@ Graphs:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help Command"""
|
||||
self.print_help()
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return MENU_GO_BACK
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - exit the program"""
|
||||
return MENU_QUIT
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "q")
|
||||
return self.queue
|
||||
return ["q"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "q")
|
||||
self.queue.insert(0, "q")
|
||||
return self.queue
|
||||
return ["q", "q"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process Reset command - reset the program"""
|
||||
return MENU_RESET
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "portfolio")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "reset", "portfolio"]
|
||||
|
||||
# MENUS
|
||||
def call_bro(self, _):
|
||||
"""Process bro command"""
|
||||
ret = bro_controller.menu()
|
||||
if ret is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
from gamestonk_terminal.portfolio.brokers import bro_controller
|
||||
|
||||
# def call_pa(self, _):
|
||||
# """Process pa command"""
|
||||
# ret = pa_controller.menu()
|
||||
# if ret is False:
|
||||
# self.print_help()
|
||||
# else:
|
||||
# return True
|
||||
return bro_controller.menu(self.queue)
|
||||
|
||||
def call_po(self, _):
|
||||
"""Process po command"""
|
||||
ret = po_controller.menu([])
|
||||
if ret is False:
|
||||
self.print_help()
|
||||
else:
|
||||
return True
|
||||
return po_controller.menu([], self.queue)
|
||||
|
||||
@try_except
|
||||
def call_load(self, other_args: List[str]):
|
||||
@@ -217,11 +244,10 @@ Graphs:
|
||||
other_args.insert(0, "-n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
self.portfolio = portfolio_model.load_df(ns_parser.name)
|
||||
print("")
|
||||
if ns_parser:
|
||||
self.portfolio = portfolio_model.load_df(ns_parser.name)
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_save(self, other_args: List[str]):
|
||||
@@ -244,23 +270,24 @@ Graphs:
|
||||
other_args.insert(0, "-n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if (
|
||||
".csv" in ns_parser.name
|
||||
or ".xlsx" in ns_parser.name
|
||||
or ".json" in ns_parser.name
|
||||
):
|
||||
portfolio_model.save_df(self.portfolio, ns_parser.name)
|
||||
else:
|
||||
print(
|
||||
"Please submit as 'filename.filetype' with filetype being csv, xlsx, or json\n"
|
||||
)
|
||||
|
||||
if (
|
||||
".csv" not in ns_parser.name
|
||||
and ".xlsx" not in ns_parser.name
|
||||
and ".json" not in ns_parser.name
|
||||
):
|
||||
print(
|
||||
"Please submit as 'filename.filetype' with filetype being csv, xlsx, or json\n"
|
||||
)
|
||||
|
||||
portfolio_model.save_df(self.portfolio, ns_parser.name)
|
||||
return self.queue
|
||||
|
||||
def call_show(self, _):
|
||||
"""Process show command"""
|
||||
portfolio_view.show_df(self.portfolio, False)
|
||||
return self.queue
|
||||
|
||||
def call_add(self, other_args: List[str]):
|
||||
"""Process add command"""
|
||||
@@ -318,13 +345,7 @@ Graphs:
|
||||
type=check_positive_float,
|
||||
help="Fees paid for transaction",
|
||||
)
|
||||
# parser.add_argument(
|
||||
# "-r",
|
||||
# "--premium",
|
||||
# dest="premium",
|
||||
# type=check_positive_float,
|
||||
# help="Premium paid/received for the option",
|
||||
# )
|
||||
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--action",
|
||||
@@ -369,6 +390,7 @@ Graphs:
|
||||
self.portfolio = self.portfolio.append([data])
|
||||
self.portfolio.index = list(range(0, len(self.portfolio.values)))
|
||||
print(f"{ns_parser.name.upper()} successfully added\n")
|
||||
return self.queue
|
||||
|
||||
def call_rmv(self, _):
|
||||
"""Process rmv command"""
|
||||
@@ -381,6 +403,7 @@ Graphs:
|
||||
print(
|
||||
f"Invalid index please use an integer between 0 and {len(self.portfolio.index)-1}\n"
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ar(self, other_args: List[str]):
|
||||
@@ -399,18 +422,19 @@ Graphs:
|
||||
default="SPY",
|
||||
help="Choose a ticker to be the market asset",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if self.portfolio.empty:
|
||||
print("Please add items to the portfolio\n")
|
||||
return
|
||||
|
||||
val, hist = portfolio_model.convert_df(self.portfolio)
|
||||
if not val.empty:
|
||||
portfolio_view.Report(val, hist, ns_parser.market, 365).generate_report()
|
||||
if ns_parser:
|
||||
if not self.portfolio.empty:
|
||||
val, hist = portfolio_model.convert_df(self.portfolio)
|
||||
if not val.empty:
|
||||
portfolio_view.Report(
|
||||
val, hist, ns_parser.market, 365
|
||||
).generate_report()
|
||||
else:
|
||||
print("Cannot generate a graph from an empty dataframe\n")
|
||||
else:
|
||||
print("Please add items to the portfolio\n")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_rmr(self, other_args: List[str]):
|
||||
@@ -429,53 +453,93 @@ Graphs:
|
||||
default="SPY",
|
||||
help="Choose a ticker to be the market asset",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if self.portfolio.empty:
|
||||
val, _ = portfolio_model.convert_df(self.portfolio)
|
||||
if not val.empty:
|
||||
df_m = yfinance_model.get_market(val.index[0], ns_parser.market)
|
||||
returns, _ = portfolio_model.get_return(val, df_m, 365)
|
||||
portfolio_view.plot_overall_return(returns, ns_parser.market, True)
|
||||
else:
|
||||
print("Cannot generate a graph from an empty dataframe\n")
|
||||
else:
|
||||
print("Please add items to the portfolio\n")
|
||||
|
||||
if self.portfolio.empty:
|
||||
print("Please add items to the portfolio\n")
|
||||
return
|
||||
|
||||
val, _ = portfolio_model.convert_df(self.portfolio)
|
||||
if not val.empty:
|
||||
df_m = yfinance_model.get_market(val.index[0], ns_parser.market)
|
||||
returns, _ = portfolio_model.get_return(val, df_m, 365)
|
||||
portfolio_view.plot_overall_return(returns, ns_parser.market, True)
|
||||
else:
|
||||
print("Cannot generate a graph from an empty dataframe\n")
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
def menu(queue: List[str] = None):
|
||||
"""Portfolio Menu"""
|
||||
portfolio_controller = PortfolioController()
|
||||
portfolio_controller.call_help(None)
|
||||
while True:
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in portfolio_controller.CHOICES}
|
||||
)
|
||||
port_controller = PortfolioController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (portfolio)> ",
|
||||
completer=completer,
|
||||
)
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if port_controller.queue and len(port_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if port_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(port_controller.queue) > 1:
|
||||
return port_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = port_controller.queue[0]
|
||||
port_controller.queue = port_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in port_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /portfolio/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (portfolio)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
port_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and port_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /portfolio/ $ ",
|
||||
completer=port_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /portfolio/ $ ")
|
||||
|
||||
try:
|
||||
process_input = portfolio_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
port_controller.queue = port_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exit\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, portfolio_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /portfolio menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
port_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
port_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
port_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -2,8 +2,9 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
import matplotlib.pyplot as plt
|
||||
import difflib
|
||||
from typing import List, Union
|
||||
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
@@ -21,24 +22,24 @@ from gamestonk_terminal.portfolio.portfolio_optimization import (
|
||||
|
||||
from gamestonk_terminal.helper_funcs import get_rf
|
||||
|
||||
period_choices = [
|
||||
"1d",
|
||||
"5d",
|
||||
"1mo",
|
||||
"3mo",
|
||||
"6mo",
|
||||
"1y",
|
||||
"2y",
|
||||
"5y",
|
||||
"10y",
|
||||
"ytd",
|
||||
"max",
|
||||
]
|
||||
|
||||
# pylint: disable=C0302
|
||||
|
||||
|
||||
class PortfolioOptimization:
|
||||
|
||||
CHOICES = ["cls", "?", "help", "q", "quit"]
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
CHOICES_COMMANDS = [
|
||||
"select",
|
||||
"add",
|
||||
@@ -56,30 +57,146 @@ class PortfolioOptimization:
|
||||
"yolo",
|
||||
]
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
period_choices = [
|
||||
"1d",
|
||||
"5d",
|
||||
"1mo",
|
||||
"3mo",
|
||||
"6mo",
|
||||
"1y",
|
||||
"2y",
|
||||
"5y",
|
||||
"10y",
|
||||
"ytd",
|
||||
"max",
|
||||
]
|
||||
|
||||
yf_info_choices = [
|
||||
"previousClose",
|
||||
"regularMarketOpen",
|
||||
"twoHundredDayAverage",
|
||||
"trailingAnnualDividendYield",
|
||||
"payoutRatio",
|
||||
"volume24Hr",
|
||||
"regularMarketDayHigh",
|
||||
"navPrice",
|
||||
"averageDailyVolume10Day",
|
||||
"totalAssets",
|
||||
"regularMarketPreviousClose",
|
||||
"fiftyDayAverage",
|
||||
"trailingAnnualDividendRate",
|
||||
"open",
|
||||
"toCurrency",
|
||||
"averageVolume10days",
|
||||
"expireDate",
|
||||
"yield",
|
||||
"algorithm",
|
||||
"dividendRate",
|
||||
"exDividendDate",
|
||||
"beta",
|
||||
"circulatingSupply",
|
||||
"regularMarketDayLow",
|
||||
"priceHint",
|
||||
"currency",
|
||||
"trailingPE",
|
||||
"regularMarketVolume",
|
||||
"lastMarket",
|
||||
"maxSupply",
|
||||
"openInterest",
|
||||
"marketCap",
|
||||
"volumeAllCurrencies",
|
||||
"strikePrice",
|
||||
"averageVolume",
|
||||
"priceToSalesTrailing12Months",
|
||||
"dayLow",
|
||||
"ask",
|
||||
"ytdReturn",
|
||||
"askSize",
|
||||
"volume",
|
||||
"fiftyTwoWeekHigh",
|
||||
"forwardPE",
|
||||
"fromCurrency",
|
||||
"fiveYearAvgDividendYield",
|
||||
"fiftyTwoWeekLow",
|
||||
"bid",
|
||||
"dividendYield",
|
||||
"bidSize",
|
||||
"dayHigh",
|
||||
"annualHoldingsTurnover",
|
||||
"enterpriseToRevenue",
|
||||
"beta3Year",
|
||||
"profitMargins",
|
||||
"enterpriseToEbitda",
|
||||
"52WeekChange",
|
||||
"morningStarRiskRating",
|
||||
"forwardEps",
|
||||
"revenueQuarterlyGrowth",
|
||||
"sharesOutstanding",
|
||||
"fundInceptionDate",
|
||||
"annualReportExpenseRatio",
|
||||
"bookValue",
|
||||
"sharesShort",
|
||||
"sharesPercentSharesOut",
|
||||
"heldPercentInstitutions",
|
||||
"netIncomeToCommon",
|
||||
"trailingEps",
|
||||
"lastDividendValue",
|
||||
"SandP52WeekChange",
|
||||
"priceToBook",
|
||||
"heldPercentInsiders",
|
||||
"shortRatio",
|
||||
"sharesShortPreviousMonthDate",
|
||||
"floatShares",
|
||||
"enterpriseValue",
|
||||
"fundFamily",
|
||||
"threeYearAverageReturn",
|
||||
"lastSplitFactor",
|
||||
"legalType",
|
||||
"lastDividendDate",
|
||||
"morningStarOverallRating",
|
||||
"earningsQuarterlyGrowth",
|
||||
"pegRatio",
|
||||
"lastCapGain",
|
||||
"shortPercentOfFloat",
|
||||
"sharesShortPriorMonth",
|
||||
"impliedSharesOutstanding",
|
||||
"fiveYearAverageReturn",
|
||||
"regularMarketPrice",
|
||||
]
|
||||
|
||||
# pylint: disable=dangerous-default-value
|
||||
def __init__(self, tickers: List[str]):
|
||||
|
||||
def __init__(self, tickers: List[str], queue: List[str] = None):
|
||||
"""Construct Portfolio Optimization"""
|
||||
|
||||
self.po_parser = argparse.ArgumentParser(add_help=False, prog="po")
|
||||
self.po_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
self.tickers = list(set(tickers))
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
@staticmethod
|
||||
def print_help(tickers: List[str]):
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["property"]["-p"] = {c: None for c in self.yf_info_choices}
|
||||
choices["property"]["--property"] = {c: None for c in self.yf_info_choices}
|
||||
for fn in ["maxsharpe", "minvol", "maxquadutil", "effret", "effrisk", "ef"]:
|
||||
choices[fn]["-p"] = {c: None for c in self.period_choices}
|
||||
choices[fn]["--period"] = {c: None for c in self.period_choices}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = f"""
|
||||
What would you like to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
|
||||
Portfolio Optimization:
|
||||
select select list of tickers to be optimized
|
||||
add add tickers to the list of the tickers to be optimized
|
||||
rmv remove tickers from the list of the tickers to be optimized
|
||||
|
||||
Tickers: {('None', ', '.join(tickers))[bool(tickers)]}
|
||||
Tickers: {('None', ', '.join(self.tickers))[bool(self.tickers)]}
|
||||
|
||||
Optimization:
|
||||
equal equally weighted
|
||||
@@ -103,28 +220,40 @@ Mean Variance Optimization:
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.po_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help(self.tickers)
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -132,30 +261,65 @@ Mean Variance Optimization:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help(self.tickers)
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "po")
|
||||
self.queue.insert(0, "portfolio")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "reset", "portfolio", "po"]
|
||||
|
||||
def call_select(self, other_args: List[str]):
|
||||
"""Process select command"""
|
||||
self.tickers = []
|
||||
self.add_stocks(other_args)
|
||||
return self.queue
|
||||
|
||||
def call_add(self, other_args: List[str]):
|
||||
"""Process add command"""
|
||||
self.add_stocks(other_args)
|
||||
return self.queue
|
||||
|
||||
def call_rmv(self, other_args: List[str]):
|
||||
"""Process rmv command"""
|
||||
self.rmv_stocks(other_args)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_equal(self, other_args: List[str]):
|
||||
@@ -183,16 +347,15 @@ Mean Variance Optimization:
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return self.queue
|
||||
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
|
||||
optimizer_view.display_equal_weight(
|
||||
stocks=self.tickers, value=ns_parser.value, pie=ns_parser.pie
|
||||
)
|
||||
optimizer_view.display_equal_weight(
|
||||
stocks=self.tickers, value=ns_parser.value, pie=ns_parser.pie
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_mktcap(self, other_args: List[str]):
|
||||
@@ -219,18 +382,18 @@ Mean Variance Optimization:
|
||||
help="Display a pie chart for weights",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 stocks selected to perform calculations.")
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 stocks selected to perform calculations.")
|
||||
return self.queue
|
||||
|
||||
optimizer_view.display_property_weighting(
|
||||
self.tickers,
|
||||
s_property="marketCap",
|
||||
value=ns_parser.value,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
optimizer_view.display_property_weighting(
|
||||
self.tickers,
|
||||
s_property="marketCap",
|
||||
value=ns_parser.value,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_dividend(self, other_args: List[str]):
|
||||
@@ -257,18 +420,18 @@ Mean Variance Optimization:
|
||||
help="Display a pie chart for weights",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 stocks selected to perform calculations.")
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 stocks selected to perform calculations.")
|
||||
return self.queue
|
||||
|
||||
optimizer_view.display_property_weighting(
|
||||
self.tickers,
|
||||
s_property="dividendYield",
|
||||
value=ns_parser.value,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
optimizer_view.display_property_weighting(
|
||||
self.tickers,
|
||||
s_property="dividendYield",
|
||||
value=ns_parser.value,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_property(self, other_args: List[str]):
|
||||
@@ -285,22 +448,8 @@ Mean Variance Optimization:
|
||||
required=bool("-h" not in other_args),
|
||||
type=optimizer_helper.check_valid_property_type,
|
||||
dest="property",
|
||||
help="""Property info to weigh. Use one of:
|
||||
previousClose, regularMarketOpen, twoHundredDayAverage, trailingAnnualDividendYield,
|
||||
payoutRatio, volume24Hr, regularMarketDayHigh, navPrice, averageDailyVolume10Day, totalAssets,
|
||||
regularMarketPreviousClose, fiftyDayAverage, trailingAnnualDividendRate, open, toCurrency,
|
||||
averageVolume10days,expireDate, yield, algorithm, dividendRate, exDividendDate, beta, circulatingSupply,
|
||||
regularMarketDayLow, priceHint, currency, trailingPE, regularMarketVolume, lastMarket, maxSupply,
|
||||
openInterest,marketCap, volumeAllCurrencies, strikePrice, averageVolume, priceToSalesTrailing12Months,
|
||||
dayLow, ask, ytdReturn,askSize,volume, fiftyTwoWeekHigh, forwardPE, fromCurrency, fiveYearAvgDividendYield,
|
||||
fiftyTwoWeekLow, bid,dividendYield,bidSize, dayHigh, annualHoldingsTurnover, enterpriseToRevenue, beta3Year,
|
||||
profitMargins, enterpriseToEbitda, 52WeekChange, morningStarRiskRating, forwardEps, revenueQuarterlyGrowth,
|
||||
sharesOutstanding, fundInceptionDate, annualReportExpenseRatio, bookValue, sharesShort, sharesPercentSharesOut
|
||||
heldPercentInstitutions, netIncomeToCommon, trailingEps, lastDividendValue, SandP52WeekChange, priceToBook,
|
||||
heldPercentInsiders, shortRatio, sharesShortPreviousMonthDate, floatShares, enterpriseValue,fundFamily,
|
||||
threeYearAverageReturn, lastSplitFactor, legalType, lastDividendDate, morningStarOverallRating,
|
||||
earningsQuarterlyGrowth, pegRatio, lastCapGain, shortPercentOfFloat, sharesShortPriorMonth,
|
||||
impliedSharesOutstanding, fiveYearAverageReturn, and regularMarketPrice.""",
|
||||
choices=self.yf_info_choices,
|
||||
help="""Property info to weigh. Use one of yfinance info options.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
@@ -318,18 +467,18 @@ Mean Variance Optimization:
|
||||
help="Display a pie chart for weights",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 stocks selected to perform calculations.")
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 stocks selected to perform calculations.")
|
||||
return self.queue
|
||||
|
||||
optimizer_view.display_property_weighting(
|
||||
self.tickers,
|
||||
s_property=ns_parser.property,
|
||||
value=ns_parser.value,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
optimizer_view.display_property_weighting(
|
||||
self.tickers,
|
||||
s_property=ns_parser.property,
|
||||
value=ns_parser.value,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_maxsharpe(self, other_args: List[str]):
|
||||
@@ -346,7 +495,7 @@ Mean Variance Optimization:
|
||||
default="3mo",
|
||||
dest="period",
|
||||
help="period to get yfinance data from",
|
||||
choices=period_choices,
|
||||
choices=self.period_choices,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
@@ -374,19 +523,18 @@ Mean Variance Optimization:
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
optimizer_view.display_max_sharpe(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
rfrate=ns_parser.risk_free_rate,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return self.queue
|
||||
optimizer_view.display_max_sharpe(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
rfrate=ns_parser.risk_free_rate,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_minvol(self, other_args: List[str]):
|
||||
@@ -403,7 +551,7 @@ Mean Variance Optimization:
|
||||
default="3mo",
|
||||
dest="period",
|
||||
help="period to get yfinance data from",
|
||||
choices=period_choices,
|
||||
choices=self.period_choices,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
@@ -422,19 +570,18 @@ Mean Variance Optimization:
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return self.queue
|
||||
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
|
||||
optimizer_view.display_min_volatility(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
optimizer_view.display_min_volatility(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_maxquadutil(self, other_args: List[str]):
|
||||
@@ -451,7 +598,7 @@ Mean Variance Optimization:
|
||||
default="3mo",
|
||||
dest="period",
|
||||
help="period to get yfinance data from",
|
||||
choices=period_choices,
|
||||
choices=self.period_choices,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
@@ -487,25 +634,26 @@ Mean Variance Optimization:
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return self.queue
|
||||
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
if ns_parser.pie and ns_parser.market_neutral:
|
||||
print(
|
||||
"Cannot show pie chart for market neutral due to negative weights."
|
||||
)
|
||||
return self.queue
|
||||
|
||||
if ns_parser.pie and ns_parser.market_neutral:
|
||||
print("Cannot show pie chart for market neutral due to negative weights.")
|
||||
return
|
||||
|
||||
optimizer_view.display_max_quadratic_utility(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
risk_aversion=ns_parser.risk_aversion,
|
||||
market_neutral=ns_parser.market_neutral,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
optimizer_view.display_max_quadratic_utility(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
risk_aversion=ns_parser.risk_aversion,
|
||||
market_neutral=ns_parser.market_neutral,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_effrisk(self, other_args: List[str]):
|
||||
@@ -523,7 +671,7 @@ Mean Variance Optimization:
|
||||
default="3mo",
|
||||
dest="period",
|
||||
help="period to get yfinance data from",
|
||||
choices=period_choices,
|
||||
choices=self.period_choices,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
@@ -551,7 +699,7 @@ Mean Variance Optimization:
|
||||
help="Display a pie chart for weights. Only if neutral flag is left False.",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-t")
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
@@ -563,25 +711,26 @@ Mean Variance Optimization:
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return self.queue
|
||||
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
if ns_parser.pie and ns_parser.market_neutral:
|
||||
print(
|
||||
"Cannot show pie chart for market neutral due to negative weights."
|
||||
)
|
||||
return self.queue
|
||||
|
||||
if ns_parser.pie and ns_parser.market_neutral:
|
||||
print("Cannot show pie chart for market neutral due to negative weights.")
|
||||
return
|
||||
|
||||
optimizer_view.display_efficient_risk(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
target_volatility=ns_parser.target_volatility,
|
||||
market_neutral=ns_parser.market_neutral,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
optimizer_view.display_efficient_risk(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
target_volatility=ns_parser.target_volatility,
|
||||
market_neutral=ns_parser.market_neutral,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_effret(self, other_args: List[str]):
|
||||
@@ -598,7 +747,7 @@ Mean Variance Optimization:
|
||||
default="3mo",
|
||||
dest="period",
|
||||
help="period to get yfinance data from",
|
||||
choices=period_choices,
|
||||
choices=self.period_choices,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-v",
|
||||
@@ -625,7 +774,7 @@ Mean Variance Optimization:
|
||||
help="Display a pie chart for weights. Only if neutral flag is left False.",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
parser.add_argument(
|
||||
@@ -637,25 +786,26 @@ Mean Variance Optimization:
|
||||
help="the desired return of the resulting portfolio",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return self.queue
|
||||
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
if ns_parser.pie and ns_parser.market_neutral:
|
||||
print(
|
||||
"Cannot show pie chart for market neutral due to negative weights."
|
||||
)
|
||||
return self.queue
|
||||
|
||||
if ns_parser.pie and ns_parser.market_neutral:
|
||||
print("Cannot show pie chart for market neutral due to negative weights.")
|
||||
return
|
||||
|
||||
optimizer_view.display_efficient_return(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
target_return=ns_parser.target_return,
|
||||
market_neutral=ns_parser.market_neutral,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
optimizer_view.display_efficient_return(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
value=ns_parser.value,
|
||||
target_return=ns_parser.target_return,
|
||||
market_neutral=ns_parser.market_neutral,
|
||||
pie=ns_parser.pie,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ef(self, other_args):
|
||||
@@ -673,9 +823,9 @@ Mean Variance Optimization:
|
||||
default="3mo",
|
||||
dest="period",
|
||||
help="period to get yfinance data from",
|
||||
choices=period_choices,
|
||||
choices=self.period_choices,
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-n")
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
@@ -695,24 +845,24 @@ Mean Variance Optimization:
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return self.queue
|
||||
|
||||
if len(self.tickers) < 2:
|
||||
print("Please have at least 2 loaded tickers to calculate weights.\n")
|
||||
return
|
||||
|
||||
optimizer_view.display_ef(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
n_portfolios=ns_parser.n_port,
|
||||
risk_free=ns_parser.risk_free,
|
||||
)
|
||||
optimizer_view.display_ef(
|
||||
stocks=self.tickers,
|
||||
period=ns_parser.period,
|
||||
n_portfolios=ns_parser.n_port,
|
||||
risk_free=ns_parser.risk_free,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
def call_yolo(self, _):
|
||||
# Easter egg :)
|
||||
print("DFV YOLO")
|
||||
print("GME: ALL", "\n")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def add_stocks(self, other_args: List[str]):
|
||||
@@ -735,17 +885,18 @@ Mean Variance Optimization:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
tickers = set(self.tickers)
|
||||
for ticker in ns_parser.add_tickers:
|
||||
tickers.add(ticker)
|
||||
if ns_parser:
|
||||
tickers = set(self.tickers)
|
||||
for ticker in ns_parser.add_tickers:
|
||||
tickers.add(ticker)
|
||||
|
||||
if self.tickers:
|
||||
print(f"\nCurrent Tickers: {('None', ', '.join(tickers))[bool(tickers)]}")
|
||||
if self.tickers:
|
||||
print(
|
||||
f"\nCurrent Tickers: {('None', ', '.join(tickers))[bool(tickers)]}"
|
||||
)
|
||||
|
||||
self.tickers = list(tickers)
|
||||
print("")
|
||||
self.tickers = list(tickers)
|
||||
print("")
|
||||
|
||||
@try_except
|
||||
def rmv_stocks(self, other_args: List[str]):
|
||||
@@ -768,47 +919,104 @@ Mean Variance Optimization:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
tickers = set(self.tickers)
|
||||
for ticker in ns_parser.rmv_tickers:
|
||||
tickers.remove(ticker)
|
||||
|
||||
tickers = set(self.tickers)
|
||||
for ticker in ns_parser.rmv_tickers:
|
||||
tickers.remove(ticker)
|
||||
if self.tickers:
|
||||
print(
|
||||
f"\nCurrent Tickers: {('None', ', '.join(tickers))[bool(tickers)]}"
|
||||
)
|
||||
|
||||
if self.tickers:
|
||||
print(f"\nCurrent Tickers: {('None', ', '.join(tickers))[bool(tickers)]}")
|
||||
|
||||
self.tickers = list(tickers)
|
||||
print("")
|
||||
self.tickers = list(tickers)
|
||||
print("")
|
||||
|
||||
|
||||
def menu(tickers: List[str]):
|
||||
def menu(
|
||||
tickers: List[str],
|
||||
queue: List[str] = None,
|
||||
from_submenu: bool = False,
|
||||
):
|
||||
"""Portfolio Optimization Menu"""
|
||||
plt.close("all")
|
||||
po_controller = PortfolioOptimization(tickers)
|
||||
po_controller.call_help(tickers)
|
||||
po_controller = PortfolioOptimization(tickers, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if po_controller.queue and len(po_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if po_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
# Since we came from another menu we need to quit an additional time
|
||||
if from_submenu:
|
||||
# Skip quit
|
||||
po_controller.queue = po_controller.queue[1:]
|
||||
po_controller.queue.insert(0, "portfolio")
|
||||
po_controller.queue.insert(0, "home")
|
||||
from_submenu = False
|
||||
return po_controller.queue
|
||||
|
||||
if len(po_controller.queue) > 1:
|
||||
return po_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = po_controller.queue[0]
|
||||
po_controller.queue = po_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in po_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /portfolio/po/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in po_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (portfolio)>(po)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (portfolio)>(po)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
po_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and po_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /portfolio/po/ $ ",
|
||||
completer=po_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /portfolio/po/ $ ")
|
||||
|
||||
try:
|
||||
plt.close("all")
|
||||
|
||||
process_input = po_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
po_controller.queue = po_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /portfolio/po menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
po_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
po_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
po_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -145,7 +145,7 @@
|
||||
"source": [
|
||||
"from gamestonk_terminal.stocks.fundamental_analysis import av_view\n",
|
||||
"\n",
|
||||
"av_view.display_income_statement(ticker, number=1)"
|
||||
"av_view.display_income_statement(ticker, limit=1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -165,7 +165,7 @@
|
||||
"source": [
|
||||
"from gamestonk_terminal.stocks.fundamental_analysis import av_view\n",
|
||||
"\n",
|
||||
"av_view.display_income_statement(ticker, number = 1, quarterly=True)"
|
||||
"av_view.display_income_statement(ticker, limit=1, quarterly=True)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -184,7 +184,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from gamestonk_terminal.stocks.fundamental_analysis import av_view\n",
|
||||
"av_view.display_balance_sheet(ticker, number = 1)"
|
||||
"av_view.display_balance_sheet(ticker, limit=1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -203,7 +203,7 @@
|
||||
"outputs": [],
|
||||
"source": [
|
||||
"from gamestonk_terminal.stocks.fundamental_analysis import av_view\n",
|
||||
"av_view.display_cash_flow(ticker,number=1)"
|
||||
"av_view.display_cash_flow(ticker, limit=1)"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
"""Papermill Controller Module"""
|
||||
"""Reports Controller Module."""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
# pylint: disable=R1732
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import os
|
||||
import subprocess
|
||||
from typing import List, Union
|
||||
import webbrowser
|
||||
from datetime import datetime
|
||||
from ast import literal_eval
|
||||
@@ -16,23 +15,22 @@ import papermill as pm
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.helper_funcs import get_flair, system_clear
|
||||
from gamestonk_terminal import config_terminal
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
MENU_GO_BACK,
|
||||
MENU_QUIT,
|
||||
MENU_RESET,
|
||||
)
|
||||
|
||||
|
||||
class ReportController:
|
||||
"""Report Controller class"""
|
||||
"""Report Controller class."""
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
@@ -64,7 +62,7 @@ class ReportController:
|
||||
notebook_content = open(notebook_file + ".ipynb").read()
|
||||
metadata_cell = """"metadata": {\n "tags": [\n "parameters"\n ]\n },\n "outputs":"""
|
||||
notebook_metadata_content = notebook_content[
|
||||
notebook_content.find(metadata_cell) :
|
||||
notebook_content.find(metadata_cell) : # noqa: E203
|
||||
]
|
||||
cell_start = 'source": '
|
||||
cell_end = '"report_name='
|
||||
@@ -72,7 +70,9 @@ class ReportController:
|
||||
notebook_metadata_content[
|
||||
notebook_metadata_content.find(
|
||||
cell_start
|
||||
) : notebook_metadata_content.find(cell_end)
|
||||
) : notebook_metadata_content.find( # noqa: E203
|
||||
cell_end
|
||||
)
|
||||
]
|
||||
+ "]"
|
||||
)
|
||||
@@ -85,83 +85,92 @@ class ReportController:
|
||||
d_params[report_to_run] = l_params
|
||||
|
||||
args = f"<{'> <'.join(l_params)}>"
|
||||
reports_opts += f" {k}. {report_to_run}{(max_len_name-len(report_to_run))*' '} {args if args != '<>' else ''}\n"
|
||||
reports_opts += (
|
||||
f" {k}. {report_to_run}"
|
||||
+ f"{(max_len_name-len(report_to_run))*' '} "
|
||||
+ f"{args if args != '<>' else ''}\n"
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
"""Constructor"""
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Construct the Reports Controller."""
|
||||
self.report_parser = argparse.ArgumentParser(add_help=False, prog="reports")
|
||||
self.report_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
"""Print help."""
|
||||
help_text = f"""
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon the program
|
||||
reset reset terminal and reload configs
|
||||
|
||||
Select one of the following reports:
|
||||
{self.reports_opts}"""
|
||||
print(help_text)
|
||||
|
||||
def switch(self, an_input: str, proc: subprocess.Popen):
|
||||
"""Process and dispatch input
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input.
|
||||
|
||||
Parameters
|
||||
-------
|
||||
----------
|
||||
an_input : str
|
||||
string with input arguments
|
||||
proc : subprocess.Popen
|
||||
subprocess that calls jupyter notebook for report generation
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.report_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd in ["?", "help"]:
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
|
||||
# Go back to menu above
|
||||
if known_args.cmd == "q":
|
||||
proc.kill()
|
||||
print("")
|
||||
return MENU_GO_BACK
|
||||
|
||||
# Quit terminal
|
||||
if known_args.cmd == "quit":
|
||||
proc.kill()
|
||||
print("")
|
||||
return MENU_QUIT
|
||||
|
||||
# Reset menu
|
||||
if known_args.cmd == "reset":
|
||||
print("")
|
||||
return MENU_RESET
|
||||
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
if known_args.cmd in ["quit", "help", "reset", "home", "exit"]:
|
||||
return getattr(
|
||||
self,
|
||||
"call_" + known_args.cmd,
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
# Execute the requested report
|
||||
if known_args.cmd in self.d_id_to_report_name:
|
||||
report_to_run = self.d_id_to_report_name[known_args.cmd]
|
||||
else:
|
||||
@@ -169,9 +178,10 @@ Select one of the following reports:
|
||||
|
||||
params = self.d_params[report_to_run]
|
||||
|
||||
# Check that the number of arguments match. We can't check validity of argument used because
|
||||
# this depends on what the user will use it for in the notebook. This is a downside of allowing
|
||||
# the user to have this much flexibility.
|
||||
# Check that the number of arguments match. We can't check validity of the
|
||||
# argument used because this depends on what the user will use it for in
|
||||
# the notebook. This is a downside of allowing the user to have this much
|
||||
# flexibility.
|
||||
if len(other_args) != len(params):
|
||||
print("Wrong number of arguments provided!")
|
||||
if len(params):
|
||||
@@ -181,17 +191,22 @@ Select one of the following reports:
|
||||
else:
|
||||
print("No argument required.")
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
notebook_template = os.path.join(
|
||||
"gamestonk_terminal", "reports", report_to_run
|
||||
)
|
||||
args_to_output = f"_{'_'.join(other_args)}" if "_".join(other_args) else ""
|
||||
report_output_name = (
|
||||
f"{datetime.now().strftime('%Y%m%d_%H%M%S')}"
|
||||
+ "_"
|
||||
+ f"{report_to_run}{args_to_output}"
|
||||
)
|
||||
notebook_output = os.path.join(
|
||||
"gamestonk_terminal",
|
||||
"reports",
|
||||
"stored",
|
||||
f"{datetime.now().strftime('%Y%m%d_%H%M%S')}_{report_to_run}{args_to_output}",
|
||||
report_output_name,
|
||||
)
|
||||
|
||||
d_report_params = {}
|
||||
@@ -207,22 +222,10 @@ Select one of the following reports:
|
||||
)
|
||||
|
||||
if gtff.OPEN_REPORT_AS_HTML:
|
||||
webbrowser.open(
|
||||
os.path.join(
|
||||
f"http://localhost:{config_terminal.PAPERMILL_NOTEBOOK_REPORT_PORT}",
|
||||
"view",
|
||||
notebook_output + ".html",
|
||||
)
|
||||
)
|
||||
|
||||
else:
|
||||
webbrowser.open(
|
||||
os.path.join(
|
||||
f"http://localhost:{config_terminal.PAPERMILL_NOTEBOOK_REPORT_PORT}",
|
||||
"notebooks",
|
||||
notebook_output + ".ipynb",
|
||||
)
|
||||
report_output_path = os.path.join(
|
||||
os.path.dirname(__file__), "..", "..", f"{notebook_output}.html"
|
||||
)
|
||||
webbrowser.open(f"file://{report_output_path}")
|
||||
|
||||
print("")
|
||||
print(
|
||||
@@ -232,51 +235,123 @@ Select one of the following reports:
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
return self.queue
|
||||
return self.queue
|
||||
|
||||
return None
|
||||
def call_home(self, _):
|
||||
"""Process home command."""
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_cls(self, _):
|
||||
"""Process cls command."""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command."""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process quit menu command."""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "q")
|
||||
return self.queue
|
||||
return ["q"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command."""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "q")
|
||||
self.queue.insert(0, "q")
|
||||
return self.queue
|
||||
return ["q", "q"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command."""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "reports")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "reset", "reports"]
|
||||
|
||||
|
||||
def menu():
|
||||
"""Report Menu"""
|
||||
report_controller = ReportController()
|
||||
report_controller.print_help()
|
||||
|
||||
# Initialize jupyter notebook
|
||||
|
||||
cmd = f"jupyter notebook --port={config_terminal.PAPERMILL_NOTEBOOK_REPORT_PORT}"
|
||||
proc = subprocess.Popen(
|
||||
cmd, shell=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
|
||||
)
|
||||
|
||||
print(
|
||||
f"Jupyter notebook environment launched on http://localhost:{config_terminal.PAPERMILL_NOTEBOOK_REPORT_PORT}\n"
|
||||
)
|
||||
def menu(queue: List[str] = None):
|
||||
"""Report Menu."""
|
||||
report_controller = ReportController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if report_controller.queue and len(report_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if report_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(report_controller.queue) > 1:
|
||||
return report_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = report_controller.queue[0]
|
||||
report_controller.queue = report_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction
|
||||
# and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in report_controller.CHOICES:
|
||||
print(f"{get_flair()} /reports/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in report_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (reports)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (reports)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
report_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and report_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /reports/ $ ",
|
||||
completer=report_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /reports/ $ ")
|
||||
|
||||
try:
|
||||
process_input = report_controller.switch(an_input, proc)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
report_controller.queue = report_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, report_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /reports menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
report_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
report_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
report_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import webbrowser
|
||||
from typing import List, Union
|
||||
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
@@ -10,9 +12,6 @@ from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
get_flair,
|
||||
system_clear,
|
||||
MENU_GO_BACK,
|
||||
MENU_QUIT,
|
||||
MENU_RESET,
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
|
||||
@@ -23,10 +22,15 @@ class ResourceCollectionController:
|
||||
# Command choices
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
@@ -42,25 +46,26 @@ class ResourceCollectionController:
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self):
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Constructor"""
|
||||
self.rc_parser = argparse.ArgumentParser(add_help=False, prog="resources")
|
||||
self.rc_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.queue = queue if queue else list()
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
@staticmethod
|
||||
def print_help():
|
||||
"""Print help"""
|
||||
help_str = """
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
reset reset terminal and reload configs
|
||||
|
||||
Resource Collection:
|
||||
hfletters hedge fund letters or reports
|
||||
arxiv open-access archive for academic articles
|
||||
finra self-regulatory organization
|
||||
@@ -76,31 +81,40 @@ What do you want to do?
|
||||
|
||||
Returns
|
||||
-------
|
||||
MENU_GO_BACK, MENU_QUIT, MENU_RESET
|
||||
MENU_GO_BACK - Show main context menu again
|
||||
MENU_QUIT - Quit terminal
|
||||
MENU_RESET - Reset terminal and go back to same previous menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.rc_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
|
||||
if other_args:
|
||||
print(f"The following args were unexpected: {other_args}")
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -108,83 +122,160 @@ What do you want to do?
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return MENU_GO_BACK
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - exit the program"""
|
||||
return MENU_QUIT
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process Reset command - reset the program"""
|
||||
return MENU_RESET
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "resources")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "reset", "resources"]
|
||||
|
||||
def call_hfletters(self, _):
|
||||
"""Process hfletters command"""
|
||||
webbrowser.open("https://miltonfmr.com/hedge-fund-letters/")
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
def call_arxiv(self, _):
|
||||
"""Process arxiv command"""
|
||||
webbrowser.open("https://arxiv.org")
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
def call_finra(self, _):
|
||||
"""Process finra command"""
|
||||
webbrowser.open("https://www.finra.org/#/")
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
def call_edgar(self, _):
|
||||
"""Process edgar command"""
|
||||
webbrowser.open("https://www.sec.gov/edgar.shtml")
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
def call_fred(self, _):
|
||||
"""Process fred command"""
|
||||
webbrowser.open("https://fred.stlouisfed.org")
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
def call_learn(self, _):
|
||||
"""Process learn command"""
|
||||
webbrowser.open("https://moongangcapital.com/free-stock-market-resources/")
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
def call_econiverse(self, _):
|
||||
"""Process econiverse command"""
|
||||
webbrowser.open("https://econiverse.github.io")
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu():
|
||||
def menu(queue: List[str] = None):
|
||||
"""Resource Collection Menu"""
|
||||
|
||||
rc_controller = ResourceCollectionController()
|
||||
rc_controller.call_help(None)
|
||||
rc_controller = ResourceCollectionController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if rc_controller.queue and len(rc_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if rc_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(rc_controller.queue) > 1:
|
||||
return rc_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = rc_controller.queue[0]
|
||||
rc_controller.queue = rc_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in rc_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /resources/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in rc_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (resources)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (resources)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
rc_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and rc_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /resources/ $ ",
|
||||
completer=rc_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /resources/ $ ")
|
||||
|
||||
try:
|
||||
process_input = rc_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
rc_controller.queue = rc_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /resources menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
rc_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
rc_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
rc_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -3,10 +3,9 @@ __docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
|
||||
import matplotlib as mpl
|
||||
import matplotlib.pyplot as plt
|
||||
import pandas as pd
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
@@ -16,10 +15,10 @@ from gamestonk_terminal.helper_funcs import (
|
||||
get_flair,
|
||||
parse_known_args_and_warn,
|
||||
try_except,
|
||||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||||
system_clear,
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.stocks import stocks_helper
|
||||
|
||||
# This code below aims to fix an issue with the fnn module, used by bt module
|
||||
# which forces matplotlib backend to be 'agg' which doesn't allow to plot
|
||||
@@ -35,11 +34,23 @@ mpl.use(default_backend)
|
||||
class BacktestingController:
|
||||
"""Backtesting Class"""
|
||||
|
||||
CHOICES = ["?", "cls", "help", "q", "quit", "load"]
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
CHOICES_COMMANDS = ["ema", "ema_cross", "rsi"]
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self, ticker: str, stock: pd.DataFrame):
|
||||
def __init__(self, ticker: str, stock: pd.DataFrame, queue: List[str] = None):
|
||||
self.ticker = ticker
|
||||
self.stock = stock
|
||||
self.bt_parser = argparse.ArgumentParser(add_help=False, prog="bt")
|
||||
@@ -47,17 +58,18 @@ class BacktestingController:
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
self.queue = queue if queue else list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = f"""
|
||||
Backtesting:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
load load new ticker to analyze
|
||||
|
||||
Ticker: {self.ticker.upper()}
|
||||
|
||||
ema buy when price exceeds EMA(l)
|
||||
@@ -71,28 +83,40 @@ Ticker: {self.ticker.upper()}
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.bt_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -100,54 +124,56 @@ Ticker: {self.ticker.upper()}
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_load(self, other_args: List[str]):
|
||||
"""Process load command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="load",
|
||||
description="Load stock ticker to perform analysis on. When the data source is 'yf', an Indian ticker can be"
|
||||
" loaded by using '.NS' at the end, e.g. 'SBIN.NS'. See available market in"
|
||||
" https://help.yahoo.com/kb/exchanges-data-providers-yahoo-finance-sln2310.html.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--ticker",
|
||||
action="store",
|
||||
dest="ticker",
|
||||
required="-h" not in other_args,
|
||||
help="Stock ticker",
|
||||
)
|
||||
# For the case where a user uses: 'load BB'
|
||||
if other_args and "-t" not in other_args and "-h" not in other_args:
|
||||
other_args.insert(0, "-t")
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "bt")
|
||||
if self.ticker:
|
||||
self.queue.insert(0, f"load {self.ticker}")
|
||||
self.queue.insert(0, "stocks")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
df_stock_candidate = stocks_helper.load(
|
||||
ns_parser.ticker,
|
||||
)
|
||||
|
||||
if not df_stock_candidate.empty:
|
||||
self.stock = df_stock_candidate
|
||||
if "." in ns_parser.ticker:
|
||||
self.ticker = ns_parser.ticker.upper().split(".")[0]
|
||||
else:
|
||||
self.ticker = ns_parser.ticker.upper()
|
||||
reset_commands = ["quit", "quit", "reset", "stocks"]
|
||||
if self.ticker:
|
||||
reset_commands.append(f"load {self.ticker}")
|
||||
reset_commands.append("bt")
|
||||
return reset_commands
|
||||
|
||||
@try_except
|
||||
def call_ema(self, other_args: List[str]):
|
||||
@@ -179,27 +205,20 @@ Ticker: {self.ticker.upper()}
|
||||
help="Flag to not show buy and hold comparison",
|
||||
dest="no_bench",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
bt_view.display_simple_ema(
|
||||
ticker=self.ticker,
|
||||
df_stock=self.stock,
|
||||
ema_length=ns_parser.length,
|
||||
spy_bt=ns_parser.spy,
|
||||
no_bench=ns_parser.no_bench,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
bt_view.display_simple_ema(
|
||||
ticker=self.ticker,
|
||||
df_stock=self.stock,
|
||||
ema_length=ns_parser.length,
|
||||
spy_bt=ns_parser.spy,
|
||||
no_bench=ns_parser.no_bench,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ema_cross(self, other_args: List[str]):
|
||||
@@ -247,33 +266,27 @@ Ticker: {self.ticker.upper()}
|
||||
dest="shortable",
|
||||
help="Flag that disables the short sell",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser.long < ns_parser.short:
|
||||
print("Short EMA period is longer than Long EMA period\n")
|
||||
return self.queue
|
||||
|
||||
if ns_parser.long < ns_parser.short:
|
||||
print("Short EMA period is longer than Long EMA period\n")
|
||||
return
|
||||
|
||||
bt_view.display_ema_cross(
|
||||
ticker=self.ticker,
|
||||
df_stock=self.stock,
|
||||
short_ema=ns_parser.short,
|
||||
long_ema=ns_parser.long,
|
||||
spy_bt=ns_parser.spy,
|
||||
no_bench=ns_parser.no_bench,
|
||||
shortable=ns_parser.shortable,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
bt_view.display_ema_cross(
|
||||
ticker=self.ticker,
|
||||
df_stock=self.stock,
|
||||
short_ema=ns_parser.short,
|
||||
long_ema=ns_parser.long,
|
||||
spy_bt=ns_parser.spy,
|
||||
no_bench=ns_parser.no_bench,
|
||||
shortable=ns_parser.shortable,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_rsi(self, other_args: List[str]):
|
||||
@@ -330,69 +343,99 @@ Ticker: {self.ticker.upper()}
|
||||
dest="shortable",
|
||||
help="Flag that disables the short sell",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
if ns_parser.high < ns_parser.low:
|
||||
print("Low RSI value is higher than Low RSI value\n")
|
||||
return self.queue
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if ns_parser.high < ns_parser.low:
|
||||
print("Low RSI value is higher than Low RSI value\n")
|
||||
return
|
||||
|
||||
bt_view.display_rsi_strategy(
|
||||
ticker=self.ticker,
|
||||
df_stock=self.stock,
|
||||
periods=ns_parser.periods,
|
||||
low_rsi=ns_parser.low,
|
||||
high_rsi=ns_parser.high,
|
||||
spy_bt=ns_parser.spy,
|
||||
no_bench=ns_parser.no_bench,
|
||||
shortable=ns_parser.shortable,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
bt_view.display_rsi_strategy(
|
||||
ticker=self.ticker,
|
||||
df_stock=self.stock,
|
||||
periods=ns_parser.periods,
|
||||
low_rsi=ns_parser.low,
|
||||
high_rsi=ns_parser.high,
|
||||
spy_bt=ns_parser.spy,
|
||||
no_bench=ns_parser.no_bench,
|
||||
shortable=ns_parser.shortable,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(ticker: str, stock: pd.DataFrame):
|
||||
def menu(ticker: str, stock: pd.DataFrame, queue: List[str] = None):
|
||||
"""Backtesting Menu"""
|
||||
plt.close("all")
|
||||
bt_controller = BacktestingController(ticker, stock)
|
||||
bt_controller.call_help(None)
|
||||
bt_controller = BacktestingController(ticker, stock, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if bt_controller.queue and len(bt_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if bt_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(bt_controller.queue) > 1:
|
||||
return bt_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = bt_controller.queue[0]
|
||||
bt_controller.queue = bt_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in bt_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /stocks/bt/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in bt_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (stocks)>(bt)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (stocks)>(bt)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
bt_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and bt_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /stocks/bt/ $ ",
|
||||
completer=bt_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /stocks/bt/ $ ")
|
||||
|
||||
try:
|
||||
plt.close("all")
|
||||
|
||||
process_input = bt_controller.switch(an_input)
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
bt_controller.queue = bt_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, bt_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/bt menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
bt_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
bt_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
continue
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
bt_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -11,6 +11,7 @@ import pandas as pd
|
||||
import seaborn as sns
|
||||
from pandas.plotting import register_matplotlib_converters
|
||||
from sklearn.preprocessing import MinMaxScaler
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
|
||||
from gamestonk_terminal.config_plot import PLOT_DPI
|
||||
from gamestonk_terminal.helper_funcs import export_data, plot_autoscale
|
||||
@@ -76,6 +77,8 @@ def display_historical(
|
||||
ax.set_xlim([df_similar.index[0], df_similar.index[-1]])
|
||||
plt.gcf().autofmt_xdate()
|
||||
fig.tight_layout()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
export_data(
|
||||
export, os.path.dirname(os.path.abspath(__file__)), "historical", df_similar
|
||||
@@ -96,8 +99,6 @@ def display_volume(
|
||||
List of similar tickers
|
||||
start : str, optional
|
||||
Start date of comparison, by default 1 year ago
|
||||
normalize : bool, optional
|
||||
Boolean to normalize all stock prices using MinMax defaults True
|
||||
export : str, optional
|
||||
Format to export historical prices, by default ""
|
||||
"""
|
||||
|
||||
@@ -1,16 +1,14 @@
|
||||
""" Dark Pool Shorts Controller """
|
||||
""" Dark Pool and Shorts Controller Module """
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
from datetime import datetime, timedelta
|
||||
from colorama import Style
|
||||
import pandas as pd
|
||||
from matplotlib import pyplot as plt
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import EXPORT_BOTH_RAW_DATA_AND_FIGURES, get_flair
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
parse_known_args_and_warn,
|
||||
@@ -19,6 +17,9 @@ from gamestonk_terminal.helper_funcs import (
|
||||
check_int_range,
|
||||
try_except,
|
||||
system_clear,
|
||||
get_flair,
|
||||
EXPORT_BOTH_RAW_DATA_AND_FIGURES,
|
||||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||||
)
|
||||
from gamestonk_terminal.stocks import stocks_helper
|
||||
from gamestonk_terminal.stocks.dark_pool_shorts import (
|
||||
@@ -33,51 +34,64 @@ from gamestonk_terminal.stocks.dark_pool_shorts import (
|
||||
|
||||
|
||||
class DarkPoolShortsController:
|
||||
"""Dark Pool Shorts Controller"""
|
||||
"""Dark Pool Shorts Controller class"""
|
||||
|
||||
# Command choices
|
||||
CHOICES = [
|
||||
"?",
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"load",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS = [
|
||||
"load",
|
||||
"shorted",
|
||||
"hsi",
|
||||
"prom",
|
||||
"pos",
|
||||
"sidtc",
|
||||
"psi",
|
||||
"dpotc",
|
||||
"ftd",
|
||||
"spos",
|
||||
"volexch",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS_WITH_TICKER = ["psi", "dpotc", "ftd", "spos", "volexch"]
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
CHOICES += CHOICES_COMMANDS_WITH_TICKER
|
||||
|
||||
def __init__(self, ticker: str, start: str, stock: pd.DataFrame):
|
||||
def __init__(
|
||||
self, ticker: str, start: str, stock: pd.DataFrame, queue: List[str] = None
|
||||
):
|
||||
"""Constructor"""
|
||||
self.ticker = ticker
|
||||
self.start = start
|
||||
self.stock = stock
|
||||
|
||||
self.disc_parser = argparse.ArgumentParser(add_help=False, prog="dps")
|
||||
self.disc_parser.add_argument(
|
||||
self.dps_parser = argparse.ArgumentParser(add_help=False, prog="dps")
|
||||
self.dps_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
self.ticker = ticker
|
||||
self.start = start
|
||||
self.stock = stock
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = f"""
|
||||
Dark Pool Shorts:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
load load a specific stock ticker for analysis
|
||||
|
||||
Yahoo Finance:
|
||||
@@ -110,27 +124,40 @@ NYSE:
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
(known_args, other_args) = self.disc_parser.parse_known_args(an_input.split())
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.dps_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -138,18 +165,56 @@ NYSE:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
if self.ticker:
|
||||
self.queue.insert(0, f"load {self.ticker}")
|
||||
self.queue.insert(0, "dps")
|
||||
self.queue.insert(0, "stocks")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
reset_commands = ["quit", "quit", "reset", "stocks", "dps"]
|
||||
if self.ticker:
|
||||
reset_commands.append(f"load {self.ticker}")
|
||||
return reset_commands
|
||||
|
||||
@try_except
|
||||
def call_load(self, other_args: List[str]):
|
||||
"""Process load command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -176,26 +241,24 @@ NYSE:
|
||||
dest="start",
|
||||
help="The starting date (format YYYY-MM-DD) of the stock",
|
||||
)
|
||||
# For the case where a user uses: 'load BB'
|
||||
if other_args and "-t" not in other_args and "-h" not in other_args:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
df_stock_candidate = stocks_helper.load(
|
||||
ns_parser.ticker,
|
||||
ns_parser.start,
|
||||
)
|
||||
|
||||
df_stock_candidate = stocks_helper.load(
|
||||
ns_parser.ticker,
|
||||
ns_parser.start,
|
||||
)
|
||||
if not df_stock_candidate.empty:
|
||||
self.stock = df_stock_candidate
|
||||
self.start = ns_parser.start
|
||||
if "." in ns_parser.ticker:
|
||||
self.ticker = ns_parser.ticker.upper().split(".")[0]
|
||||
else:
|
||||
self.ticker = ns_parser.ticker.upper()
|
||||
|
||||
if not df_stock_candidate.empty:
|
||||
self.stock = df_stock_candidate
|
||||
self.start = ns_parser.start
|
||||
if "." in ns_parser.ticker:
|
||||
self.ticker = ns_parser.ticker.upper().split(".")[0]
|
||||
else:
|
||||
self.ticker = ns_parser.ticker.upper()
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_shorted(self, other_args: List[str]):
|
||||
@@ -207,34 +270,26 @@ NYSE:
|
||||
description="Print up to 25 top ticker most shorted. [Source: Yahoo Finance]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
type=check_int_range(1, 25),
|
||||
default=5,
|
||||
help="Number of the most shorted stocks to retrieve.",
|
||||
default=10,
|
||||
help="Limit of the most shorted stocks to retrieve.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-n")
|
||||
if ns_parser:
|
||||
yahoofinance_view.display_most_shorted(
|
||||
num_stocks=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
yahoofinance_view.display_most_shorted(
|
||||
num_stocks=ns_parser.num,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_hsi(self, other_args: List[str]):
|
||||
@@ -252,30 +307,24 @@ NYSE:
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="n_num",
|
||||
type=check_positive,
|
||||
dest="limit",
|
||||
type=check_int_range(1, 25),
|
||||
default=10,
|
||||
help="Number of top stocks to print.",
|
||||
help="Limit of the top heavily shorted stocks to retrieve.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
shortinterest_view.high_short_interest(
|
||||
num=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
shortinterest_view.high_short_interest(
|
||||
num=ns_parser.n_num,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_prom(self, other_args: List[str]):
|
||||
@@ -284,7 +333,7 @@ NYSE:
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="prom",
|
||||
description="Display dark pool (ATS) data of tickers with growing trades activity",
|
||||
description="Display dark pool (ATS) data of tickers with growing trades activity using linear regression.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
@@ -296,15 +345,16 @@ NYSE:
|
||||
help="Number of tickers to filter from entire ATS data based on the sum of the total weekly shares quantity.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--top",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="n_top",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=5,
|
||||
help="List of tickers from most promising with better linear regression slope.",
|
||||
default=10,
|
||||
help="Limit of most promising tickers to display.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--tier",
|
||||
action="store",
|
||||
dest="tier",
|
||||
@@ -313,23 +363,20 @@ NYSE:
|
||||
default="",
|
||||
help="Tier to process data from.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
finra_view.darkpool_otc(
|
||||
num=ns_parser.n_num,
|
||||
promising=ns_parser.limit,
|
||||
tier=ns_parser.tier,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
finra_view.darkpool_otc(
|
||||
num=ns_parser.n_num,
|
||||
promising=ns_parser.n_top,
|
||||
tier=ns_parser.tier,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_pos(self, other_args: List[str]):
|
||||
@@ -341,12 +388,13 @@ NYSE:
|
||||
description="Get dark pool short positions. [Source: Stockgrid]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--number",
|
||||
help="Number of top tickers to show",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
dest="num",
|
||||
help="Limit of tickers to display.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
@@ -367,23 +415,18 @@ NYSE:
|
||||
dest="ascending",
|
||||
help="Data in ascending order",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
stockgrid_view.dark_pool_short_positions(
|
||||
num=ns_parser.limit,
|
||||
sort_field=ns_parser.sort_field,
|
||||
ascending=ns_parser.ascending,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
stockgrid_view.dark_pool_short_positions(
|
||||
num=ns_parser.num,
|
||||
sort_field=ns_parser.sort_field,
|
||||
ascending=ns_parser.ascending,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_sidtc(self, other_args: List[str]):
|
||||
@@ -395,12 +438,13 @@ NYSE:
|
||||
description="Print short interest and days to cover. [Source: Stockgrid]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--number",
|
||||
help="Number of top tickers to show",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
dest="num",
|
||||
help="Limit of tickers to display.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
@@ -411,22 +455,17 @@ NYSE:
|
||||
default="float",
|
||||
dest="sort_field",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
stockgrid_view.short_interest_days_to_cover(
|
||||
num=ns_parser.limit,
|
||||
sort_field=ns_parser.sort_field,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
stockgrid_view.short_interest_days_to_cover(
|
||||
num=ns_parser.num,
|
||||
sort_field=ns_parser.sort_field,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_dpotc(self, other_args: List[str]):
|
||||
@@ -437,24 +476,19 @@ NYSE:
|
||||
prog="dpotc",
|
||||
description="Display barchart of dark pool (ATS) and OTC (Non ATS) data. [Source: FINRA]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if not self.ticker:
|
||||
print("No ticker loaded.\n")
|
||||
return
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
finra_view.darkpool_ats_otc(
|
||||
ticker=self.ticker,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded.\n")
|
||||
|
||||
finra_view.darkpool_ats_otc(
|
||||
ticker=self.ticker,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ftd(self, other_args: List[str]):
|
||||
@@ -498,30 +532,24 @@ NYSE:
|
||||
dest="raw",
|
||||
help="Print raw data.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
sec_view.fails_to_deliver(
|
||||
ticker=self.ticker,
|
||||
stock=self.stock,
|
||||
start=ns_parser.start,
|
||||
end=ns_parser.end,
|
||||
num=ns_parser.n_num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded.\n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if not self.ticker:
|
||||
print("No ticker loaded.\n")
|
||||
return
|
||||
|
||||
sec_view.fails_to_deliver(
|
||||
ticker=self.ticker,
|
||||
stock=self.stock,
|
||||
start=ns_parser.start,
|
||||
end=ns_parser.end,
|
||||
num=ns_parser.n_num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_spos(self, other_args: List[str]):
|
||||
@@ -541,31 +569,27 @@ NYSE:
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--raw",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Flag to print raw data instead",
|
||||
dest="raw",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if not self.ticker:
|
||||
print("No ticker loaded.\n")
|
||||
return
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
stockgrid_view.net_short_position(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded.\n")
|
||||
|
||||
stockgrid_view.net_short_position(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_psi(self, other_args: List[str]):
|
||||
@@ -582,13 +606,14 @@ NYSE:
|
||||
dest="stockgrid",
|
||||
help="Source of short interest volume",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--nyse",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="b_nyse",
|
||||
help="ONLY QUANDL SOURCE. Data from NYSE flag. Otherwise comes from NASDAQ.",
|
||||
)
|
||||
if "quandl" in other_args:
|
||||
parser.add_argument(
|
||||
"--nyse",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="b_nyse",
|
||||
help="Data from NYSE flag. Otherwise comes from NASDAQ.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--number",
|
||||
@@ -599,40 +624,36 @@ NYSE:
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--raw",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Flag to print raw data instead",
|
||||
dest="raw",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if not self.ticker:
|
||||
print("No ticker loaded.\n")
|
||||
return
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
if "quandl" in other_args:
|
||||
quandl_view.short_interest(
|
||||
ticker=self.ticker,
|
||||
nyse=ns_parser.b_nyse,
|
||||
days=ns_parser.num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
stockgrid_view.short_interest_volume(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded.\n")
|
||||
|
||||
if "quandl" in other_args:
|
||||
quandl_view.short_interest(
|
||||
ticker=self.ticker,
|
||||
nyse=ns_parser.b_nyse,
|
||||
days=ns_parser.num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
stockgrid_view.short_interest_volume(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_volexch(self, other_args: List[str]):
|
||||
@@ -643,6 +664,7 @@ NYSE:
|
||||
description="Displays short volume based on exchange.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-r",
|
||||
"--raw",
|
||||
help="Display raw data",
|
||||
dest="raw",
|
||||
@@ -677,65 +699,98 @@ NYSE:
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
if not self.ticker:
|
||||
print("No ticker loaded. Use `load ticker` first.")
|
||||
return
|
||||
nyse_view.display_short_by_exchange(
|
||||
ticker=self.ticker,
|
||||
raw=ns_parser.raw,
|
||||
sort=ns_parser.sort,
|
||||
asc=ns_parser.asc,
|
||||
mpl=ns_parser.mpl,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
nyse_view.display_short_by_exchange(
|
||||
ticker=self.ticker,
|
||||
raw=ns_parser.raw,
|
||||
sort=ns_parser.sort,
|
||||
asc=ns_parser.asc,
|
||||
mpl=ns_parser.mpl,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded. Use `load ticker` first.")
|
||||
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(ticker: str = "", start: str = "", stock: pd.DataFrame = pd.DataFrame()):
|
||||
"""Dark Pool Shorts Menu
|
||||
def menu(
|
||||
ticker: str = "",
|
||||
start: str = "",
|
||||
stock: pd.DataFrame = pd.DataFrame(),
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Dark Pool Shorts Menu"""
|
||||
dps_controller = DarkPoolShortsController(ticker, start, stock, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
Parameters
|
||||
----------
|
||||
stock : DataFrame
|
||||
Due diligence stock dataframe
|
||||
ticker : str
|
||||
Due diligence ticker symbol
|
||||
start : str
|
||||
Start date of the stock data
|
||||
"""
|
||||
dps_controller = DarkPoolShortsController(ticker, start, stock)
|
||||
dps_controller.call_help(None)
|
||||
|
||||
# Loop forever and ever
|
||||
while True:
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in dps_controller.CHOICES}
|
||||
)
|
||||
# There is a command in the queue
|
||||
if dps_controller.queue and len(dps_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if dps_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(dps_controller.queue) > 1:
|
||||
return dps_controller.queue[1:]
|
||||
return []
|
||||
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (stocks)>(dps)> ",
|
||||
completer=completer,
|
||||
)
|
||||
# Consume 1 element from the queue
|
||||
an_input = dps_controller.queue[0]
|
||||
dps_controller.queue = dps_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in dps_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /stocks/dps/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (stocks)>(dps)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
dps_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and dps_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /stocks/dps/ $ ",
|
||||
completer=dps_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /stocks/dps/ $ ")
|
||||
|
||||
try:
|
||||
plt.close("all")
|
||||
|
||||
process_input = dps_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
dps_controller.queue = dps_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, dps_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/dps menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
dps_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
dps_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
dps_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -34,6 +34,10 @@ def get_high_short_interest() -> DataFrame:
|
||||
a_high_short_interest_header.append(
|
||||
high_short_interest_header.text.strip("\n").split("\n")[0]
|
||||
)
|
||||
|
||||
if not a_high_short_interest_header:
|
||||
return pd.DataFrame()
|
||||
|
||||
df_high_short_interest = pd.DataFrame(columns=a_high_short_interest_header)
|
||||
df_high_short_interest.loc[0] = ["", "", "", "", "", "", ""]
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from tabulate import tabulate
|
||||
from gamestonk_terminal.helper_funcs import export_data
|
||||
|
||||
from gamestonk_terminal.stocks.dark_pool_shorts import shortinterest_model
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
|
||||
|
||||
def high_short_interest(num: int, export: str):
|
||||
@@ -19,22 +20,30 @@ def high_short_interest(num: int, export: str):
|
||||
Export dataframe data to csv,json,xlsx file
|
||||
"""
|
||||
df_high_short_interest = shortinterest_model.get_high_short_interest()
|
||||
|
||||
if df_high_short_interest.empty:
|
||||
print("No data available.", "\n")
|
||||
return
|
||||
|
||||
df_high_short_interest = df_high_short_interest.iloc[1:].head(n=num)
|
||||
|
||||
print(
|
||||
tabulate(
|
||||
df_high_short_interest,
|
||||
headers=df_high_short_interest.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df_high_short_interest,
|
||||
headers=df_high_short_interest.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df_high_short_interest.to_string())
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"lowfloat",
|
||||
"hsi",
|
||||
df_high_short_interest,
|
||||
)
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -48,15 +48,18 @@ def price_target_from_analysts(
|
||||
|
||||
if raw:
|
||||
df_analyst_data.index = df_analyst_data.index.strftime("%d/%m/%Y")
|
||||
print(
|
||||
tabulate(
|
||||
df_analyst_data.sort_index(ascending=False).head(num),
|
||||
headers=df_analyst_data.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=True,
|
||||
tablefmt="fancy_grid",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df_analyst_data.sort_index(ascending=False).head(num),
|
||||
headers=df_analyst_data.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=True,
|
||||
tablefmt="fancy_grid",
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(df_analyst_data.head(num).to_string())
|
||||
|
||||
else:
|
||||
# Slice start of ratings
|
||||
@@ -86,7 +89,7 @@ def price_target_from_analysts(
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.gcf().autofmt_xdate()
|
||||
plt.show()
|
||||
print("")
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ __docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
from datetime import datetime, timedelta
|
||||
from pandas.core.frame import DataFrame
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
@@ -35,9 +35,22 @@ class DueDiligenceController:
|
||||
"""Due Diligence Controller"""
|
||||
|
||||
# Command choices
|
||||
CHOICES = ["?", "cls", "help", "q", "quit", "load"]
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS = [
|
||||
"load",
|
||||
"sec",
|
||||
"rating",
|
||||
"pt",
|
||||
@@ -51,20 +64,15 @@ class DueDiligenceController:
|
||||
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self, ticker: str, start: str, interval: str, stock: DataFrame):
|
||||
"""Constructor
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker : str
|
||||
Due diligence ticker symbol
|
||||
start : str
|
||||
Start date of the stock data
|
||||
interval : str
|
||||
Stock data interval
|
||||
stock : DataFrame
|
||||
Due diligence stock dataframe
|
||||
"""
|
||||
def __init__(
|
||||
self,
|
||||
ticker: str,
|
||||
start: str,
|
||||
interval: str,
|
||||
stock: DataFrame,
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Constructor"""
|
||||
self.ticker = ticker
|
||||
self.start = start
|
||||
self.interval = interval
|
||||
@@ -75,17 +83,21 @@ class DueDiligenceController:
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["load"]["-i"] = {c: {} for c in stocks_helper.INTERVALS}
|
||||
choices["load"]["-s"] = {c: {} for c in stocks_helper.SOURCES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = f"""
|
||||
Due Diligence:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
load load new ticker
|
||||
|
||||
Ticker: {self.ticker}
|
||||
|
||||
Finviz:
|
||||
@@ -109,31 +121,47 @@ cathiesark.com
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Parameters
|
||||
-------
|
||||
an_input : str
|
||||
string with input arguments
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.dd_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -141,17 +169,57 @@ cathiesark.com
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "dd")
|
||||
if self.ticker:
|
||||
self.queue.insert(0, f"load {self.ticker}")
|
||||
self.queue.insert(0, "stocks")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
reset_commands = ["quit", "quit", "reset", "stocks"]
|
||||
if self.ticker:
|
||||
reset_commands.append(f"load {self.ticker}")
|
||||
reset_commands.append("dd")
|
||||
return reset_commands
|
||||
|
||||
def call_load(self, other_args: List[str]):
|
||||
"""Process load command"""
|
||||
@@ -186,29 +254,40 @@ cathiesark.com
|
||||
dest="interval",
|
||||
type=int,
|
||||
default=1440,
|
||||
choices=[1, 5, 15, 30, 60],
|
||||
choices=stocks_helper.INTERVALS,
|
||||
help="Intraday stock minutes",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--source",
|
||||
action="store",
|
||||
dest="source",
|
||||
choices=stocks_helper.SOURCES,
|
||||
default="yf",
|
||||
help="Source of historical data.",
|
||||
)
|
||||
# For the case where a user uses: 'load BB'
|
||||
if other_args and "-t" not in other_args and "-h" not in other_args:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
df_stock_candidate = stocks_helper.load(
|
||||
ns_parser.ticker,
|
||||
ns_parser.start,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
df_stock_candidate = stocks_helper.load(
|
||||
ticker=ns_parser.ticker,
|
||||
start=ns_parser.start,
|
||||
interval=ns_parser.interval,
|
||||
source=ns_parser.source,
|
||||
)
|
||||
|
||||
if not df_stock_candidate.empty:
|
||||
self.stock = df_stock_candidate
|
||||
self.start = ns_parser.start
|
||||
if "." in ns_parser.ticker:
|
||||
self.ticker = ns_parser.ticker.upper().split(".")[0]
|
||||
else:
|
||||
self.ticker = ns_parser.ticker.upper()
|
||||
if not df_stock_candidate.empty:
|
||||
self.stock = df_stock_candidate
|
||||
self.start = ns_parser.start
|
||||
if "." in ns_parser.ticker:
|
||||
self.ticker = ns_parser.ticker.upper().split(".")[0]
|
||||
else:
|
||||
self.ticker = ns_parser.ticker.upper()
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_analyst(self, other_args: List[str]):
|
||||
@@ -221,19 +300,14 @@ cathiesark.com
|
||||
date, analyst, category, price from, price to, and rating. [Source: Finviz]
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
finviz_view.analyst(ticker=self.ticker, export=ns_parser.export)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
finviz_view.analyst(ticker=self.ticker, export=ns_parser.export)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_pt(self, other_args: List[str]):
|
||||
@@ -250,35 +324,31 @@ cathiesark.com
|
||||
help="Only output raw data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="n_num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
help="Number of latest price targets from analysts to print.",
|
||||
help="Limit of latest price targets from analysts to print.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
business_insider_view.price_target_from_analysts(
|
||||
ticker=self.ticker,
|
||||
start=self.start,
|
||||
interval=self.interval,
|
||||
stock=self.stock,
|
||||
num=ns_parser.n_num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
business_insider_view.price_target_from_analysts(
|
||||
ticker=self.ticker,
|
||||
start=self.start,
|
||||
interval=self.interval,
|
||||
stock=self.stock,
|
||||
num=ns_parser.limit,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_est(self, other_args: List[str]):
|
||||
@@ -288,22 +358,17 @@ cathiesark.com
|
||||
prog="est",
|
||||
description="""Yearly estimates and quarter earnings/revenues. [Source: Business Insider]""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
business_insider_view.estimates(
|
||||
ticker=self.ticker,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
business_insider_view.estimates(
|
||||
ticker=self.ticker,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_rot(self, other_args: List[str]):
|
||||
@@ -316,13 +381,13 @@ cathiesark.com
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="n_num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
help="number of last months",
|
||||
help="Limit of last months",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--raw",
|
||||
@@ -330,28 +395,21 @@ cathiesark.com
|
||||
dest="raw",
|
||||
help="Only output raw data",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
finnhub_view.rating_over_time(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.n_num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
finnhub_view.rating_over_time(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.limit,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_rating(self, other_args: List[str]):
|
||||
@@ -366,35 +424,28 @@ cathiesark.com
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="n_num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
help="number of last days to display ratings",
|
||||
help="limit of last days to display ratings",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
fmp_view.rating(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.n_num,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
fmp_view.rating(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_sec(self, other_args: List[str]):
|
||||
@@ -408,35 +459,29 @@ cathiesark.com
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="n_num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=5,
|
||||
help="number of latest SEC filings.",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-n")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
|
||||
marketwatch_view.sec_filings(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.n_num,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
marketwatch_view.sec_filings(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_supplier(self, other_args: List[str]):
|
||||
@@ -446,22 +491,16 @@ cathiesark.com
|
||||
add_help=False,
|
||||
description="List of suppliers from ticker provided. [Source: CSIMarket]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
csimarket_view.suppliers(
|
||||
ticker=self.ticker,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
csimarket_view.suppliers(
|
||||
ticker=self.ticker,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
def call_customer(self, other_args: List[str]):
|
||||
"""Process customer command"""
|
||||
@@ -470,22 +509,16 @@ cathiesark.com
|
||||
add_help=False,
|
||||
description="List of customers from ticker provided. [Source: CSIMarket]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--export",
|
||||
choices=["csv", "json", "xlsx"],
|
||||
default="",
|
||||
type=str,
|
||||
dest="export",
|
||||
help="Export dataframe data to csv,json,xlsx file",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
csimarket_view.customers(
|
||||
ticker=self.ticker,
|
||||
export=ns_parser.export,
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
csimarket_view.customers(
|
||||
ticker=self.ticker,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_arktrades(self, other_args):
|
||||
@@ -498,12 +531,12 @@ cathiesark.com
|
||||
""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
help="Number of rows to show",
|
||||
dest="num",
|
||||
default=20,
|
||||
type=int,
|
||||
"-l",
|
||||
"--limi",
|
||||
help="Limit of rows to show",
|
||||
dest="limit",
|
||||
default=10,
|
||||
type=check_positive,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
@@ -513,20 +546,28 @@ cathiesark.com
|
||||
help="Flag to show ticker in table",
|
||||
dest="show_ticker",
|
||||
)
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
ark_view.display_ark_trades(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.num,
|
||||
export=ns_parser.export,
|
||||
show_ticker=ns_parser.show_ticker,
|
||||
)
|
||||
if ns_parser:
|
||||
ark_view.display_ark_trades(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
show_ticker=ns_parser.show_ticker,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(ticker: str, start: str, interval: str, stock: DataFrame):
|
||||
def menu(
|
||||
ticker: str,
|
||||
start: str,
|
||||
interval: str,
|
||||
stock: DataFrame,
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Due Diligence Menu
|
||||
|
||||
Parameters
|
||||
@@ -539,37 +580,79 @@ def menu(ticker: str, start: str, interval: str, stock: DataFrame):
|
||||
Stock data interval
|
||||
stock : DataFrame
|
||||
Due diligence stock dataframe
|
||||
queue: List[str]
|
||||
List with commands in queue to run
|
||||
"""
|
||||
|
||||
dd_controller = DueDiligenceController(ticker, start, interval, stock)
|
||||
dd_controller.call_help(None)
|
||||
dd_controller = DueDiligenceController(ticker, start, interval, stock, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in dd_controller.CHOICES}
|
||||
)
|
||||
# There is a command in the queue
|
||||
if dd_controller.queue and len(dd_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if dd_controller.queue[0] in ("q", "..", "quit"):
|
||||
if len(dd_controller.queue) > 1:
|
||||
return dd_controller.queue[1:]
|
||||
return []
|
||||
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (stocks)>(dd)> ",
|
||||
completer=completer,
|
||||
)
|
||||
# Consume 1 element from the queue
|
||||
an_input = dd_controller.queue[0]
|
||||
dd_controller.queue = dd_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in dd_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /stocks/dd/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (stocks)>(dd)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
dd_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and dd_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /stocks/dd/ $ ",
|
||||
completer=dd_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /stocks/dd/ $ ")
|
||||
|
||||
try:
|
||||
process_input = dd_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
dd_controller.queue = dd_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, dd_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
dd_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
else:
|
||||
candidate_input = similar_cmd[0]
|
||||
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
dd_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
dd_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
an_input = ""
|
||||
dd_controller.queue = []
|
||||
|
||||
@@ -14,19 +14,19 @@ from gamestonk_terminal import feature_flags as gtff
|
||||
register_matplotlib_converters()
|
||||
|
||||
|
||||
def plot_rating_over_time(rot: pd.DataFrame, ticker: str):
|
||||
def plot_rating_over_time(df_rot: pd.DataFrame, ticker: str):
|
||||
"""Plot rating over time
|
||||
|
||||
Parameters
|
||||
----------
|
||||
rot : pd.DataFrame
|
||||
df_rot : pd.DataFrame
|
||||
Rating over time
|
||||
ticker : str
|
||||
Ticker associated with ratings
|
||||
"""
|
||||
plt.figure(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
|
||||
rot.sort_values("period", inplace=True)
|
||||
rot = df_rot.sort_values("period")
|
||||
plt.plot(pd.to_datetime(rot["period"]), rot["strongBuy"], c="green", lw=3)
|
||||
plt.plot(pd.to_datetime(rot["period"]), rot["buy"], c="lightgreen", lw=3)
|
||||
plt.plot(pd.to_datetime(rot["period"]), rot["hold"], c="grey", lw=3)
|
||||
@@ -82,15 +82,18 @@ def rating_over_time(ticker: str, num: int, raw: bool, export: str):
|
||||
.rename(columns=d_cols)
|
||||
.head(num)
|
||||
)
|
||||
print(
|
||||
tabulate(
|
||||
df_rot_raw,
|
||||
headers=df_rot_raw.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df_rot_raw,
|
||||
headers=df_rot_raw.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(df_rot_raw.to_string())
|
||||
else:
|
||||
plot_rating_over_time(df_rot.head(num), ticker)
|
||||
|
||||
|
||||
@@ -32,7 +32,12 @@ def get_overview(ticker: str) -> pd.DataFrame:
|
||||
# If the returned data was successful
|
||||
if result.status_code == 200:
|
||||
# Parse json data to dataframe
|
||||
if "Note" in result.json():
|
||||
print(result.json()["Note"], "\n")
|
||||
return pd.DataFrame()
|
||||
|
||||
df_fa = pd.json_normalize(result.json())
|
||||
|
||||
# Keep json data sorting in dataframe
|
||||
df_fa = df_fa[list(result.json().keys())].T
|
||||
df_fa.iloc[5:] = df_fa.iloc[5:].applymap(lambda x: long_number_format(x))
|
||||
@@ -136,18 +141,24 @@ def get_income_statements(
|
||||
"""
|
||||
url = f"https://www.alphavantage.co/query?function=INCOME_STATEMENT&symbol={ticker}&apikey={cfg.API_KEY_ALPHAVANTAGE}"
|
||||
r = requests.get(url)
|
||||
statements = r.json()
|
||||
if quarterly:
|
||||
# pylint: disable=unbalanced-tuple-unpacking
|
||||
df_fa = pd.DataFrame(statements["quarterlyReports"])
|
||||
else:
|
||||
# pylint: disable=unbalanced-tuple-unpacking
|
||||
df_fa = pd.DataFrame(statements["annualReports"])
|
||||
if r.status_code == 200:
|
||||
statements = r.json()
|
||||
df_fa = pd.DataFrame()
|
||||
if quarterly:
|
||||
if "quarterlyReports" in statements:
|
||||
df_fa = pd.DataFrame(statements["quarterlyReports"])
|
||||
else:
|
||||
if "annualReports" in statements:
|
||||
df_fa = pd.DataFrame(statements["annualReports"])
|
||||
|
||||
df_fa = df_fa.set_index("fiscalDateEnding")
|
||||
df_fa = df_fa.head(number)
|
||||
df_fa = df_fa.applymap(lambda x: long_number_format(x))
|
||||
return df_fa[::-1].T
|
||||
if df_fa.empty:
|
||||
return pd.DataFrame()
|
||||
|
||||
df_fa = df_fa.set_index("fiscalDateEnding")
|
||||
df_fa = df_fa.head(number)
|
||||
df_fa = df_fa.applymap(lambda x: long_number_format(x))
|
||||
return df_fa[::-1].T
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
def get_balance_sheet(
|
||||
@@ -171,16 +182,24 @@ def get_balance_sheet(
|
||||
"""
|
||||
url = f"https://www.alphavantage.co/query?function=BALANCE_SHEET&symbol={ticker}&apikey={cfg.API_KEY_ALPHAVANTAGE}"
|
||||
r = requests.get(url)
|
||||
statements = r.json()
|
||||
if quarterly:
|
||||
df_fa = pd.DataFrame(statements["quarterlyReports"])
|
||||
else:
|
||||
df_fa = pd.DataFrame(statements["annualReports"])
|
||||
if r.status_code == 200:
|
||||
statements = r.json()
|
||||
df_fa = pd.DataFrame()
|
||||
if quarterly:
|
||||
if "quarterlyReports" in statements:
|
||||
df_fa = pd.DataFrame(statements["quarterlyReports"])
|
||||
else:
|
||||
if "annualReports" in statements:
|
||||
df_fa = pd.DataFrame(statements["annualReports"])
|
||||
|
||||
df_fa = df_fa.set_index("fiscalDateEnding")
|
||||
df_fa = df_fa.head(number)
|
||||
df_fa = df_fa.applymap(lambda x: long_number_format(x))
|
||||
return df_fa[::-1].T
|
||||
if df_fa.empty:
|
||||
return pd.DataFrame()
|
||||
|
||||
df_fa = df_fa.set_index("fiscalDateEnding")
|
||||
df_fa = df_fa.head(number)
|
||||
df_fa = df_fa.applymap(lambda x: long_number_format(x))
|
||||
return df_fa[::-1].T
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
def get_cash_flow(ticker: str, number: int, quarterly: bool = False) -> pd.DataFrame:
|
||||
@@ -202,16 +221,24 @@ def get_cash_flow(ticker: str, number: int, quarterly: bool = False) -> pd.DataF
|
||||
"""
|
||||
url = f"https://www.alphavantage.co/query?function=CASH_FLOW&symbol={ticker}&apikey={cfg.API_KEY_ALPHAVANTAGE}"
|
||||
r = requests.get(url)
|
||||
statements = r.json()
|
||||
if quarterly:
|
||||
df_fa = pd.DataFrame(statements["quarterlyReports"])
|
||||
else:
|
||||
df_fa = pd.DataFrame(statements["annualReports"])
|
||||
if r.status_code == 200:
|
||||
statements = r.json()
|
||||
df_fa = pd.DataFrame()
|
||||
if quarterly:
|
||||
if "quarterlyReports" in statements:
|
||||
df_fa = pd.DataFrame(statements["quarterlyReports"])
|
||||
else:
|
||||
if "annualReports" in statements:
|
||||
df_fa = pd.DataFrame(statements["annualReports"])
|
||||
|
||||
df_fa = df_fa.set_index("fiscalDateEnding")
|
||||
df_fa = df_fa.head(number)
|
||||
df_fa = df_fa.applymap(lambda x: long_number_format(x))
|
||||
return df_fa[::-1].T
|
||||
if df_fa.empty:
|
||||
return pd.DataFrame()
|
||||
|
||||
df_fa = df_fa.set_index("fiscalDateEnding")
|
||||
df_fa = df_fa.head(number)
|
||||
df_fa = df_fa.applymap(lambda x: long_number_format(x))
|
||||
return df_fa[::-1].T
|
||||
return pd.DataFrame()
|
||||
|
||||
|
||||
def get_earnings(ticker: str, quarterly: bool = False) -> pd.DataFrame:
|
||||
|
||||
@@ -21,8 +21,9 @@ def display_overview(ticker: str):
|
||||
"""
|
||||
df_fa = av_model.get_overview(ticker)
|
||||
if df_fa.empty:
|
||||
print(f"No data found from alphavantage for {ticker}.\n")
|
||||
print("No API calls left. Try me later", "\n")
|
||||
return
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
@@ -45,9 +46,8 @@ def display_key(ticker: str):
|
||||
Fundamental analysis ticker symbol
|
||||
"""
|
||||
df_key = av_model.get_key_metrics(ticker)
|
||||
|
||||
if df_key.empty:
|
||||
print("Issue getting key metrics from alpha vantage.")
|
||||
print("No API calls left. Try me later", "\n")
|
||||
return
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
@@ -59,7 +59,7 @@ def display_key(ticker: str):
|
||||
|
||||
|
||||
def display_income_statement(
|
||||
ticker: str, number: int, quarterly: bool = False, export: str = ""
|
||||
ticker: str, limit: int, quarterly: bool = False, export: str = ""
|
||||
):
|
||||
"""Alpha Vantage income statement
|
||||
|
||||
@@ -67,14 +67,17 @@ def display_income_statement(
|
||||
----------
|
||||
ticker : str
|
||||
Fundamental analysis ticker symbol
|
||||
number: int
|
||||
limit: int
|
||||
Number of past statements
|
||||
quarterly: bool
|
||||
Flag to get quarterly instead of annual
|
||||
export: str
|
||||
Format to export data
|
||||
"""
|
||||
df_income = av_model.get_income_statements(ticker, number, quarterly)
|
||||
df_income = av_model.get_income_statements(ticker, limit, quarterly)
|
||||
if df_income.empty:
|
||||
print("No API calls left. Try me later", "\n")
|
||||
return
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(tabulate(df_income, headers=df_income.columns, tablefmt="fancy_grid"))
|
||||
@@ -86,7 +89,7 @@ def display_income_statement(
|
||||
|
||||
|
||||
def display_balance_sheet(
|
||||
ticker: str, number: int, quarterly: bool = False, export: str = ""
|
||||
ticker: str, limit: int, quarterly: bool = False, export: str = ""
|
||||
):
|
||||
"""Alpha Vantage income statement
|
||||
|
||||
@@ -94,14 +97,17 @@ def display_balance_sheet(
|
||||
----------
|
||||
ticker : str
|
||||
Fundamental analysis ticker symbol
|
||||
number: int
|
||||
limit: int
|
||||
Number of past statements
|
||||
quarterly: bool
|
||||
Flag to get quarterly instead of annual
|
||||
export: str
|
||||
Format to export data
|
||||
"""
|
||||
df_balance = av_model.get_balance_sheet(ticker, number, quarterly)
|
||||
df_balance = av_model.get_balance_sheet(ticker, limit, quarterly)
|
||||
if df_balance.empty:
|
||||
print("No API calls left. Try me later", "\n")
|
||||
return
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(tabulate(df_balance, headers=df_balance.columns, tablefmt="fancy_grid"))
|
||||
@@ -115,7 +121,7 @@ def display_balance_sheet(
|
||||
|
||||
|
||||
def display_cash_flow(
|
||||
ticker: str, number: int, quarterly: bool = False, export: str = ""
|
||||
ticker: str, limit: int, quarterly: bool = False, export: str = ""
|
||||
):
|
||||
"""Alpha Vantage income statement
|
||||
|
||||
@@ -123,14 +129,17 @@ def display_cash_flow(
|
||||
----------
|
||||
ticker : str
|
||||
Fundamental analysis ticker symbol
|
||||
number: int
|
||||
limit: int
|
||||
Number of past statements
|
||||
quarterly: bool
|
||||
Flag to get quarterly instead of annual
|
||||
export: str
|
||||
Format to export data
|
||||
"""
|
||||
df_cash = av_model.get_cash_flow(ticker, number, quarterly)
|
||||
df_cash = av_model.get_cash_flow(ticker, limit, quarterly)
|
||||
if df_cash.empty:
|
||||
print("No API calls left. Try me later", "\n")
|
||||
return
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(tabulate(df_cash, headers=df_cash.columns, tablefmt="fancy_grid"))
|
||||
@@ -141,33 +150,33 @@ def display_cash_flow(
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "cash", df_cash)
|
||||
|
||||
|
||||
def display_earnings(ticker: str, number: int, quarterly: bool = False):
|
||||
def display_earnings(ticker: str, limit: int, quarterly: bool = False):
|
||||
"""Alpha Vantage earnings
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker : str
|
||||
Fundamental analysis ticker symbol
|
||||
number:int
|
||||
limit:int
|
||||
Number of events to show
|
||||
quarterly: bool
|
||||
Flag to show quarterly instead of annual
|
||||
"""
|
||||
df_fa = av_model.get_earnings(ticker, quarterly)
|
||||
if df_fa.empty:
|
||||
print("Error getting earnings data.\n")
|
||||
print("No API calls left. Try me later", "\n")
|
||||
return
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df_fa.head(number),
|
||||
df_fa.head(limit),
|
||||
headers=df_fa.columns,
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(df_fa.head(n=number).T.to_string(header=False))
|
||||
print(df_fa.head(n=limit).T.to_string(header=False))
|
||||
|
||||
print("")
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ from openpyxl import Workbook, worksheet
|
||||
from openpyxl.styles import Font
|
||||
from sklearn.linear_model import LinearRegression
|
||||
from bs4 import BeautifulSoup
|
||||
import regex as re
|
||||
import yfinance as yf
|
||||
import pandas as pd
|
||||
import numpy as np
|
||||
@@ -114,7 +115,7 @@ class CreateExcelFA:
|
||||
soup = BeautifulSoup(r.content, "html.parser")
|
||||
|
||||
table = soup.find(
|
||||
"table", attrs={"class": "FinancialTable_table_financial__1RhYq"}
|
||||
"table", attrs={"class": re.compile("^FinancialTable_table_financial__.*")}
|
||||
)
|
||||
head = table.find("thead")
|
||||
columns = head.find_all("th")
|
||||
@@ -1288,7 +1289,7 @@ class CreateExcelFA:
|
||||
soup = BeautifulSoup(r.content, "html.parser")
|
||||
|
||||
table = soup.find(
|
||||
"table", attrs={"class": "FinancialTable_table_financial__1RhYq"}
|
||||
"table", attrs={"class": re.compile("^FinancialTable_table_financial__.*")}
|
||||
)
|
||||
head = table.find("thead")
|
||||
if head is None:
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,7 +2,8 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
import difflib
|
||||
from typing import List, Union
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from gamestonk_terminal.stocks.fundamental_analysis.financial_modeling_prep import (
|
||||
@@ -24,7 +25,20 @@ class FinancialModelingPrepController:
|
||||
"""Financial Modeling Prep Controller"""
|
||||
|
||||
# Command choices
|
||||
CHOICES = ["cls", "?", "help", "q", "quit"]
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS = [
|
||||
"profile",
|
||||
"quote",
|
||||
@@ -39,7 +53,13 @@ class FinancialModelingPrepController:
|
||||
]
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
def __init__(self, ticker: str, start: str, interval: str):
|
||||
def __init__(
|
||||
self,
|
||||
ticker: str,
|
||||
start: str,
|
||||
interval: str,
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Constructor
|
||||
|
||||
Parameters
|
||||
@@ -60,15 +80,20 @@ class FinancialModelingPrepController:
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = f"""
|
||||
Financial Modeling Prep:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
Financial Modeling Prep Menu:
|
||||
|
||||
Ticker: {self.ticker}
|
||||
|
||||
@@ -87,31 +112,47 @@ Ticker: {self.ticker}
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Parameters
|
||||
-------
|
||||
an_input : str
|
||||
string with input arguments
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.fmp_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -119,17 +160,53 @@ Ticker: {self.ticker}
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "fmp")
|
||||
self.queue.insert(0, "fa")
|
||||
self.queue.insert(0, "stocks")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "reset", "stocks", "fa", "fmp"]
|
||||
|
||||
@try_except
|
||||
def call_profile(self, other_args: List[str]):
|
||||
@@ -148,9 +225,9 @@ Ticker: {self.ticker}
|
||||
""",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_profile(self.ticker)
|
||||
if ns_parser:
|
||||
fmp_view.display_profile(self.ticker)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_quote(self, other_args: List[str]):
|
||||
@@ -169,9 +246,9 @@ Ticker: {self.ticker}
|
||||
""",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_quote(self.ticker)
|
||||
if ns_parser:
|
||||
fmp_view.display_quote(self.ticker)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_enterprise(self, other_args: List[str]):
|
||||
@@ -207,15 +284,15 @@ Ticker: {self.ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
fmp_view.display_enterprise(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
fmp_view.display_enterprise(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_dcf(self, other_args: List[str]):
|
||||
@@ -250,14 +327,14 @@ Ticker: {self.ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_discounted_cash_flow(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
fmp_view.display_discounted_cash_flow(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_income(self, other_args: List[str]):
|
||||
@@ -298,14 +375,14 @@ Ticker: {self.ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_income_statement(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
fmp_view.display_income_statement(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_balance(self, other_args: List[str]):
|
||||
@@ -351,14 +428,14 @@ Ticker: {self.ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_balance_sheet(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
fmp_view.display_balance_sheet(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_cash(self, other_args: List[str]):
|
||||
@@ -402,14 +479,14 @@ Ticker: {self.ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_cash_flow(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
fmp_view.display_cash_flow(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_metrics(self, other_args: List[str]):
|
||||
@@ -458,14 +535,14 @@ Ticker: {self.ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_key_metrics(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
fmp_view.display_key_metrics(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ratios(self, other_args: List[str]):
|
||||
@@ -515,14 +592,14 @@ Ticker: {self.ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_financial_ratios(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
fmp_view.display_financial_ratios(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_growth(self, other_args: List[str]):
|
||||
@@ -568,17 +645,22 @@ Ticker: {self.ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
fmp_view.display_financial_statement_growth(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
fmp_view.display_financial_statement_growth(
|
||||
ticker=self.ticker,
|
||||
number=ns_parser.n_num,
|
||||
quarterly=ns_parser.b_quarter,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(ticker: str, start: str, interval: str):
|
||||
def menu(
|
||||
ticker: str,
|
||||
start: str,
|
||||
interval: str,
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Financial Modeling Prep menu
|
||||
|
||||
Parameters
|
||||
@@ -591,29 +673,75 @@ def menu(ticker: str, start: str, interval: str):
|
||||
Stock data interval
|
||||
"""
|
||||
|
||||
fmp_controller = FinancialModelingPrepController(ticker, start, interval)
|
||||
fmp_controller.call_help(None)
|
||||
fmp_controller = FinancialModelingPrepController(ticker, start, interval, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in fmp_controller.CHOICES}
|
||||
)
|
||||
# There is a command in the queue
|
||||
if fmp_controller.queue and len(fmp_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if fmp_controller.queue[0] in ("q", "..", "quit"):
|
||||
if len(fmp_controller.queue) > 1:
|
||||
return fmp_controller.queue[1:]
|
||||
return []
|
||||
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (stocks)>(fa)>(fmp)> ",
|
||||
completer=completer,
|
||||
)
|
||||
# Consume 1 element from the queue
|
||||
an_input = fmp_controller.queue[0]
|
||||
fmp_controller.queue = fmp_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in fmp_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /stocks/fa/fmp/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (stocks)>(fa)>(fmp)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
fmp_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and fmp_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /stocks/fa/fmp/ $ ",
|
||||
completer=fmp_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /stocks/fa/fmp/ $ ")
|
||||
|
||||
try:
|
||||
process_input = fmp_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
fmp_controller.queue = fmp_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
fmp_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
else:
|
||||
candidate_input = similar_cmd[0]
|
||||
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
fmp_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
fmp_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
an_input = ""
|
||||
fmp_controller.queue = []
|
||||
|
||||
@@ -101,7 +101,7 @@ def display_sustainability(ticker: str):
|
||||
df_sustainability = yahoo_finance_model.get_sustainability(ticker)
|
||||
|
||||
if df_sustainability.empty:
|
||||
print("No sustainability data found.")
|
||||
print("No sustainability data found.", "\n")
|
||||
return
|
||||
|
||||
if gtff.USE_TABULATE_DF:
|
||||
@@ -143,13 +143,15 @@ def display_calendar_earnings(ticker: str):
|
||||
print("")
|
||||
|
||||
|
||||
def display_dividends(ticker: str, num: int = 12, plot: bool = False, export: str = ""):
|
||||
def display_dividends(
|
||||
ticker: str, limit: int = 12, plot: bool = False, export: str = ""
|
||||
):
|
||||
"""Display historical dividends
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Stock ticker
|
||||
num: int
|
||||
limit: int
|
||||
Number to show
|
||||
plot: bool
|
||||
Plots hitsorical data
|
||||
@@ -193,7 +195,7 @@ def display_dividends(ticker: str, num: int = 12, plot: bool = False, export: st
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
div_history.head(num),
|
||||
div_history.head(limit),
|
||||
tablefmt="fancy_grid",
|
||||
headers=["Amount Paid ($)", "Change"],
|
||||
floatfmt=".2f",
|
||||
|
||||
@@ -1,12 +1,11 @@
|
||||
"""Government Controller Module"""
|
||||
""" Government Controller Module """
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
from datetime import datetime, timedelta
|
||||
from typing import List
|
||||
from typing import List, Union
|
||||
from colorama import Style
|
||||
from matplotlib import pyplot as plt
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import get_flair
|
||||
@@ -29,49 +28,74 @@ from gamestonk_terminal.helper_funcs import (
|
||||
class GovController:
|
||||
"""Gov Controller class"""
|
||||
|
||||
# Command choices
|
||||
CHOICES = ["cls", "?", "help", "q", "quit", "load"]
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
CHOICES_COMMANDS = [
|
||||
"load",
|
||||
"lasttrades",
|
||||
"topbuys",
|
||||
"topsells",
|
||||
"qtrcontracts",
|
||||
"toplobbying",
|
||||
]
|
||||
|
||||
CHOICES_COMMANDS_TICKER = [
|
||||
"gtrades",
|
||||
"lastcontracts",
|
||||
"contracts",
|
||||
"histcont",
|
||||
"lobbying",
|
||||
]
|
||||
CHOICES += CHOICES_COMMANDS + CHOICES_COMMANDS_TICKER
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
gov_type_choices = ["congress", "senate", "house"]
|
||||
analysis_choices = ["total", "upmom", "downmom"]
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ticker: str,
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Constructor"""
|
||||
self.ticker = ticker
|
||||
self.gov_parser = argparse.ArgumentParser(add_help=False, prog="gov")
|
||||
self.gov_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["lasttrades"] = {c: {} for c in self.gov_type_choices}
|
||||
choices["topbuys"] = {c: {} for c in self.gov_type_choices}
|
||||
choices["topsells"] = {c: {} for c in self.gov_type_choices}
|
||||
choices["qtrcontracts"]["-a"] = {c: {} for c in self.analysis_choices}
|
||||
choices["qtrcontracts"]["--analysis"] = {
|
||||
c: {} for c in self.analysis_choices
|
||||
}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
self.ticker = ticker
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
dim_no_ticker = Style.DIM if not self.ticker else ""
|
||||
reset_style = Style.RESET_ALL
|
||||
help_string = f"""
|
||||
Government:
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
load load a ticker
|
||||
|
||||
help_txt = f"""
|
||||
Explore:
|
||||
lasttrades last trades
|
||||
topbuys show most purchased stocks
|
||||
@@ -80,6 +104,8 @@ Explore:
|
||||
qtrcontracts quarterly government contracts analysis
|
||||
toplobbying top corporate lobbying tickers
|
||||
|
||||
load load a specific ticker for analysis
|
||||
|
||||
Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
|
||||
gtrades show government trades for ticker
|
||||
@@ -87,35 +113,47 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
histcont show historical quarterly government contracts for ticker
|
||||
lobbying corporate lobbying details for ticker{reset_style}
|
||||
"""
|
||||
print(help_string)
|
||||
print(help_txt)
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.gov_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -123,18 +161,51 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "gov")
|
||||
self.queue.insert(0, "stocks")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "reset", "stocks", "gov"]
|
||||
|
||||
@try_except
|
||||
def call_load(self, other_args: List[str]):
|
||||
"""Process load command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
@@ -161,22 +232,20 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
dest="start",
|
||||
help="The starting date (format YYYY-MM-DD) of the stock",
|
||||
)
|
||||
|
||||
# For the case where a user uses: 'load BB'
|
||||
if other_args and "-t" not in other_args and "-h" not in other_args:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
df_stock_candidate = stocks_helper.load(
|
||||
ns_parser.ticker,
|
||||
ns_parser.start,
|
||||
)
|
||||
if not df_stock_candidate.empty:
|
||||
self.ticker = ns_parser.ticker.upper()
|
||||
else:
|
||||
print("Ticker selected does not exist!", "\n")
|
||||
|
||||
df_stock_candidate = stocks_helper.load(
|
||||
ns_parser.ticker,
|
||||
ns_parser.start,
|
||||
)
|
||||
|
||||
if not df_stock_candidate.empty:
|
||||
self.ticker = ns_parser.ticker.upper()
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_lasttrades(self, other_args: List[str]):
|
||||
@@ -191,7 +260,7 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
"-g",
|
||||
"--govtype",
|
||||
dest="gov",
|
||||
choices=["congress", "senate", "house"],
|
||||
choices=self.gov_type_choices,
|
||||
type=str,
|
||||
default="congress",
|
||||
)
|
||||
@@ -213,20 +282,20 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
default="",
|
||||
help="Representative",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-g")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
quiverquant_view.display_last_government(
|
||||
gov_type=ns_parser.gov,
|
||||
past_days=ns_parser.past_transactions_days,
|
||||
representative=ns_parser.representative,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
quiverquant_view.display_last_government(
|
||||
gov_type=ns_parser.gov,
|
||||
past_days=ns_parser.past_transactions_days,
|
||||
representative=ns_parser.representative,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_topbuys(self, other_args: List[str]):
|
||||
@@ -241,7 +310,7 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
"-g",
|
||||
"--govtype",
|
||||
dest="gov",
|
||||
choices=["congress", "senate", "house"],
|
||||
choices=self.gov_type_choices,
|
||||
type=str,
|
||||
default="congress",
|
||||
)
|
||||
@@ -255,13 +324,13 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
help="Past transaction months",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
help="Number of top tickers",
|
||||
help="Limit of top tickers to display",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--raw",
|
||||
@@ -270,20 +339,21 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
dest="raw",
|
||||
help="Print raw data.",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-g")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
quiverquant_view.display_government_buys(
|
||||
gov_type=ns_parser.gov,
|
||||
past_transactions_months=ns_parser.past_transactions_months,
|
||||
num=ns_parser.num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
quiverquant_view.display_government_buys(
|
||||
gov_type=ns_parser.gov,
|
||||
past_transactions_months=ns_parser.past_transactions_months,
|
||||
num=ns_parser.limit,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_topsells(self, other_args: List[str]):
|
||||
@@ -298,7 +368,7 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
"-g",
|
||||
"--govtype",
|
||||
dest="gov",
|
||||
choices=["congress", "senate", "house"],
|
||||
choices=self.gov_type_choices,
|
||||
type=str,
|
||||
default="congress",
|
||||
)
|
||||
@@ -312,13 +382,13 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
help="Past transaction months",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
help="Number of top tickers",
|
||||
help="Limit of top tickers to display",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--raw",
|
||||
@@ -327,20 +397,21 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
dest="raw",
|
||||
help="Print raw data.",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-g")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
quiverquant_view.display_government_sells(
|
||||
gov_type=ns_parser.gov,
|
||||
past_transactions_months=ns_parser.past_transactions_months,
|
||||
num=ns_parser.num,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
quiverquant_view.display_government_sells(
|
||||
gov_type=ns_parser.gov,
|
||||
past_transactions_months=ns_parser.past_transactions_months,
|
||||
num=ns_parser.limit,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_lastcontracts(self, other_args: List[str]):
|
||||
@@ -361,13 +432,13 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
help="Past transaction days",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=20,
|
||||
help="Number of contracts to display",
|
||||
help="Limit of contracts to display",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-s",
|
||||
@@ -377,17 +448,20 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
default=False,
|
||||
help="Flag to show total amount of contracts.",
|
||||
)
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
quiverquant_view.display_last_contracts(
|
||||
past_transaction_days=ns_parser.past_transaction_days,
|
||||
num=ns_parser.num,
|
||||
sum_contracts=ns_parser.sum,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
if ns_parser:
|
||||
quiverquant_view.display_last_contracts(
|
||||
past_transaction_days=ns_parser.past_transaction_days,
|
||||
num=ns_parser.limit,
|
||||
sum_contracts=ns_parser.sum,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_qtrcontracts(self, other_args: List[str]):
|
||||
@@ -399,32 +473,46 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
description="Look at government contracts [Source: www.quiverquant.com]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=5,
|
||||
help="Number of tickers to get",
|
||||
help="Limit of tickers to get",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--analysis",
|
||||
action="store",
|
||||
dest="analysis",
|
||||
choices=["total", "upmom", "downmom"],
|
||||
choices=self.analysis_choices,
|
||||
type=str,
|
||||
default="total",
|
||||
help="""Analysis to look at contracts. 'Total' shows summed contracts.
|
||||
'Upmom' shows highest sloped contacts while 'downmom' shows highest decreasing slopes.""",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
quiverquant_view.display_qtr_contracts(
|
||||
analysis=ns_parser.analysis, num=ns_parser.num
|
||||
parser.add_argument(
|
||||
"--raw",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="raw",
|
||||
help="Print raw data.",
|
||||
)
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
quiverquant_view.display_qtr_contracts(
|
||||
analysis=ns_parser.analysis,
|
||||
num=ns_parser.limit,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_toplobbying(self, other_args: List[str]):
|
||||
@@ -436,13 +524,13 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
description="Top lobbying. [Source: www.quiverquant.com]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
help="Number to show",
|
||||
help="Limit of stocks to display",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--raw",
|
||||
@@ -454,19 +542,12 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
quiverquant_view.display_top_lobbying(
|
||||
num=ns_parser.limit, raw=ns_parser.raw, export=ns_parser.export
|
||||
)
|
||||
|
||||
quiverquant_view.display_top_lobbying(
|
||||
num=ns_parser.num, raw=ns_parser.raw, export=ns_parser.export
|
||||
)
|
||||
|
||||
def _check_ticker(self):
|
||||
"""Check if ticker loaded"""
|
||||
if self.ticker:
|
||||
return True
|
||||
print("No ticker loaded. Use `load <ticker>` first.\n")
|
||||
return False
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_gtrades(self, other_args: List[str]):
|
||||
@@ -490,7 +571,7 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
"-g",
|
||||
"--govtype",
|
||||
dest="gov",
|
||||
choices=["congress", "senate", "house"],
|
||||
choices=self.gov_type_choices,
|
||||
type=str,
|
||||
default="congress",
|
||||
)
|
||||
@@ -501,20 +582,24 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
dest="raw",
|
||||
help="Print raw data.",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-g")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
quiverquant_view.display_government_trading(
|
||||
ticker=self.ticker,
|
||||
gov_type=ns_parser.gov,
|
||||
past_transactions_months=ns_parser.past_transactions_months,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded. Use `load <ticker>` first.\n")
|
||||
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if self._check_ticker():
|
||||
quiverquant_view.display_government_trading(
|
||||
ticker=self.ticker,
|
||||
gov_type=ns_parser.gov,
|
||||
past_transactions_months=ns_parser.past_transactions_months,
|
||||
raw=ns_parser.raw,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_contracts(self, other_args: List[str]):
|
||||
@@ -541,16 +626,23 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
dest="raw",
|
||||
help="Print raw data.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-p")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
quiverquant_view.display_contracts(
|
||||
ticker=self.ticker,
|
||||
past_transaction_days=ns_parser.past_transaction_days,
|
||||
raw=ns_parser.raw,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded. Use `load <ticker>` first.\n")
|
||||
|
||||
if self._check_ticker():
|
||||
quiverquant_view.display_contracts(
|
||||
ticker=self.ticker,
|
||||
past_transaction_days=ns_parser.past_transaction_days,
|
||||
raw=ns_parser.raw,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_histcont(self, other_args: List[str]):
|
||||
@@ -561,17 +653,25 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
prog="histcont",
|
||||
description="Quarterly-contracts historical [Source: www.quiverquant.com]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"--raw",
|
||||
action="store_true",
|
||||
default=False,
|
||||
dest="raw",
|
||||
help="Print raw data.",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, export_allowed=EXPORT_BOTH_RAW_DATA_AND_FIGURES
|
||||
)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
quiverquant_view.display_hist_contracts(
|
||||
ticker=self.ticker, raw=ns_parser.raw, export=ns_parser.export
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded. Use `load <ticker>` first.\n")
|
||||
|
||||
if self._check_ticker():
|
||||
|
||||
quiverquant_view.display_hist_contracts(
|
||||
ticker=self.ticker, export=ns_parser.export
|
||||
)
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_lobbying(self, other_args: List[str]):
|
||||
@@ -583,55 +683,99 @@ Ticker: {self.ticker or None}{dim_no_ticker}
|
||||
description="Lobbying details [Source: www.quiverquant.com]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
"-l",
|
||||
"--limit",
|
||||
action="store",
|
||||
dest="num",
|
||||
dest="limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
help="Number of events to show",
|
||||
help="Limit of events to show",
|
||||
)
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-l")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
quiverquant_view.display_lobbying(
|
||||
ticker=self.ticker,
|
||||
num=ns_parser.limit,
|
||||
)
|
||||
else:
|
||||
print("No ticker loaded. Use `load <ticker>` first.\n")
|
||||
|
||||
if self._check_ticker():
|
||||
quiverquant_view.display_lobbying(ticker=self.ticker, num=ns_parser.num)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(ticker: str):
|
||||
def menu(ticker: str, queue: List[str] = None):
|
||||
"""Government Menu"""
|
||||
|
||||
gov_controller = GovController(ticker)
|
||||
gov_controller.call_help(None)
|
||||
gov_controller = GovController(ticker, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if gov_controller.queue and len(gov_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if gov_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(gov_controller.queue) > 1:
|
||||
return gov_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = gov_controller.queue[0]
|
||||
gov_controller.queue = gov_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in gov_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /stocks/gov/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in gov_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (stocks)>(gov)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (stocks)>(gov)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
gov_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and gov_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /stocks/gov/ $ ",
|
||||
completer=gov_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /stocks/gov/ $ ")
|
||||
|
||||
try:
|
||||
plt.close("all")
|
||||
|
||||
process_input = gov_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
gov_controller.queue = gov_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input, gov_controller.CHOICES, n=1, cutoff=0.7
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/gov menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
gov_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
|
||||
if similar_cmd:
|
||||
print(f"Did you mean '{similar_cmd[0]}'?\n")
|
||||
continue
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
gov_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
gov_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -318,7 +318,7 @@ def display_government_sells(
|
||||
fig.tight_layout()
|
||||
plt.show()
|
||||
print("")
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "top_sells", df_gov)
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "topsells", df_gov)
|
||||
|
||||
|
||||
def display_last_contracts(
|
||||
@@ -386,9 +386,7 @@ def display_last_contracts(
|
||||
fig.tight_layout()
|
||||
plt.show()
|
||||
print("")
|
||||
export_data(
|
||||
export, os.path.dirname(os.path.abspath(__file__)), "last_contracts", df
|
||||
)
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "lastcontracts", df)
|
||||
|
||||
|
||||
def plot_government(government: pd.DataFrame, ticker: str, gov_type: str):
|
||||
@@ -431,7 +429,11 @@ def plot_government(government: pd.DataFrame, ticker: str, gov_type: str):
|
||||
|
||||
|
||||
def display_government_trading(
|
||||
ticker: str, gov_type: str, past_transactions_months: int = 6, raw: bool = False
|
||||
ticker: str,
|
||||
gov_type: str,
|
||||
past_transactions_months: int = 6,
|
||||
raw: bool = False,
|
||||
export: str = "",
|
||||
):
|
||||
"""Government trading for specific ticker [Source: quiverquant.com]
|
||||
|
||||
@@ -445,6 +447,8 @@ def display_government_trading(
|
||||
Number of months to get transactions for
|
||||
raw: bool
|
||||
Show raw output of trades
|
||||
export: str
|
||||
Format to export data
|
||||
"""
|
||||
df_gov = quiverquant_model.get_government_trading(gov_type, ticker)
|
||||
|
||||
@@ -500,8 +504,10 @@ def display_government_trading(
|
||||
)
|
||||
else:
|
||||
print(df_gov.to_string())
|
||||
else:
|
||||
plot_government(df_gov, ticker, gov_type)
|
||||
|
||||
plot_government(df_gov, ticker, gov_type)
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "gtrades", df_gov)
|
||||
print("")
|
||||
|
||||
|
||||
@@ -535,17 +541,6 @@ def display_contracts(
|
||||
|
||||
df_contracts.drop_duplicates(inplace=True)
|
||||
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
|
||||
df_contracts.groupby("Date").sum().div(1000).plot(kind="bar", rot=0, ax=ax)
|
||||
ax.set_ylabel("Amount ($1k)")
|
||||
ax.set_title(f"Sum of latest government contracts to {ticker}")
|
||||
fig.tight_layout()
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "contracts")
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
if raw:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
@@ -560,11 +555,25 @@ def display_contracts(
|
||||
else:
|
||||
print(df_contracts.to_string())
|
||||
|
||||
plt.show()
|
||||
else:
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
df_contracts.groupby("Date").sum().div(1000).plot(kind="bar", rot=0, ax=ax)
|
||||
ax.set_ylabel("Amount ($1k)")
|
||||
ax.set_title(f"Sum of latest government contracts to {ticker}")
|
||||
plt.gca().xaxis.set_major_formatter(mdates.DateFormatter("%Y/%m/%d"))
|
||||
plt.gcf().autofmt_xdate()
|
||||
fig.tight_layout()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
|
||||
export_data(
|
||||
export, os.path.dirname(os.path.abspath(__file__)), "contracts", df_contracts
|
||||
)
|
||||
print("")
|
||||
|
||||
|
||||
def display_qtr_contracts(analysis: str, num: int, export: str = ""):
|
||||
def display_qtr_contracts(analysis: str, num: int, raw: bool = False, export: str = ""):
|
||||
"""Quarterly contracts [Source: quiverquant.com]
|
||||
|
||||
Parameters
|
||||
@@ -573,6 +582,8 @@ def display_qtr_contracts(analysis: str, num: int, export: str = ""):
|
||||
Analysis to perform. Either 'total', 'upmom' 'downmom'
|
||||
num: int
|
||||
Number to show
|
||||
raw: bool
|
||||
Flag to display raw data
|
||||
export: str
|
||||
Format to export data
|
||||
"""
|
||||
@@ -583,66 +594,68 @@ def display_qtr_contracts(analysis: str, num: int, export: str = ""):
|
||||
return
|
||||
|
||||
tickers = quiverquant_model.analyze_qtr_contracts(analysis, num)
|
||||
if analysis in {"upmom", "downmom"}:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
pd.DataFrame(tickers.values),
|
||||
headers=["tickers"],
|
||||
tablefmt="fancy_grid",
|
||||
showindex=False,
|
||||
if analysis in ("upmom", "downmom"):
|
||||
if raw:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
pd.DataFrame(tickers.values),
|
||||
headers=["tickers"],
|
||||
tablefmt="fancy_grid",
|
||||
showindex=False,
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(tickers.to_string())
|
||||
else:
|
||||
print(tickers.to_string())
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
max_amount = 0
|
||||
quarter_ticks = []
|
||||
for symbol in tickers:
|
||||
amounts = (
|
||||
df_contracts[df_contracts["Ticker"] == symbol]
|
||||
.sort_values(by=["Year", "Qtr"])["Amount"]
|
||||
.values
|
||||
)
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
max_amount = 0
|
||||
quarter_ticks = []
|
||||
for symbol in tickers:
|
||||
amounts = (
|
||||
df_contracts[df_contracts["Ticker"] == symbol]
|
||||
.sort_values(by=["Year", "Qtr"])["Amount"]
|
||||
.values
|
||||
)
|
||||
|
||||
qtr = (
|
||||
df_contracts[df_contracts["Ticker"] == symbol]
|
||||
.sort_values(by=["Year", "Qtr"])["Qtr"]
|
||||
.values
|
||||
)
|
||||
year = (
|
||||
df_contracts[df_contracts["Ticker"] == symbol]
|
||||
.sort_values(by=["Year", "Qtr"])["Year"]
|
||||
.values
|
||||
)
|
||||
qtr = (
|
||||
df_contracts[df_contracts["Ticker"] == symbol]
|
||||
.sort_values(by=["Year", "Qtr"])["Qtr"]
|
||||
.values
|
||||
)
|
||||
year = (
|
||||
df_contracts[df_contracts["Ticker"] == symbol]
|
||||
.sort_values(by=["Year", "Qtr"])["Year"]
|
||||
.values
|
||||
)
|
||||
|
||||
ax.plot(np.arange(0, len(amounts)), amounts / 1_000_000, "-*", lw=2, ms=15)
|
||||
ax.plot(
|
||||
np.arange(0, len(amounts)), amounts / 1_000_000, "-*", lw=2, ms=15
|
||||
)
|
||||
|
||||
if len(amounts) > max_amount:
|
||||
max_amount = len(amounts)
|
||||
quarter_ticks = [
|
||||
f"{quarter[0]} - Q{quarter[1]} " for quarter in zip(year, qtr)
|
||||
]
|
||||
if len(amounts) > max_amount:
|
||||
max_amount = len(amounts)
|
||||
quarter_ticks = [
|
||||
f"{quarter[0]} - Q{quarter[1]} " for quarter in zip(year, qtr)
|
||||
]
|
||||
|
||||
ax.set_xlim([-0.5, max_amount - 0.5])
|
||||
ax.set_xticks(np.arange(0, max_amount))
|
||||
ax.set_xticklabels(quarter_ticks)
|
||||
ax.grid()
|
||||
ax.legend(tickers)
|
||||
titles = {
|
||||
"upmom": "Highest increasing quarterly Government Contracts",
|
||||
"downmom": "Highest decreasing quarterly Government Contracts",
|
||||
}
|
||||
ax.set_title(titles[analysis])
|
||||
ax.set_xlabel("Date")
|
||||
ax.set_ylabel("Amount ($1M)")
|
||||
fig.tight_layout()
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "qtr_contracts")
|
||||
ax.set_xlim([-0.5, max_amount - 0.5])
|
||||
ax.set_xticks(np.arange(0, max_amount))
|
||||
ax.set_xticklabels(quarter_ticks)
|
||||
ax.grid()
|
||||
ax.legend(tickers)
|
||||
titles = {
|
||||
"upmom": "Highest increasing quarterly Government Contracts",
|
||||
"downmom": "Highest decreasing quarterly Government Contracts",
|
||||
}
|
||||
ax.set_title(titles[analysis])
|
||||
ax.set_xlabel("Date")
|
||||
ax.set_ylabel("Amount ($1M)")
|
||||
fig.tight_layout()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.show()
|
||||
plt.show()
|
||||
|
||||
elif analysis == "total":
|
||||
if gtff.USE_TABULATE_DF:
|
||||
@@ -654,16 +667,21 @@ def display_qtr_contracts(analysis: str, num: int, export: str = ""):
|
||||
else:
|
||||
print(tickers.to_string())
|
||||
|
||||
export_data(
|
||||
export, os.path.dirname(os.path.abspath(__file__)), "qtrcontracts", df_contracts
|
||||
)
|
||||
print("")
|
||||
|
||||
|
||||
def display_hist_contracts(ticker: str, export: str = ""):
|
||||
def display_hist_contracts(ticker: str, raw: bool = False, export: str = ""):
|
||||
"""Show historical quarterly government contracts [Source: quiverquant.com]
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Ticker to get congress trading data from
|
||||
raw: bool
|
||||
Flag to display raw data
|
||||
export: str
|
||||
Format to export data
|
||||
"""
|
||||
@@ -683,23 +701,39 @@ def display_hist_contracts(ticker: str, export: str = ""):
|
||||
quarter_ticks = [
|
||||
f"{quarter[0]}" if quarter[1] == 1 else "" for quarter in zip(year, qtr)
|
||||
]
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
|
||||
ax.plot(np.arange(0, len(amounts)), amounts / 1000, "-*", lw=2, ms=15)
|
||||
if raw:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df_contracts,
|
||||
headers=df_contracts.columns,
|
||||
tablefmt="fancy_grid",
|
||||
showindex=False,
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(df_contracts.to_string())
|
||||
|
||||
ax.set_xlim([-0.5, len(amounts) - 0.5])
|
||||
ax.set_xticks(np.arange(0, len(amounts)))
|
||||
ax.set_xticklabels(quarter_ticks)
|
||||
ax.grid()
|
||||
ax.set_title(f"Historical Quarterly Government Contracts for {ticker.upper()}")
|
||||
ax.set_xlabel("Date")
|
||||
ax.set_ylabel("Amount ($1k)")
|
||||
fig.tight_layout()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
else:
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
|
||||
plt.show()
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "hist_cont")
|
||||
ax.plot(np.arange(0, len(amounts)), amounts / 1000, "-*", lw=2, ms=15)
|
||||
|
||||
ax.set_xlim([-0.5, len(amounts) - 0.5])
|
||||
ax.set_xticks(np.arange(0, len(amounts)))
|
||||
ax.set_xticklabels(quarter_ticks)
|
||||
ax.grid()
|
||||
ax.set_title(f"Historical Quarterly Government Contracts for {ticker.upper()}")
|
||||
ax.set_xlabel("Date")
|
||||
ax.set_ylabel("Amount ($1k)")
|
||||
fig.tight_layout()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.show()
|
||||
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "histcont")
|
||||
print("")
|
||||
|
||||
|
||||
@@ -726,16 +760,6 @@ def display_top_lobbying(num: int, raw: bool = False, export: str = ""):
|
||||
lobbying_by_ticker = pd.DataFrame(
|
||||
df_lobbying.groupby("Ticker")["Amount"].agg("sum")
|
||||
).sort_values(by="Amount", ascending=False)
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
lobbying_by_ticker.head(num).plot(kind="bar", ax=ax)
|
||||
ax.set_xlabel("Ticker")
|
||||
ax.set_ylabel("Total Amount ($100k)")
|
||||
ax.set_title(f"Corporate Lobbying Spent since {df_lobbying['Date'].min()}")
|
||||
fig.tight_layout()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.show()
|
||||
|
||||
if raw:
|
||||
if gtff.USE_TABULATE_DF:
|
||||
@@ -750,9 +774,20 @@ def display_top_lobbying(num: int, raw: bool = False, export: str = ""):
|
||||
)
|
||||
else:
|
||||
print(lobbying_by_ticker.head(num).to_string())
|
||||
else:
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=PLOT_DPI)
|
||||
lobbying_by_ticker.head(num).plot(kind="bar", ax=ax)
|
||||
ax.set_xlabel("Ticker")
|
||||
ax.set_ylabel("Total Amount ($100k)")
|
||||
ax.set_title(f"Corporate Lobbying Spent since {df_lobbying['Date'].min()}")
|
||||
fig.tight_layout()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
plt.show()
|
||||
print("")
|
||||
export_data(
|
||||
export, os.path.dirname(os.path.abspath(__file__)), "top_lobbying", df_lobbying
|
||||
export, os.path.dirname(os.path.abspath(__file__)), "lobbying", df_lobbying
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import pandas as pd
|
||||
from tabulate import tabulate
|
||||
from gamestonk_terminal.stocks.insider import finviz_model
|
||||
from gamestonk_terminal.helper_funcs import export_data
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
|
||||
|
||||
def last_insider_activity(ticker: str, num: int, export: str):
|
||||
@@ -36,15 +37,18 @@ def last_insider_activity(ticker: str, num: int, export: str):
|
||||
]
|
||||
]
|
||||
|
||||
print(
|
||||
tabulate(
|
||||
df.head(num),
|
||||
tablefmt="fancy_grid",
|
||||
floatfmt=".2f",
|
||||
headers=list(df.columns),
|
||||
showindex=True,
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df.head(num),
|
||||
tablefmt="fancy_grid",
|
||||
floatfmt=".2f",
|
||||
headers=list(df.columns),
|
||||
showindex=True,
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(df.to_string())
|
||||
print("")
|
||||
|
||||
export_data(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,5 @@
|
||||
import argparse
|
||||
import os
|
||||
import textwrap
|
||||
from typing import List
|
||||
import itertools
|
||||
from bs4 import BeautifulSoup
|
||||
import requests
|
||||
@@ -9,10 +8,8 @@ import pandas as pd
|
||||
from tabulate import tabulate
|
||||
from colorama import Fore, Style
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
check_positive,
|
||||
parse_known_args_and_warn,
|
||||
patch_pandas_text_adjustment,
|
||||
try_except,
|
||||
export_data,
|
||||
)
|
||||
from gamestonk_terminal.stocks.insider.openinsider_model import (
|
||||
get_open_insider_link,
|
||||
@@ -125,37 +122,18 @@ def green_highlight(values):
|
||||
return [f"{Fore.GREEN}{val}{Style.RESET_ALL}" for val in values]
|
||||
|
||||
|
||||
@try_except
|
||||
def print_insider_data(other_args: List[str], type_insider: str):
|
||||
def print_insider_data(type_insider: str, limit: int = 10, export: str = ""):
|
||||
"""Print insider data
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args : List[str]
|
||||
Command line arguments to be processed with argparse
|
||||
type_insider: str
|
||||
Insider type of data
|
||||
limit: int
|
||||
Limit of data rows to display
|
||||
export: str
|
||||
Export data format
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog=type_insider,
|
||||
description=f"Print {d_open_insider[type_insider].replace('-', ' ')} [Source: OpenInsider]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
action="store",
|
||||
dest="num",
|
||||
type=check_positive,
|
||||
default=20,
|
||||
help="Number of datarows to display",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
response = requests.get(f"http://openinsider.com/{d_open_insider[type_insider]}")
|
||||
soup = BeautifulSoup(response.text, "html.parser")
|
||||
table = soup.find("table", {"class": "tinytable"})
|
||||
@@ -172,7 +150,7 @@ def print_insider_data(other_args: List[str], type_insider: str):
|
||||
row = [tr.text.strip() for tr in td if tr.text.strip()]
|
||||
res.append(row)
|
||||
|
||||
df = pd.DataFrame(res).dropna().head(n=ns_parser.num)
|
||||
df = pd.DataFrame(res).dropna().head(n=limit)
|
||||
|
||||
df.columns = [
|
||||
"X",
|
||||
@@ -208,15 +186,21 @@ def print_insider_data(other_args: List[str], type_insider: str):
|
||||
lambda x: "\n".join(textwrap.wrap(x, width=20)) if isinstance(x, str) else x
|
||||
)
|
||||
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
tablefmt="fancy_grid",
|
||||
stralign="right",
|
||||
showindex=False,
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df,
|
||||
headers=df.columns,
|
||||
tablefmt="fancy_grid",
|
||||
stralign="right",
|
||||
showindex=False,
|
||||
)
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(df.to_string())
|
||||
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), type_insider, df)
|
||||
|
||||
l_chars = [list(chars) for chars in df["X"].values]
|
||||
l_uchars = np.unique(list(itertools.chain(*l_chars)))
|
||||
|
||||
@@ -225,56 +209,30 @@ def print_insider_data(other_args: List[str], type_insider: str):
|
||||
print("")
|
||||
|
||||
|
||||
@try_except
|
||||
def print_insider_filter(other_args: List[str], preset_loaded: str):
|
||||
"""Print insider filter based on loaded preset
|
||||
def print_insider_filter(
|
||||
preset_loaded: str,
|
||||
ticker: str,
|
||||
limit: int = 10,
|
||||
links: bool = False,
|
||||
export: str = "",
|
||||
):
|
||||
"""Print insider filter based on loaded preset. [Source: OpenInsider]
|
||||
|
||||
Parameters
|
||||
----------
|
||||
other_args : List[str]
|
||||
Command line arguments to be processed with argparse
|
||||
preset_loaded: str
|
||||
preset_loaded : str
|
||||
Loaded preset filter
|
||||
ticker : str
|
||||
Stock ticker
|
||||
limit : int
|
||||
Limit of rows of data to display
|
||||
links : bool
|
||||
Flag to show hyperlinks
|
||||
export : str
|
||||
Format to export data
|
||||
"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="filter",
|
||||
description="Print open insider filtered data using loaded preset, or selected ticker. [Source: OpenInsider]",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-n",
|
||||
"--num",
|
||||
action="store",
|
||||
dest="num",
|
||||
type=check_positive,
|
||||
default=20,
|
||||
help="Number of datarows to display",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--ticker",
|
||||
action="store",
|
||||
dest="ticker",
|
||||
type=str,
|
||||
default="",
|
||||
help="Filter latest insiders from this ticker",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--links",
|
||||
action="store_true",
|
||||
default=False,
|
||||
help="Flag to show hyperlinks",
|
||||
dest="links",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if ns_parser.ticker:
|
||||
link = f"http://openinsider.com/screener?s={ns_parser.ticker}"
|
||||
if ticker:
|
||||
link = f"http://openinsider.com/screener?s={ticker}"
|
||||
else:
|
||||
link = get_open_insider_link(preset_loaded)
|
||||
|
||||
@@ -282,25 +240,23 @@ def print_insider_filter(other_args: List[str], preset_loaded: str):
|
||||
print("")
|
||||
return
|
||||
|
||||
df_insider = get_open_insider_data(
|
||||
link, has_company_name=bool(not ns_parser.ticker)
|
||||
)
|
||||
df_insider = get_open_insider_data(link, has_company_name=bool(not ticker))
|
||||
df_insider_orig = df_insider.copy()
|
||||
|
||||
if df_insider.empty:
|
||||
print("")
|
||||
print("No insider data found\n")
|
||||
return
|
||||
|
||||
if ns_parser.links:
|
||||
if links:
|
||||
df_insider = df_insider[["Ticker Link", "Insider Link", "Filing Link"]].head(
|
||||
ns_parser.num
|
||||
limit
|
||||
)
|
||||
else:
|
||||
df_insider = df_insider.drop(
|
||||
columns=["Filing Link", "Ticker Link", "Insider Link"]
|
||||
).head(ns_parser.num)
|
||||
).head(limit)
|
||||
|
||||
if gtff.USE_COLOR and not ns_parser.links:
|
||||
if gtff.USE_COLOR and not links:
|
||||
if not df_insider[df_insider["Trade Type"] == "S - Sale"].empty:
|
||||
df_insider[df_insider["Trade Type"] == "S - Sale"] = df_insider[
|
||||
df_insider["Trade Type"] == "S - Sale"
|
||||
@@ -330,9 +286,26 @@ def print_insider_filter(other_args: List[str], preset_loaded: str):
|
||||
df_insider = df_insider.drop(columns=["Filing Date"])
|
||||
|
||||
print("")
|
||||
print(df_insider.to_string(index=False))
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df_insider,
|
||||
headers=df_insider.columns,
|
||||
tablefmt="fancy_grid",
|
||||
)
|
||||
)
|
||||
else:
|
||||
print(df_insider.to_string(index=False))
|
||||
|
||||
if not ns_parser.links:
|
||||
if export:
|
||||
if preset_loaded:
|
||||
cmd = "filter"
|
||||
if ticker:
|
||||
cmd = "lis"
|
||||
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), cmd, df_insider)
|
||||
|
||||
if not links:
|
||||
l_chars = [list(chars) for chars in df_insider_orig["X"].values]
|
||||
l_uchars = np.unique(list(itertools.chain(*l_chars)))
|
||||
print("")
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,13 +2,13 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List, Dict
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
import difflib
|
||||
from typing import List, Dict, Union
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
|
||||
from colorama import Style
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
check_non_negative,
|
||||
get_flair,
|
||||
parse_known_args_and_warn,
|
||||
try_except,
|
||||
@@ -19,25 +19,42 @@ from gamestonk_terminal.stocks.options.yfinance_model import get_option_chain, g
|
||||
from gamestonk_terminal.stocks.options.yfinance_view import plot_payoff
|
||||
|
||||
|
||||
class PayoffController:
|
||||
"""Payoff Controller class."""
|
||||
# pylint: disable=R0902
|
||||
|
||||
CHOICES = ["cls", "?", "help", "q", "quit"]
|
||||
|
||||
class PayoffController:
|
||||
"""Payoff Controller class"""
|
||||
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
CHOICES_COMMANDS = [
|
||||
"list",
|
||||
"add",
|
||||
"rmv",
|
||||
"pick",
|
||||
"plot",
|
||||
"sop",
|
||||
]
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
# pylint: disable=dangerous-default-value
|
||||
def __init__(self, ticker: str, expiration: str):
|
||||
"""Construct Payoff"""
|
||||
underlying_asset_choices = ["long", "short", "none"]
|
||||
|
||||
self.payoff_parser = argparse.ArgumentParser(add_help=False, prog="po")
|
||||
def __init__(self, ticker: str, expiration: str, queue: List[str] = None):
|
||||
"""Construct"""
|
||||
self.payoff_parser = argparse.ArgumentParser(add_help=False, prog="payoff")
|
||||
self.payoff_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
|
||||
self.chain = get_option_chain(ticker, expiration)
|
||||
self.calls = list(
|
||||
zip(
|
||||
@@ -52,10 +69,28 @@ class PayoffController:
|
||||
)
|
||||
)
|
||||
self.ticker = ticker
|
||||
self.current_price = get_price(ticker)
|
||||
self.expiration = expiration
|
||||
self.options: List[Dict[str, str]] = []
|
||||
self.underlying = 0
|
||||
self.current_price = get_price(ticker)
|
||||
|
||||
self.call_index_choices = range(len(self.calls))
|
||||
self.put_index_choices = range(len(self.puts))
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
|
||||
self.choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.choices["pick"] = {c: {} for c in self.underlying_asset_choices}
|
||||
self.choices["add"] = {
|
||||
str(c): {} for c in list(range(max(len(self.puts), len(self.calls))))
|
||||
}
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
@@ -67,23 +102,19 @@ class PayoffController:
|
||||
text = "Short"
|
||||
|
||||
help_text = f"""
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
Ticker: {self.ticker or None}
|
||||
Expiry: {self.expiration or None}
|
||||
|
||||
Current Ticker: {self.ticker or None}
|
||||
Current Expiry: {self.expiration or None}
|
||||
pick long, short, or none (default) underlying asset
|
||||
|
||||
Underlying Asset: {text}
|
||||
|
||||
list list available strike prices for calls and puts
|
||||
|
||||
pick long, short, or none (default) underlying asset
|
||||
add add option to the list of the options to be plotted
|
||||
rmv remove option from the list of the options to be plotted
|
||||
add add option to the list of the options to be plotted{Style.DIM if not self.options else ""}
|
||||
rmv remove option from the list of the options to be plotted{Style.RESET_ALL}
|
||||
|
||||
sop selected options
|
||||
plot show the option payoff diagram
|
||||
"""
|
||||
print(help_text)
|
||||
@@ -93,28 +124,40 @@ Underlying Asset: {text}
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.payoff_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -122,107 +165,89 @@ Underlying Asset: {text}
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
|
||||
def call_list(self, _):
|
||||
"""Lists available calls and puts"""
|
||||
length = max(len(self.calls), len(self.puts)) - 1
|
||||
print(
|
||||
"Add an option using the index on the left, and not the actual strike price"
|
||||
)
|
||||
print("#\tcall\tput")
|
||||
for i in range(length):
|
||||
call = self.calls[i][0] if i < len(self.calls) else ""
|
||||
put = self.puts[i][0] if i < len(self.puts) else ""
|
||||
print(f"{i}\t{call}\t{put}")
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_add(self, other_args: List[str]):
|
||||
"""Process add command"""
|
||||
self.add_option(other_args)
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "quit"]
|
||||
|
||||
def call_rmv(self, other_args: List[str]):
|
||||
"""Process rmv command"""
|
||||
self.rmv_option(other_args)
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "payoff")
|
||||
if self.expiration:
|
||||
self.queue.insert(0, f"exp {self.expiration}")
|
||||
if self.ticker:
|
||||
self.queue.insert(0, f"load {self.ticker}")
|
||||
self.queue.insert(0, "options")
|
||||
self.queue.insert(0, "stocks")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_pick(self, other_args: List[str]):
|
||||
"""Process pick command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="long",
|
||||
description="This function plots option payoff diagrams",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--type",
|
||||
dest="type",
|
||||
type=str,
|
||||
help="choose what you would like to do with the underlying asset, choose from: long, short, or none",
|
||||
required="-h" not in other_args,
|
||||
)
|
||||
reset_commands = ["quit", "quit", "quit", "reset", "stocks", "options"]
|
||||
if self.ticker:
|
||||
reset_commands.append(f"load {self.ticker}")
|
||||
if self.expiration:
|
||||
reset_commands.append(f"exp -d {self.expiration}")
|
||||
reset_commands.append("payoff")
|
||||
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-t")
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
if ns_parser.type == "long":
|
||||
self.underlying = 1
|
||||
elif ns_parser.type == "none":
|
||||
self.underlying = 0
|
||||
elif ns_parser.type == "short":
|
||||
self.underlying = -1
|
||||
|
||||
self.print_help()
|
||||
return reset_commands
|
||||
|
||||
@try_except
|
||||
def call_plot(self, other_args):
|
||||
"""Process plot command"""
|
||||
def call_list(self, other_args):
|
||||
"""Lists available calls and puts"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="plot",
|
||||
description="This function plots option payoff diagrams",
|
||||
prog="list",
|
||||
description="""Lists available calls and puts.""",
|
||||
)
|
||||
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
|
||||
plot_payoff(
|
||||
self.current_price,
|
||||
self.options,
|
||||
self.underlying,
|
||||
self.ticker,
|
||||
self.expiration,
|
||||
)
|
||||
|
||||
def show_setup(self, nl: bool = False):
|
||||
"""Shows the current assets to display in the diagram"""
|
||||
print("#\tType\tHold\tStrike\tCost")
|
||||
for i, o in enumerate(self.options):
|
||||
sign = "Long" if o["sign"] == 1 else "Short"
|
||||
print(f"{i}\t{o['type']}\t{sign}\t{o['strike']}\t{o['cost']}")
|
||||
if nl:
|
||||
if ns_parser:
|
||||
length = max(len(self.calls), len(self.puts)) - 1
|
||||
print("#\tcall\tput")
|
||||
for i in range(length):
|
||||
call = self.calls[i][0] if i < len(self.calls) else ""
|
||||
put = self.puts[i][0] if i < len(self.puts) else ""
|
||||
print(f"{i}\t{call}\t{put}")
|
||||
print("")
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def add_option(self, other_args: List[str]):
|
||||
"""Add an option to the diagram"""
|
||||
def call_add(self, other_args: List[str]):
|
||||
"""Process add command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="add",
|
||||
@@ -248,70 +273,61 @@ Underlying Asset: {text}
|
||||
"-i",
|
||||
"--index",
|
||||
dest="index",
|
||||
type=int,
|
||||
type=check_non_negative,
|
||||
help="list index of the option",
|
||||
required="-h" not in other_args and "-k" not in other_args,
|
||||
choices=self.put_index_choices
|
||||
if "-p" in other_args
|
||||
else self.call_index_choices,
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-k",
|
||||
"--strike",
|
||||
dest="strike",
|
||||
type=float,
|
||||
help="strike price for the option",
|
||||
default=None,
|
||||
)
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-i")
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-i")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
opt_type = "put" if ns_parser.put else "call"
|
||||
sign = -1 if ns_parser.short else 1
|
||||
options_list = self.puts if ns_parser.put else self.calls
|
||||
|
||||
opt_type = "put" if ns_parser.put else "call"
|
||||
sign = -1 if ns_parser.short else 1
|
||||
options_list = self.puts if ns_parser.put else self.calls
|
||||
if ns_parser.strike is None:
|
||||
index = ns_parser.index
|
||||
elif float(ns_parser.strike) in [float(x[0]) for x in options_list]:
|
||||
index = filter(
|
||||
lambda x: float(x[0]) == float(ns_parser.strike), options_list
|
||||
)
|
||||
index = options_list.index(list(index)[0])
|
||||
print(index)
|
||||
else:
|
||||
print("Invalid strike price, please select a valid price from the list")
|
||||
return
|
||||
try:
|
||||
strike = options_list[index][0]
|
||||
cost = options_list[index][1]
|
||||
except IndexError:
|
||||
print("Please use the index, and not the strike price\n")
|
||||
return
|
||||
if ns_parser.index < len(options_list):
|
||||
strike = options_list[ns_parser.index][0]
|
||||
cost = options_list[ns_parser.index][1]
|
||||
|
||||
option = {"type": opt_type, "sign": sign, "strike": strike, "cost": cost}
|
||||
self.options.append(option)
|
||||
option = {
|
||||
"type": opt_type,
|
||||
"sign": sign,
|
||||
"strike": strike,
|
||||
"cost": cost,
|
||||
}
|
||||
self.options.append(option)
|
||||
|
||||
self.show_setup(True)
|
||||
print("#\tType\tHold\tStrike\tCost")
|
||||
for i, o in enumerate(self.options):
|
||||
asset: str = "Long" if o["sign"] == 1 else "Short"
|
||||
print(f"{i}\t{o['type']}\t{asset}\t{o['strike']}\t{o['cost']}")
|
||||
print("")
|
||||
|
||||
else:
|
||||
print("Please use a valid index\n")
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def rmv_option(self, other_args: List[str]):
|
||||
"""Remove one of the options from the diagram"""
|
||||
def call_rmv(self, other_args: List[str]):
|
||||
"""Process rmv command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="rmv",
|
||||
description="""Remove one of the options to be shown in the payoff.""",
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-k",
|
||||
"--strike",
|
||||
dest="strike",
|
||||
type=int,
|
||||
help="strike price for option",
|
||||
"-i",
|
||||
"--index",
|
||||
dest="index",
|
||||
type=check_non_negative,
|
||||
help="index of the option to remove",
|
||||
required=bool("-h" not in other_args and len(self.options) > 0),
|
||||
choices=range(len(self.options)),
|
||||
)
|
||||
|
||||
parser.add_argument(
|
||||
"-a",
|
||||
"--all",
|
||||
@@ -320,52 +336,179 @@ Underlying Asset: {text}
|
||||
help="remove all of the options",
|
||||
default=False,
|
||||
)
|
||||
if other_args:
|
||||
if "-" not in other_args[0]:
|
||||
other_args.insert(0, "-k")
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-i")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser:
|
||||
if self.options:
|
||||
if ns_parser.all:
|
||||
self.options = []
|
||||
else:
|
||||
if ns_parser.index < len(self.options):
|
||||
del self.options[ns_parser.index]
|
||||
else:
|
||||
print("Please use a valid index.\n")
|
||||
|
||||
if ns_parser.all:
|
||||
self.options = []
|
||||
print("#\tType\tHold\tStrike\tCost")
|
||||
for i, o in enumerate(self.options):
|
||||
sign = "Long" if o["sign"] == 1 else "Short"
|
||||
print(f"{i}\t{o['type']}\t{sign}\t{o['strike']}\t{o['cost']}")
|
||||
print("")
|
||||
else:
|
||||
try:
|
||||
del self.options[ns_parser.strike]
|
||||
except IndexError:
|
||||
print("Please use the index, and not the strike price\n")
|
||||
return
|
||||
print("No options have been selected, removing them is not possible\n")
|
||||
|
||||
self.show_setup(True)
|
||||
return self.queue
|
||||
|
||||
def call_pick(self, other_args: List[str]):
|
||||
"""Process pick command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="long",
|
||||
description="This function plots option payoff diagrams",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-t",
|
||||
"--type",
|
||||
dest="underlyingtype",
|
||||
type=str,
|
||||
help="Choose what you would like to do with the underlying asset",
|
||||
required="-h" not in other_args,
|
||||
choices=self.underlying_asset_choices,
|
||||
)
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-t")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
if ns_parser.underlyingtype == "long":
|
||||
self.underlying = 1
|
||||
elif ns_parser.underlyingtype == "none":
|
||||
self.underlying = 0
|
||||
elif ns_parser.underlyingtype == "short":
|
||||
self.underlying = -1
|
||||
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_sop(self, other_args):
|
||||
"""Process sop command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="sop",
|
||||
description="Displays selected option",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
print("#\tType\tHold\tStrike\tCost")
|
||||
for i, o in enumerate(self.options):
|
||||
sign = "Long" if o["sign"] == 1 else "Short"
|
||||
print(f"{i}\t{o['type']}\t{sign}\t{o['strike']}\t{o['cost']}")
|
||||
print("")
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_plot(self, other_args):
|
||||
"""Process plot command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="plot",
|
||||
description="This function plots option payoff diagrams",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
plot_payoff(
|
||||
self.current_price,
|
||||
self.options,
|
||||
self.underlying,
|
||||
self.ticker,
|
||||
self.expiration,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(ticker: str, expiration: str):
|
||||
"""Options Payoff Menu"""
|
||||
plt.close("all")
|
||||
payoff_controller = PayoffController(ticker, expiration)
|
||||
payoff_controller.call_help(None)
|
||||
def menu(ticker: str, expiration: str, queue: List[str] = None):
|
||||
"""Payoff Menu"""
|
||||
payoff_controller = PayoffController(ticker, expiration, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if payoff_controller.queue and len(payoff_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if payoff_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(payoff_controller.queue) > 1:
|
||||
return payoff_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = payoff_controller.queue[0]
|
||||
payoff_controller.queue = payoff_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if (
|
||||
an_input
|
||||
and an_input.split(" ")[0] in payoff_controller.CHOICES_COMMANDS
|
||||
):
|
||||
print(f"{get_flair()} /stocks/options/payoff/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in payoff_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (stocks)>(options)>(payoff)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (stocks)>(options)>(payoff)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
payoff_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and payoff_controller.choices:
|
||||
if payoff_controller.options:
|
||||
payoff_controller.choices["rmv"] = {
|
||||
str(c): {} for c in range(len(payoff_controller.options))
|
||||
}
|
||||
completer = NestedCompleter.from_nested_dict(payoff_controller.choices)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /stocks/options/payoff/ $ ",
|
||||
completer=completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /stocks/options/payoff/ $ ")
|
||||
|
||||
try:
|
||||
plt.close("all")
|
||||
|
||||
process_input = payoff_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
payoff_controller.queue = payoff_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options/payoff menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
payoff_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
payoff_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
payoff_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
from typing import List
|
||||
import matplotlib.pyplot as plt
|
||||
import difflib
|
||||
from typing import List, Union
|
||||
import pandas as pd
|
||||
from tabulate import tabulate
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
@@ -20,9 +20,21 @@ from gamestonk_terminal.stocks.options import yfinance_view
|
||||
|
||||
|
||||
class PricingController:
|
||||
"""Pricing Controller class."""
|
||||
"""Pricing Controller class"""
|
||||
|
||||
CHOICES = ["cls", "?", "help", "q", "quit"]
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
CHOICES_COMMANDS = [
|
||||
"add",
|
||||
"rmv",
|
||||
@@ -31,13 +43,29 @@ class PricingController:
|
||||
]
|
||||
CHOICES += CHOICES_COMMANDS
|
||||
|
||||
# pylint: disable=dangerous-default-value
|
||||
def __init__(self, ticker: str, selected_date: str, prices: pd.DataFrame):
|
||||
"""Construct Pricing"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
ticker: str,
|
||||
selected_date: str,
|
||||
prices: pd.DataFrame,
|
||||
queue: List[str] = None,
|
||||
):
|
||||
"""Construct"""
|
||||
self.pricing_parser = argparse.ArgumentParser(add_help=False, prog="pricing")
|
||||
self.pricing_parser.add_argument("cmd", choices=self.CHOICES)
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
self.ticker = ticker
|
||||
self.selected_date = selected_date
|
||||
self.prices = prices
|
||||
@@ -45,18 +73,12 @@ class PricingController:
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = f"""
|
||||
What do you want to do?
|
||||
cls clear screen
|
||||
?/help show this menu again
|
||||
q quit this menu, and shows back to main menu
|
||||
quit quit to abandon program
|
||||
Ticker: {self.ticker or None}
|
||||
Expiry: {self.selected_date or None}
|
||||
|
||||
Current Ticker: {self.ticker or None}
|
||||
Current Expiry: {self.selected_date or None}
|
||||
|
||||
Options Pricing:
|
||||
add add an expected price to the list
|
||||
rmv remove an expected price from the list
|
||||
|
||||
show show the listed of expected prices
|
||||
rnval risk neutral valuation for an option
|
||||
"""
|
||||
@@ -67,30 +89,42 @@ Options Pricing:
|
||||
|
||||
Returns
|
||||
-------
|
||||
True, False or None
|
||||
False - quit the menu
|
||||
True - quit the program
|
||||
None - continue in the menu
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return None
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.pricing_parser.parse_known_args(
|
||||
an_input.split()
|
||||
)
|
||||
|
||||
# Help menu again
|
||||
if known_args.cmd == "?":
|
||||
self.print_help()
|
||||
return None
|
||||
|
||||
# Clear screen
|
||||
if known_args.cmd == "cls":
|
||||
system_clear()
|
||||
return None
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
@@ -98,17 +132,65 @@ Options Pricing:
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process Help command"""
|
||||
self.print_help()
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_q(self, _):
|
||||
"""Process Q command - quit the menu"""
|
||||
return False
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process Quit command - quit the program"""
|
||||
return True
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "pricing")
|
||||
if self.selected_date:
|
||||
self.queue.insert(0, f"exp {self.selected_date}")
|
||||
if self.ticker:
|
||||
self.queue.insert(0, f"load {self.ticker}")
|
||||
self.queue.insert(0, "options")
|
||||
self.queue.insert(0, "stocks")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
reset_commands = ["quit", "quit", "quit", "reset", "stocks", "options"]
|
||||
if self.ticker:
|
||||
reset_commands.append(f"load {self.ticker}")
|
||||
if self.selected_date:
|
||||
reset_commands.append(f"exp -d {self.selected_date}")
|
||||
reset_commands.append("pricing")
|
||||
|
||||
return reset_commands
|
||||
|
||||
@try_except
|
||||
def call_add(self, other_args: List[str]):
|
||||
@@ -135,18 +217,21 @@ Options Pricing:
|
||||
dest="chance",
|
||||
help="Chance that the stock is at a given projected price",
|
||||
)
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-p")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser.price in self.prices["Price"].to_list():
|
||||
df = self.prices[(self.prices["Price"] != ns_parser.price)]
|
||||
else:
|
||||
df = self.prices
|
||||
if ns_parser:
|
||||
if ns_parser.price in self.prices["Price"].to_list():
|
||||
df = self.prices[(self.prices["Price"] != ns_parser.price)]
|
||||
else:
|
||||
df = self.prices
|
||||
|
||||
new = {"Price": ns_parser.price, "Chance": ns_parser.chance}
|
||||
df = df.append(new, ignore_index=True)
|
||||
self.prices = df.sort_values("Price")
|
||||
print("")
|
||||
new = {"Price": ns_parser.price, "Chance": ns_parser.chance}
|
||||
df = df.append(new, ignore_index=True)
|
||||
self.prices = df.sort_values("Price")
|
||||
print("")
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_rmv(self, other_args: List[str]):
|
||||
@@ -173,36 +258,45 @@ Options Pricing:
|
||||
dest="all",
|
||||
help="Remove all prices from the list",
|
||||
)
|
||||
if other_args and "-" not in other_args[0]:
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-p")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if ns_parser.all:
|
||||
self.prices = pd.DataFrame(columns=["Price", "Chance"])
|
||||
return
|
||||
if ns_parser:
|
||||
if ns_parser.all:
|
||||
self.prices = pd.DataFrame(columns=["Price", "Chance"])
|
||||
else:
|
||||
self.prices = self.prices[(self.prices["Price"] != ns_parser.price)]
|
||||
print("")
|
||||
|
||||
self.prices = self.prices[(self.prices["Price"] != ns_parser.price)]
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_show(self, _):
|
||||
def call_show(self, other_args):
|
||||
"""Process show command"""
|
||||
print(f"Estimated price(s) of {self.ticker} at {self.selected_date}")
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
self.prices,
|
||||
headers=self.prices.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(self.prices.to_string, "\n")
|
||||
print("")
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="show",
|
||||
description="Display prices",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
print(f"Estimated price(s) of {self.ticker} at {self.selected_date}")
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
self.prices,
|
||||
headers=self.prices.columns,
|
||||
floatfmt=".2f",
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(self.prices.to_string, "\n")
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_rnval(self, other_args: List[str]):
|
||||
@@ -245,53 +339,104 @@ Options Pricing:
|
||||
help="The risk-free rate to use",
|
||||
)
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if not ns_parser:
|
||||
return
|
||||
if not self.ticker and not self.selected_date:
|
||||
print("Ticker and expiration required. \n")
|
||||
return
|
||||
if sum(self.prices["Chance"]) != 1:
|
||||
print("Total chances must equal one\n")
|
||||
return
|
||||
if ns_parser:
|
||||
if self.ticker:
|
||||
if self.selected_date:
|
||||
if sum(self.prices["Chance"]) == 1:
|
||||
yfinance_view.risk_neutral_vals(
|
||||
self.ticker,
|
||||
self.selected_date,
|
||||
ns_parser.put,
|
||||
self.prices,
|
||||
ns_parser.mini,
|
||||
ns_parser.maxi,
|
||||
ns_parser.risk,
|
||||
)
|
||||
else:
|
||||
print("Total chances must equal one\n")
|
||||
else:
|
||||
print("No expiry loaded. First use `exp {expiry date}`\n")
|
||||
else:
|
||||
print("No ticker loaded. First use `load <ticker>`\n")
|
||||
|
||||
yfinance_view.risk_neutral_vals(
|
||||
self.ticker,
|
||||
self.selected_date,
|
||||
ns_parser.put,
|
||||
self.prices,
|
||||
ns_parser.mini,
|
||||
ns_parser.maxi,
|
||||
ns_parser.risk,
|
||||
)
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(ticker: str, selected_date: str, prices: pd.DataFrame):
|
||||
"""Options Pricing Menu"""
|
||||
plt.close("all")
|
||||
pricing_controller = PricingController(ticker, selected_date, prices)
|
||||
pricing_controller.call_help(None)
|
||||
def menu(
|
||||
ticker: str, selected_date: str, prices: pd.DataFrame, queue: List[str] = None
|
||||
):
|
||||
"""Pricing Menu"""
|
||||
pricing_controller = PricingController(ticker, selected_date, prices, queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if pricing_controller.queue and len(pricing_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if pricing_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(pricing_controller.queue) > 1:
|
||||
return pricing_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = pricing_controller.queue[0]
|
||||
pricing_controller.queue = pricing_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if (
|
||||
an_input
|
||||
and an_input.split(" ")[0] in pricing_controller.CHOICES_COMMANDS
|
||||
):
|
||||
print(f"{get_flair()} /stocks/options/pricing/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
completer = NestedCompleter.from_nested_dict(
|
||||
{c: None for c in pricing_controller.CHOICES}
|
||||
)
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} (stocks)>(options)>(pricing)> ",
|
||||
completer=completer,
|
||||
)
|
||||
else:
|
||||
an_input = input(f"{get_flair()} (stocks)>(options)>(pricing)> ")
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
pricing_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and pricing_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /stocks/options/pricing/ $ ",
|
||||
completer=pricing_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /stocks/options/pricing/ $ ")
|
||||
|
||||
try:
|
||||
plt.close("all")
|
||||
|
||||
process_input = pricing_controller.switch(an_input)
|
||||
|
||||
if process_input is not None:
|
||||
return process_input
|
||||
# Process the input command
|
||||
pricing_controller.queue = pricing_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print("The command selected doesn't exist\n")
|
||||
continue
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options/pricing menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
pricing_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
pricing_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
pricing_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
|
||||
393
gamestonk_terminal/stocks/options/screener_controller.py
Normal file
393
gamestonk_terminal/stocks/options/screener_controller.py
Normal file
@@ -0,0 +1,393 @@
|
||||
""" Options Screener Controller Module """
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import argparse
|
||||
import difflib
|
||||
import os
|
||||
from typing import List, Union
|
||||
|
||||
from colorama import Style
|
||||
from prompt_toolkit.completion import NestedCompleter
|
||||
from gamestonk_terminal import feature_flags as gtff
|
||||
from gamestonk_terminal.helper_funcs import (
|
||||
EXPORT_ONLY_RAW_DATA_ALLOWED,
|
||||
check_positive,
|
||||
get_flair,
|
||||
parse_known_args_and_warn,
|
||||
system_clear,
|
||||
try_except,
|
||||
)
|
||||
from gamestonk_terminal.menu import session
|
||||
from gamestonk_terminal.portfolio.portfolio_optimization import po_controller
|
||||
from gamestonk_terminal.stocks.comparison_analysis import ca_controller
|
||||
from gamestonk_terminal.stocks.options import syncretism_view
|
||||
|
||||
# pylint: disable=E1121
|
||||
|
||||
|
||||
class ScreenerController:
|
||||
"""Screener Controller class"""
|
||||
|
||||
# Command choices
|
||||
CHOICES = [
|
||||
"cls",
|
||||
"home",
|
||||
"h",
|
||||
"?",
|
||||
"help",
|
||||
"q",
|
||||
"quit",
|
||||
"..",
|
||||
"exit",
|
||||
"r",
|
||||
"reset",
|
||||
]
|
||||
CHOICES_COMMANDS = ["view", "set", "scr"]
|
||||
CHOICES_MENUS = [
|
||||
"ca",
|
||||
"po",
|
||||
]
|
||||
CHOICES += CHOICES_COMMANDS + CHOICES_MENUS
|
||||
|
||||
presets_path = os.path.join(os.path.abspath(os.path.dirname(__file__)), "presets/")
|
||||
preset_choices = [
|
||||
f.split(".")[0] for f in os.listdir(presets_path) if f.endswith(".ini")
|
||||
]
|
||||
|
||||
def __init__(self, queue: List[str] = None):
|
||||
"""Constructor"""
|
||||
self.scr_parser = argparse.ArgumentParser(add_help=False, prog="scr")
|
||||
self.scr_parser.add_argument(
|
||||
"cmd",
|
||||
choices=self.CHOICES,
|
||||
)
|
||||
|
||||
self.completer: Union[None, NestedCompleter] = None
|
||||
|
||||
if session and gtff.USE_PROMPT_TOOLKIT:
|
||||
choices: dict = {c: {} for c in self.CHOICES}
|
||||
choices["view"] = {c: None for c in self.preset_choices}
|
||||
choices["set"] = {c: None for c in self.preset_choices}
|
||||
self.completer = NestedCompleter.from_nested_dict(choices)
|
||||
|
||||
self.preset = "high_IV"
|
||||
self.screen_tickers: List = list()
|
||||
|
||||
if queue:
|
||||
self.queue = queue
|
||||
else:
|
||||
self.queue = list()
|
||||
|
||||
def print_help(self):
|
||||
"""Print help"""
|
||||
help_text = f"""
|
||||
view view available presets (or one in particular)
|
||||
set set one of the available presets
|
||||
|
||||
PRESET: {self.preset}
|
||||
|
||||
scr screen data from this preset
|
||||
{Style.NORMAL if self.screen_tickers else Style.DIM}
|
||||
Last screened tickers: {', '.join(self.screen_tickers)}
|
||||
> ca take these to comparison analysis menu
|
||||
> po take these to portoflio optimization menu{Style.RESET_ALL}
|
||||
"""
|
||||
print(help_text)
|
||||
|
||||
def switch(self, an_input: str):
|
||||
"""Process and dispatch input
|
||||
|
||||
Returns
|
||||
-------
|
||||
List[str]
|
||||
List of commands in the queue to execute
|
||||
"""
|
||||
# Empty command
|
||||
if not an_input:
|
||||
print("")
|
||||
return self.queue
|
||||
|
||||
# Navigation slash is being used
|
||||
if "/" in an_input:
|
||||
actions = an_input.split("/")
|
||||
|
||||
# Absolute path is specified
|
||||
if not actions[0]:
|
||||
an_input = "home"
|
||||
# Relative path so execute first instruction
|
||||
else:
|
||||
an_input = actions[0]
|
||||
|
||||
# Add all instructions to the queue
|
||||
for cmd in actions[1:][::-1]:
|
||||
if cmd:
|
||||
self.queue.insert(0, cmd)
|
||||
|
||||
(known_args, other_args) = self.scr_parser.parse_known_args(an_input.split())
|
||||
|
||||
# Redirect commands to their correct functions
|
||||
if known_args.cmd:
|
||||
if known_args.cmd in ("..", "q"):
|
||||
known_args.cmd = "quit"
|
||||
elif known_args.cmd in ("?", "h"):
|
||||
known_args.cmd = "help"
|
||||
elif known_args.cmd == "r":
|
||||
known_args.cmd = "reset"
|
||||
|
||||
return getattr(
|
||||
self,
|
||||
"call_" + known_args.cmd,
|
||||
lambda _: "Command not recognized!",
|
||||
)(other_args)
|
||||
|
||||
def call_cls(self, _):
|
||||
"""Process cls command"""
|
||||
system_clear()
|
||||
return self.queue
|
||||
|
||||
def call_home(self, _):
|
||||
"""Process home command"""
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
def call_help(self, _):
|
||||
"""Process help command"""
|
||||
self.print_help()
|
||||
return self.queue
|
||||
|
||||
def call_quit(self, _):
|
||||
"""Process quit menu command"""
|
||||
print("")
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit"]
|
||||
|
||||
def call_exit(self, _):
|
||||
"""Process exit terminal command"""
|
||||
# additional quit for when we come to this menu through a relative path
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
return ["quit", "quit", "quit"]
|
||||
|
||||
def call_reset(self, _):
|
||||
"""Process reset command"""
|
||||
if len(self.queue) > 0:
|
||||
self.queue.insert(0, "scr")
|
||||
self.queue.insert(0, "options")
|
||||
self.queue.insert(0, "stocks")
|
||||
self.queue.insert(0, "reset")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
self.queue.insert(0, "quit")
|
||||
return self.queue
|
||||
|
||||
return ["quit", "quit", "quit", "reset", "stocks", "options", "scr"]
|
||||
|
||||
@try_except
|
||||
def call_view(self, other_args: List[str]):
|
||||
"""Process view command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="view",
|
||||
description="""View available presets under presets folder.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--preset",
|
||||
action="store",
|
||||
dest="preset",
|
||||
type=str,
|
||||
help="View specific custom preset",
|
||||
default="",
|
||||
choices=self.preset_choices,
|
||||
)
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-p")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
if ns_parser.preset:
|
||||
syncretism_view.view_available_presets(
|
||||
preset=ns_parser.preset, presets_path=self.presets_path
|
||||
)
|
||||
|
||||
else:
|
||||
for preset in self.preset_choices:
|
||||
print(preset)
|
||||
print("")
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_set(self, other_args: List[str]):
|
||||
"""Process set command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
prog="set",
|
||||
description="""Set preset from custom and default ones.""",
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--preset",
|
||||
action="store",
|
||||
dest="preset",
|
||||
type=str,
|
||||
default="template",
|
||||
help="Filter presets",
|
||||
choices=self.preset_choices,
|
||||
)
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-p")
|
||||
ns_parser = parse_known_args_and_warn(parser, other_args)
|
||||
if ns_parser:
|
||||
self.preset = ns_parser.preset
|
||||
print("")
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_scr(self, other_args: List[str]):
|
||||
"""Process scr command"""
|
||||
parser = argparse.ArgumentParser(
|
||||
add_help=False,
|
||||
formatter_class=argparse.ArgumentDefaultsHelpFormatter,
|
||||
prog="scr",
|
||||
description="""Screener filter output from https://ops.syncretism.io/index.html.
|
||||
Where: CS: Contract Symbol; S: Symbol, T: Option Type; Str: Strike; Exp v: Expiration;
|
||||
IV: Implied Volatility; LP: Last Price; B: Bid; A: Ask; V: Volume; OI: Open Interest;
|
||||
Y: Yield; MY: Monthly Yield; SMP: Regular Market Price; SMDL: Regular Market Day Low;
|
||||
SMDH: Regular Market Day High; LU: Last Trade Date; LC: Last Crawl; ITM: In The Money;
|
||||
PC: Price Change; PB: Price-to-book. """,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-p",
|
||||
"--preset",
|
||||
action="store",
|
||||
dest="preset",
|
||||
type=str,
|
||||
default="template",
|
||||
help="Filter presets",
|
||||
choices=self.preset_choices,
|
||||
)
|
||||
parser.add_argument(
|
||||
"-l",
|
||||
"--limit",
|
||||
type=check_positive,
|
||||
default=10,
|
||||
help="Limit of random entries to display. Default shows all",
|
||||
dest="limit",
|
||||
)
|
||||
|
||||
if other_args and "-" not in other_args[0][0]:
|
||||
other_args.insert(0, "-p")
|
||||
ns_parser = parse_known_args_and_warn(
|
||||
parser, other_args, EXPORT_ONLY_RAW_DATA_ALLOWED
|
||||
)
|
||||
if ns_parser:
|
||||
self.screen_tickers = syncretism_view.view_screener_output(
|
||||
preset=ns_parser.preset,
|
||||
presets_path=self.presets_path,
|
||||
n_show=ns_parser.limit,
|
||||
export=ns_parser.export,
|
||||
)
|
||||
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_po(self, _):
|
||||
"""Call the portfolio optimization menu with selected tickers"""
|
||||
if self.screen_tickers:
|
||||
return po_controller.menu(self.screen_tickers, from_submenu=True)
|
||||
|
||||
print("Some tickers must be screened first through one of the presets!\n")
|
||||
return self.queue
|
||||
|
||||
@try_except
|
||||
def call_ca(self, _):
|
||||
"""Call the comparison analysis menu with selected tickers"""
|
||||
if self.screen_tickers:
|
||||
return ca_controller.menu(
|
||||
self.screen_tickers, self.queue, from_submenu=True
|
||||
)
|
||||
|
||||
print("Some tickers must be screened first through one of the presets!\n")
|
||||
return self.queue
|
||||
|
||||
|
||||
def menu(queue: List[str] = None):
|
||||
"""Screener Menu"""
|
||||
scr_controller = ScreenerController(queue)
|
||||
an_input = "HELP_ME"
|
||||
|
||||
while True:
|
||||
# There is a command in the queue
|
||||
if scr_controller.queue and len(scr_controller.queue) > 0:
|
||||
# If the command is quitting the menu we want to return in here
|
||||
if scr_controller.queue[0] in ("q", "..", "quit"):
|
||||
print("")
|
||||
if len(scr_controller.queue) > 1:
|
||||
return scr_controller.queue[1:]
|
||||
return []
|
||||
|
||||
# Consume 1 element from the queue
|
||||
an_input = scr_controller.queue[0]
|
||||
scr_controller.queue = scr_controller.queue[1:]
|
||||
|
||||
# Print the current location because this was an instruction and we want user to know what was the action
|
||||
if an_input and an_input.split(" ")[0] in scr_controller.CHOICES_COMMANDS:
|
||||
print(f"{get_flair()} /stocks/options/screen/ $ {an_input}")
|
||||
|
||||
# Get input command from user
|
||||
else:
|
||||
# Display help menu when entering on this menu from a level above
|
||||
if an_input == "HELP_ME":
|
||||
scr_controller.print_help()
|
||||
|
||||
# Get input from user using auto-completion
|
||||
if session and gtff.USE_PROMPT_TOOLKIT and scr_controller.completer:
|
||||
an_input = session.prompt(
|
||||
f"{get_flair()} /stocks/options/screen/ $ ",
|
||||
completer=scr_controller.completer,
|
||||
search_ignore_case=True,
|
||||
)
|
||||
# Get input from user without auto-completion
|
||||
else:
|
||||
an_input = input(f"{get_flair()} /stocks/options/screen/ $ ")
|
||||
|
||||
try:
|
||||
# Process the input command
|
||||
scr_controller.queue = scr_controller.switch(an_input)
|
||||
|
||||
except SystemExit:
|
||||
print(
|
||||
f"\nThe command '{an_input}' doesn't exist on the /stocks/options/screen menu.",
|
||||
end="",
|
||||
)
|
||||
similar_cmd = difflib.get_close_matches(
|
||||
an_input.split(" ")[0] if " " in an_input else an_input,
|
||||
scr_controller.CHOICES,
|
||||
n=1,
|
||||
cutoff=0.7,
|
||||
)
|
||||
if similar_cmd:
|
||||
if " " in an_input:
|
||||
candidate_input = (
|
||||
f"{similar_cmd[0]} {' '.join(an_input.split(' ')[1:])}"
|
||||
)
|
||||
if candidate_input == an_input:
|
||||
an_input = ""
|
||||
scr_controller.queue = []
|
||||
print("\n")
|
||||
continue
|
||||
an_input = candidate_input
|
||||
else:
|
||||
an_input = similar_cmd[0]
|
||||
|
||||
print(f" Replacing by '{an_input}'.")
|
||||
scr_controller.queue.insert(0, an_input)
|
||||
else:
|
||||
print("\n")
|
||||
@@ -1,8 +1,9 @@
|
||||
"""Helper functions for scraping options data"""
|
||||
"""Syncretistm View module"""
|
||||
__docformat__ = "numpy"
|
||||
|
||||
import configparser
|
||||
import os
|
||||
from typing import List
|
||||
|
||||
import matplotlib.pyplot as plt
|
||||
from tabulate import tabulate
|
||||
@@ -35,7 +36,7 @@ def view_available_presets(preset: str, presets_path: str):
|
||||
d_filters = {**preset_filter[filter_header]}
|
||||
d_filters = {k: v for k, v in d_filters.items() if v}
|
||||
if d_filters:
|
||||
max_len = len(max(d_filters, key=len))
|
||||
max_len = len(max(d_filters, key=len)) + 2
|
||||
for key, value in d_filters.items():
|
||||
print(f"{key}{(max_len-len(key))*' '}: {value}")
|
||||
print("")
|
||||
@@ -45,7 +46,9 @@ def view_available_presets(preset: str, presets_path: str):
|
||||
print("")
|
||||
|
||||
|
||||
def view_screener_output(preset: str, presets_path: str, n_show: int, export: str):
|
||||
def view_screener_output(
|
||||
preset: str, presets_path: str, n_show: int, export: str
|
||||
) -> List:
|
||||
"""Print the output of screener
|
||||
|
||||
Parameters
|
||||
@@ -58,11 +61,16 @@ def view_screener_output(preset: str, presets_path: str, n_show: int, export: st
|
||||
Number of randomly sorted rows to display
|
||||
export: str
|
||||
Format for export file
|
||||
|
||||
Returns
|
||||
-------
|
||||
List
|
||||
List of tickers screened
|
||||
"""
|
||||
df_res, error_msg = syncretism_model.get_screener_output(preset, presets_path)
|
||||
if error_msg:
|
||||
print(error_msg, "\n")
|
||||
return
|
||||
return []
|
||||
|
||||
export_data(
|
||||
export,
|
||||
@@ -74,15 +82,20 @@ def view_screener_output(preset: str, presets_path: str, n_show: int, export: st
|
||||
if n_show > 0:
|
||||
df_res = df_res.sample(n_show)
|
||||
|
||||
print(
|
||||
tabulate(
|
||||
df_res,
|
||||
headers=df_res.columns,
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
if gtff.USE_TABULATE_DF:
|
||||
print(
|
||||
tabulate(
|
||||
df_res,
|
||||
headers=df_res.columns,
|
||||
showindex=False,
|
||||
tablefmt="fancy_grid",
|
||||
),
|
||||
"\n",
|
||||
)
|
||||
else:
|
||||
print(df_res.to_string())
|
||||
|
||||
return df_res["S"].values.tolist()
|
||||
|
||||
|
||||
# pylint:disable=too-many-arguments
|
||||
@@ -127,14 +140,6 @@ def view_historical_greeks(
|
||||
if raw:
|
||||
print(tabulate(df.tail(n_show), headers=df.columns, tablefmt="fancy_grid"))
|
||||
|
||||
if export:
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"grhist",
|
||||
df,
|
||||
)
|
||||
|
||||
fig, ax = plt.subplots(figsize=plot_autoscale(), dpi=cfp.PLOT_DPI)
|
||||
im1 = ax.plot(df.index, df[greek], c="firebrick", label=greek)
|
||||
ax.set_ylabel(greek)
|
||||
@@ -156,4 +161,10 @@ def view_historical_greeks(
|
||||
plt.legend(ims, labels, loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
plt.show()
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"grhist",
|
||||
df,
|
||||
)
|
||||
print("")
|
||||
|
||||
@@ -116,14 +116,6 @@ def display_chains(
|
||||
columns = to_display + ["strike", "option_type"]
|
||||
chains_df = chains_df[columns].rename(columns=column_map)
|
||||
|
||||
if export:
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"chains",
|
||||
chains_df,
|
||||
)
|
||||
|
||||
if min_sp == -1:
|
||||
min_strike = np.percentile(chains_df["strike"], 25)
|
||||
else:
|
||||
@@ -197,6 +189,13 @@ def display_chains(
|
||||
"\n",
|
||||
)
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.dirname(os.path.abspath(__file__))),
|
||||
"chains",
|
||||
chains_df,
|
||||
)
|
||||
|
||||
|
||||
def plot_oi(
|
||||
ticker: str,
|
||||
@@ -228,12 +227,6 @@ def plot_oi(
|
||||
"""
|
||||
|
||||
options = tradier_model.get_option_chains(ticker, expiry)
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"oi_tr",
|
||||
options,
|
||||
)
|
||||
current_price = tradier_model.last_price(ticker)
|
||||
|
||||
if min_sp == -1:
|
||||
@@ -301,7 +294,12 @@ def plot_oi(
|
||||
fig.tight_layout(pad=1)
|
||||
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"oi_tr",
|
||||
options,
|
||||
)
|
||||
print("")
|
||||
|
||||
|
||||
@@ -335,12 +333,6 @@ def plot_vol(
|
||||
"""
|
||||
|
||||
options = tradier_model.get_option_chains(ticker, expiry)
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"vol_tr",
|
||||
options,
|
||||
)
|
||||
current_price = tradier_model.last_price(ticker)
|
||||
|
||||
if min_sp == -1:
|
||||
@@ -398,7 +390,12 @@ def plot_vol(
|
||||
fig.tight_layout(pad=1)
|
||||
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"vol_tr",
|
||||
options,
|
||||
)
|
||||
print("")
|
||||
|
||||
|
||||
@@ -429,12 +426,6 @@ def plot_volume_open_interest(
|
||||
"""
|
||||
current_price = tradier_model.last_price(ticker)
|
||||
options = tradier_model.get_option_chains(ticker, expiry)
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"voi_tr",
|
||||
options,
|
||||
)
|
||||
|
||||
calls = options[options.option_type == "call"][
|
||||
["strike", "volume", "open_interest"]
|
||||
@@ -587,7 +578,12 @@ def plot_volume_open_interest(
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"voi_tr",
|
||||
options,
|
||||
)
|
||||
print("")
|
||||
|
||||
|
||||
|
||||
@@ -115,24 +115,21 @@ def plot_oi(
|
||||
ls="-",
|
||||
c="g",
|
||||
)
|
||||
ax.axvline(
|
||||
current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7
|
||||
)
|
||||
ax.axvline(max_pain, lw=3, c="k", label=f"Max Pain: {max_pain}", alpha=0.7)
|
||||
ax.grid("on")
|
||||
ax.set_xlabel("Strike Price")
|
||||
ax.set_ylabel("Open Interest (1k) ")
|
||||
ax.set_xlim(min_strike, max_strike)
|
||||
ax.axvline(current_price, lw=2, c="k", ls="--", label="Current Price", alpha=0.7)
|
||||
ax.axvline(max_pain, lw=3, c="k", label=f"Max Pain: {max_pain}", alpha=0.7)
|
||||
ax.grid("on")
|
||||
ax.set_xlabel("Strike Price")
|
||||
ax.set_ylabel("Open Interest (1k) ")
|
||||
ax.set_xlim(min_strike, max_strike)
|
||||
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
|
||||
ax.set_title(f"Open Interest for {ticker.upper()} expiring {expiry}")
|
||||
plt.legend(loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
ax.set_title(f"Open Interest for {ticker.upper()} expiring {expiry}")
|
||||
plt.legend(loc=0)
|
||||
fig.tight_layout(pad=1)
|
||||
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
print("")
|
||||
|
||||
|
||||
@@ -165,12 +162,6 @@ def plot_vol(
|
||||
Format to export file
|
||||
"""
|
||||
options = yfinance_model.get_option_chain(ticker, expiry)
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"vol_yf",
|
||||
options,
|
||||
)
|
||||
calls = options.calls
|
||||
puts = options.puts
|
||||
current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])
|
||||
@@ -228,7 +219,12 @@ def plot_vol(
|
||||
fig.tight_layout(pad=1)
|
||||
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"vol_yf",
|
||||
options,
|
||||
)
|
||||
print("")
|
||||
|
||||
|
||||
@@ -259,12 +255,6 @@ def plot_volume_open_interest(
|
||||
"""
|
||||
|
||||
options = yfinance_model.get_option_chain(ticker, expiry)
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"voi_yf",
|
||||
options,
|
||||
)
|
||||
calls = options.calls
|
||||
puts = options.puts
|
||||
current_price = float(yf.Ticker(ticker).info["regularMarketPrice"])
|
||||
@@ -415,14 +405,39 @@ def plot_volume_open_interest(
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
plt.style.use("default")
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"voi_yf",
|
||||
options,
|
||||
)
|
||||
print("")
|
||||
|
||||
|
||||
def plot_plot(
|
||||
ticker: str, expiration: str, put: bool, x: str, y: str, custom: str
|
||||
ticker: str, expiration: str, put: bool, x: str, y: str, custom: str, export: str
|
||||
) -> None:
|
||||
"""Generate a graph custom graph based on user input"""
|
||||
"""Generate a graph custom graph based on user input
|
||||
|
||||
Parameters
|
||||
----------
|
||||
ticker: str
|
||||
Stock ticker
|
||||
expiration: str
|
||||
Option expiration
|
||||
min_sp: float
|
||||
Min strike price
|
||||
put: bool
|
||||
put option instead of call
|
||||
x: str
|
||||
variable to display in x axis
|
||||
y: str
|
||||
variable to display in y axis
|
||||
custom: str
|
||||
type of plot
|
||||
export: str
|
||||
type of data to export
|
||||
"""
|
||||
convert = {
|
||||
"ltd": "lastTradeDate",
|
||||
"s": "strike",
|
||||
@@ -465,7 +480,11 @@ def plot_plot(
|
||||
plt.gca().yaxis.set_major_locator(mdates.DayLocator(interval=1))
|
||||
elif varis[y]["format"]:
|
||||
ax.yaxis.set_major_formatter(varis[y]["format"])
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
export_data(export, os.path.dirname(os.path.abspath(__file__)), "plot")
|
||||
print("")
|
||||
|
||||
|
||||
def plot_payoff(
|
||||
@@ -489,12 +508,14 @@ def plot_payoff(
|
||||
ax.xaxis.set_major_formatter("${x:.2f}")
|
||||
ax.yaxis.set_major_formatter("${x:.2f}")
|
||||
plt.legend()
|
||||
if gtff.USE_ION:
|
||||
plt.ion()
|
||||
plt.show()
|
||||
print("")
|
||||
|
||||
|
||||
def show_parity(
|
||||
ticker: str, exp: str, put: bool, ask: bool, mini: float, maxi: float
|
||||
ticker: str, exp: str, put: bool, ask: bool, mini: float, maxi: float, export: str
|
||||
) -> None:
|
||||
"""Prints options and whether they are under or over priced [Source: Yahoo Finance]
|
||||
|
||||
@@ -512,7 +533,8 @@ def show_parity(
|
||||
Minimum strike price to show
|
||||
maxi : float
|
||||
Maximum strike price to show
|
||||
|
||||
export : str
|
||||
Export data
|
||||
"""
|
||||
r_date = datetime.strptime(exp, "%Y-%m-%d").date()
|
||||
delta = (r_date - date.today()).days
|
||||
@@ -593,6 +615,12 @@ def show_parity(
|
||||
else:
|
||||
print(show.to_string(index=False))
|
||||
|
||||
export_data(
|
||||
export,
|
||||
os.path.dirname(os.path.abspath(__file__)),
|
||||
"parity",
|
||||
show,
|
||||
)
|
||||
print("")
|
||||
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user