Integration test coverage (#4969)

* Integration test coverage POC

* Update

* Integration test cleanup

* Fix

* update tests > index 36

* rename stocks tests

* Update Integration Tests

* Update Integration Test Coverage

* Update integration tests

* Linting

* Calculate parameter coverage

* Improvements

* Integration test update

* Improvements

* Improvements

* Improvements

* Improvements

* Add to controller

* Update README

* Add integration test summary

* Update integration tests

* Update integration tests

* Update algorithm

* avoid uncaught exception

* change summary

* change workflow

* another summawry change

* fix small bug when module not found

* summary table

* Refactor

* Workflow

* Breaking get_coverage_all_controllers

* Submenu coverage refactor

* Remove comments

* fix build workflow

* forgot intel

* update integration tests wf

* update windows wd (need to test)

* same

* fix integration test

* Windows patch

* Update integration-test.yml

* fix windows path

* fix windows build wf

* Update windows10_build.yml

* Linting

* fix paths

* Finishing path touches

---------

Co-authored-by: Diogo Sousa <montezdesousa@gmail.com>
Co-authored-by: luqazino@gmail.com <luqazino@gmail.com>
Co-authored-by: montezdesousa <79287829+montezdesousa@users.noreply.github.com>
This commit is contained in:
Igor Radovanovic
2023-05-10 11:44:12 +02:00
committed by GitHub
parent 425b740eda
commit 705a3318e0
83 changed files with 1169 additions and 357 deletions

View File

@@ -26,7 +26,6 @@ jobs:
if: always()
- name: Checkout code
if: github.event_name == 'push'
uses: actions/checkout@v3
- name: Checkout specific ref
@@ -69,7 +68,15 @@ jobs:
run: |
source $VENV
python terminal.py -t | tee result.txt
grep "================================ integration test summary ================================" result.txt -A100 | tail -c 2000 > failed_tests.txt
grep "================================ Integration Test Summary ================================" result.txt -A100 | tail --bytes=2000 > summary.txt
- name: Run Integration Tests Coverage Report
id: integration-tests-coverage
run: |
source $VENV
python terminal.py -t --coverage | tee result.txt
echo >> summary.txt
sed -n '/Integration Coverage Summary/,$p' result.txt >> summary.txt
- name: Upload summary to Slack
uses: adrey/slack-file-upload-action@master
@@ -77,7 +84,7 @@ jobs:
token: ${{ secrets.SLACK_API_TOKEN }}
initial_comment: "Integration test summary"
title: "Integration test summary"
path: failed_tests.txt
path: summary.txt
channel: ${{ secrets.SLACK_CHANNEL_ID }}
- name: Slack Notification - Success/Failure

View File

@@ -70,7 +70,8 @@ jobs:
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
MACOS_KEYCHAIN_PWD: ${{ secrets.MACOS_KEYCHAIN_PWD }}
MACOS_CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
run: | # when pushing to main, make to generate new cert, and utilize secrets to store new password, and identity
run:
| # when pushing to main, make to generate new cert, and utilize secrets to store new password, and identity
echo "Ensuring Keychain with same name does not exist"
rm -rf /Users/openbb/Library/Keychains/build.keychain-db
echo "Decoding certificate"
@@ -141,8 +142,14 @@ jobs:
- name: Run Integration Tests
run: |
extract/OpenBBTerminal.pkg/Payload/Applications/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t -s forecast cryptocurrency reports alternative economy futures econometrics dashboards portfolio stocks/test_stocks_options_screen.openbb stocks/test_stocks_options.openbb forex etf stocks/test_stocks_fa.openbb | tee result.txt
grep "================================ integration test summary ================================" result.txt -A100 | tail --bytes=2000 > failed_tests.txt
extract/OpenBBTerminal.pkg/Payload/Applications/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t -s forecast cryptocurrency reports alternative economy futures econometrics dashboards portfolio stocks/test_stocks_options_screen.openbb stocks/test_stocks_options.openbb forex etf stocks/test_stocks_fa.openbb | tee result.txt
grep "================================ Integration Test Summary ================================" result.txt -A100 | tail --bytes=2000 > summary.txt
- name: Run Integration Tests Coverage Report
run: |
extract/OpenBBTerminal.pkg/Payload/Applications/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t --coverage | tee result.txt
echo >> summary.txt
sed -n '/Integration Coverage Summary/,$p' result.txt >> summary.txt
- name: Upload summary to Slack
uses: adrey/slack-file-upload-action@master
@@ -150,7 +157,7 @@ jobs:
token: ${{ secrets.SLACK_API_TOKEN }}
initial_comment: "MacOs Build Integration test summary"
title: "MacOs Build Integration test summary"
path: failed_tests.txt
path: summary.txt
channel: ${{ secrets.SLACK_CHANNEL_ID }}
- name: Remove OpenBB Folder
@@ -159,7 +166,7 @@ jobs:
rm -rf ~/Desktop/OPENBB-exports
rm -rf extract/
# Job to build the MacOS Intel version of the Terminal===================================
# Job to build the MacOS Intel version of the Terminal===================================
Intel-MacOs-Build:
name: Intel MacOS Build
runs-on: [self-hosted, macos, x64]
@@ -216,7 +223,8 @@ jobs:
MACOS_CERTIFICATE_PWD: ${{ secrets.MACOS_CERTIFICATE_PWD }}
MACOS_KEYCHAIN_PWD: ${{ secrets.MACOS_KEYCHAIN_PWD }}
MACOS_CODESIGN_IDENTITY: ${{ secrets.MACOS_CODESIGN_IDENTITY }}
run: | # when pushing to main, make to generate new cert, and utilize secrets to store new password, and identity
run:
| # when pushing to main, make to generate new cert, and utilize secrets to store new password, and identity
echo "Ensuring Keychain with same name does not exist"
rm -rf /Users/openbb/Library/Keychains/build.keychain-db
echo "Decoding certificate"
@@ -287,8 +295,14 @@ jobs:
- name: Run Integration Tests
run: |
extract/OpenBBTerminal.pkg/Payload/Applications/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t -s forecast cryptocurrency reports alternative economy futures econometrics dashboards portfolio stocks/test_stocks_options_screen.openbb stocks/test_stocks_options.openbb forex etf stocks/test_stocks_fa.openbb | tee result.txt
grep "================================ integration test summary ================================" result.txt -A100 | tail --bytes=2000 > failed_tests.txt
extract/OpenBBTerminal.pkg/Payload/Applications/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t -s forecast cryptocurrency reports alternative economy futures econometrics dashboards portfolio stocks/test_stocks_options_screen.openbb stocks/test_stocks_options.openbb forex etf stocks/test_stocks_fa.openbb | tee result.txt
grep "================================ Integration Test Summary ================================" result.txt -A100 | tail --bytes=2000 > summary.txt
- name: Run Integration Tests Coverage Report
run: |
extract/OpenBBTerminal.pkg/Payload/Applications/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t --coverage | tee result.txt
echo >> summary.txt
sed -n '/Integration Coverage Summary/,$p' result.txt >> summary.txt
- name: Upload summary to Slack
uses: adrey/slack-file-upload-action@master
@@ -296,7 +310,7 @@ jobs:
token: ${{ secrets.SLACK_API_TOKEN }}
initial_comment: "MacOs Build Integration test summary"
title: "Intel MacOs Build Integration test summary"
path: failed_tests.txt
path: summary.txt
channel: ${{ secrets.SLACK_CHANNEL_ID }}
- name: Remove OpenBB Folder

View File

@@ -105,7 +105,13 @@ jobs:
- name: Run Integration Tests
run: |
dist\OpenBBTerminal\OpenBBTerminal.exe -t -s forecast cryptocurrency reports alternative economy futures econometrics dashboards portfolio stocks/test_stocks_options_screen.openbb stocks/test_stocks_options.openbb forex etf stocks/test_stocks_fa.openbb | tee result.txt
Select-String "================================ integration test summary ================================" result.txt -Context 0,100 | Select-Object -Last 2000 | Out-File failed_tests.txt
Select-String "================================ Integration Test Summary ================================" result.txt -Context 0,100 | Select-Object -Last 2000 | Out-File summary.txt
- name: Run Integration Tests Coverage Report
run: |
dist\OpenBBTerminal\OpenBBTerminal.exe -t --coverage | tee result.txt
echo/ >> summary.txt
Select-String "============================== Integration Coverage Summary ==============================" result.txt -Context 0,300 | Select-Object -Last 2000 >> summary.txt
- name: Upload summary to Slack
uses: adrey/slack-file-upload-action@master
@@ -113,7 +119,7 @@ jobs:
token: ${{ secrets.SLACK_API_TOKEN }}
initial_comment: "Windows Build Integration test summary"
title: "Windows Build Integration test summary"
path: failed_tests.txt
path: summary.txt
channel: ${{ secrets.SLACK_CHANNEL_ID }}
# Cleaning ------------------------

View File

@@ -215,6 +215,12 @@ To run the tests on installers you can use the same syntax as above, just substi
/Full/Path/To/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t alternative/test_alt_covid.openbb
```
- Get integration test coverage:
```zsh
/Full/Path/To/OpenBB\ Terminal/.OpenBB/OpenBBTerminal -t --coverage
```
## 4. Test report
The console report has 3 sections: progress, failures and summary.

View File

@@ -0,0 +1,26 @@
{
"openbb_terminal.mutual_funds.mutual_fund_controller": "FundController",
"openbb_terminal.alternative.alt_controller": "AlternativeDataController",
"openbb_terminal.alternative.realestate.realestate_controller": "RealEstateController",
"openbb_terminal.alternative.oss.oss_controller": "OSSController",
"openbb_terminal.forex.technical_analysis.ta_controller": "TechnicalAnalysisController",
"openbb_terminal.stocks.research.res_controller": "ResearchController",
"openbb_terminal.stocks.tradinghours.tradinghours_controller": "TradingHoursController",
"openbb_terminal.stocks.backtesting.bt_controller": "BacktestingController",
"openbb_terminal.stocks.behavioural_analysis.ba_controller": "BehaviouralAnalysisController",
"openbb_terminal.stocks.discovery.disc_controller": "DiscoveryController",
"openbb_terminal.etf.discovery.disc_controller": "DiscoveryController",
"openbb_terminal.stocks.fundamental_analysis.fa_controller": "FundamentalAnalysisController",
"openbb_terminal.stocks.technical_analysis.ta_controller": "TechnicalAnalysisController",
"openbb_terminal.stocks.dark_pool_shorts.dps_controller": "DarkPoolShortsController",
"openbb_terminal.stocks.comparison_analysis.ca_controller": "ComparisonAnalysisController",
"openbb_terminal.fixedincome.fixedincome_controller": "FixedIncomeController",
"openbb_terminal.portfolio.portfolio_optimization.po_controller": "PortfolioOptimizationController",
"openbb_terminal.cryptocurrency.nft.nft_controller": "NFTController",
"openbb_terminal.cryptocurrency.technical_analysis.ta_controller": "TechnicalAnalysisController",
"openbb_terminal.cryptocurrency.due_diligence.dd_controller": "DueDiligenceController",
"openbb_terminal.etf.etf_controller": "ETFController",
"openbb_terminal.etf.technical_analysis.ta_controller": "TechnicalAnalysisController",
"openbb_terminal.reports.reports_controller": "ReportController",
"openbb_terminal.portfolio.portfolio_optimization.parameters.params_controller": "ParametersController"
}

View File

@@ -15,10 +15,19 @@ from pathlib import Path
from traceback import FrameSummary, extract_tb, format_list
from typing import Any, Callable, Dict, List, Optional, Tuple
from matplotlib import pyplot as plt
from openbb_terminal.core.config.paths import (
MISCELLANEOUS_DIRECTORY,
REPOSITORY_DIRECTORY,
)
from openbb_terminal.core.integration_tests.integration_test_coverage import (
get_coverage_all_controllers,
)
from openbb_terminal.core.integration_tests.utils import (
SECTION_LENGTH,
to_section_title,
)
from openbb_terminal.core.models import (
CredentialsModel,
PreferencesModel,
@@ -55,49 +64,9 @@ special_arguments_values = [
"currency_vs",
]
SECTION_LENGTH = 90
STYLES = [
"[bold]",
"[/bold]",
"[red]",
"[/red]",
"[green]",
"[/green]",
"[bold red]",
"[/bold red]",
]
SCRIPTS_DIRECTORY = MISCELLANEOUS_DIRECTORY / "integration_tests_scripts"
def to_section_title(title: str, char: str = "=") -> str:
"""Format title for test mode.
Parameters
----------
title: str
The title to format
Returns
-------
str
The formatted title
"""
title = " " + title + " "
len_styles = 0
for style in STYLES:
if style in title:
len_styles += len(style)
n = int((SECTION_LENGTH - len(title) + len_styles) / 2)
formatted_title = char * n + title + char * n
formatted_title = formatted_title + char * (
SECTION_LENGTH - len(formatted_title) + len_styles
)
return formatted_title
TEST_FILES = sorted(list(SCRIPTS_DIRECTORY.glob("**/*.openbb")))
@@ -537,7 +506,7 @@ def display_summary(
"""
if fails:
console.print("\n" + to_section_title("integration test summary"))
console.print("\n" + to_section_title("Integration Test Summary"))
for file, exception in fails.items():
# Assuming the broken command is the last one called in the traceback
@@ -699,6 +668,14 @@ def parse_args_and_run():
action="store_true",
default=False,
)
parser.add_argument(
"-c",
"--coverage",
help="Display integration test coverage.",
dest="coverage",
action="store_true",
default=False,
)
for arg in special_arguments_values:
parser.add_argument(
f"--{arg}",
@@ -710,6 +687,9 @@ def parse_args_and_run():
ns_parser, unknown_args = parser.parse_known_args()
if ns_parser.coverage:
return get_coverage_all_controllers()
# Allow the tester to send a path without the -p flag
if not ns_parser.path and unknown_args:
ns_parser.path = [u for u in unknown_args if u[0] != "-"]
@@ -729,7 +709,7 @@ def parse_args_and_run():
if ns_parser.list_:
return display_available_scripts(ns_parser.path, ns_parser.skip)
run_test_session(
return run_test_session(
path_list=ns_parser.path,
skip_list=ns_parser.skip,
special_arguments=special_args_dict,
@@ -747,7 +727,7 @@ def main():
if "--test" in sys.argv:
sys.argv.remove("--test")
# user
# User
current_user = get_current_user()
current_user.preferences.ENABLE_EXIT_AUTO_HELP = False
current_user.preferences.USE_PROMPT_TOOLKIT = False
@@ -756,12 +736,15 @@ def main():
current_user.preferences.USE_INTERACTIVE_DF = False
set_current_user(current_user)
# system
# System
set_system_variable("HEADLESS", True)
set_system_variable("DEBUG_MODE", True)
set_system_variable("LOG_COLLECT", False)
# run integration tests
# Portfolio optimization - automatically close matplotlib figures
plt.ion()
# Run integration tests
parse_args_and_run()

View File

@@ -0,0 +1,609 @@
"""Obtain the integration test coverage for the OpenBB Terminal."""
# IMPORT STANDARD
import importlib
import json
import os
import re
from pathlib import Path
# IMPORT THIRD-PARTY
import pandas as pd
from openbb_terminal.core.config.paths import MISCELLANEOUS_DIRECTORY
# IMPORT INTERNAL
from openbb_terminal.core.integration_tests.utils import (
SECTION_LENGTH,
get_submodule_commands,
map_module_to_calls,
to_section_title,
validate_missing_params,
)
from openbb_terminal.helper_funcs import print_rich_table
from openbb_terminal.rich_config import console
INTEGRATION_PATH = os.path.join(MISCELLANEOUS_DIRECTORY, "integration_tests_scripts")
COMMAND_FILTERS = [
"about",
"cls",
"home",
"record",
"resources",
"screenshot",
"stop",
"support",
"whoami",
"wiki",
"h",
"e",
"q",
"r",
"?",
]
def find_controllers() -> list:
"""Find all controllers in the OpenBB Terminal."""
controllers = []
path = os.path.join(".", "openbb_terminal")
for root, _, files in os.walk(path):
for file in files:
if file.endswith("_controller.py") and "sdk" not in root:
controllers.append(os.path.join(root, file))
return controllers
def find_integration_tests() -> list:
"""Find all integration tests in the OpenBB Terminal."""
test = []
for root, _, files in os.walk(INTEGRATION_PATH):
for file in files:
if file.endswith(".openbb"):
test.append(os.path.join(root, file))
test = [
re.sub(r"^\.\\openbb_terminal\\miscellaneous\\integration_tests_scripts", "", i)
for i in test
]
return test
def create_matching_dict() -> dict:
"""Fill the controller and integration test matches by outlier files."""
controller_paths = [
os.path.join(
".", "openbb_terminal", "mutual_funds", "mutual_fund_controller.py"
),
os.path.join(
".", "openbb_terminal", "stocks", "discovery", "disc_controller.py"
),
os.path.join(
".",
"openbb_terminal",
"stocks",
"options",
"screen",
"screener_controller.py",
),
os.path.join(".", "openbb_terminal", "terminal_controller.py"),
]
test_paths = [
os.path.join("mutual_funds", "test_mutual_fund.openbb"),
os.path.join("stocks", "test_disc.openbb"),
os.path.join("stocks", "test_options_screen_ca.openbb"),
os.path.join("terminal", "test_news.openbb"),
]
return dict(zip(controller_paths, test_paths))
def match_controller_with_test(controllers: list, tests: list) -> dict:
"""Match controllers with integration tests."""
matched = create_matching_dict()
for key, value in matched.items():
controllers.remove(key)
tests = [i for i in tests if value not in i]
for controller in controllers:
controller_name = os.path.split(Path(controller))[-1].split("_controller.py")[0]
for test in tests:
test_name = os.path.split(Path(test))[-1][5:-7]
if controller_name == test_name:
matched[controller] = test
tests.remove(test)
if len(tests) > 0:
console.print(f"[red]Unmatched tests: {tests}[/red]")
return matched
def get_module(module_path: str, module_name: str = "") -> object:
"""Obtain a module from a given path."""
module = importlib.import_module(module_path)
if module_name == "":
module_name = re.sub(r".*\.", "", module.__name__)
module_name = "".join([word.capitalize() for word in module_name.split("_")])
try:
module = getattr(module, module_name)
return module
except AttributeError:
raise ModuleNotFoundError # pylint: disable=raise-missing-from
def get_commands_and_params(
module: object, get_commands: bool = True, get_params: bool = True
):
"""Get all commands from a given module."""
params: dict = {}
commands: list = []
try:
module = module() # type: ignore
except TypeError:
commands = get_submodule_commands(module)
params = map_module_to_calls(module)
if get_commands and get_params:
return commands, params
if get_commands and not get_params:
return commands
if not get_commands and get_params:
return params
module_data = getattr(module, "choices_default")
if len(commands) == 0:
commands = list(module_data.keys())
commands = [command for command in commands if command not in COMMAND_FILTERS]
for command in commands:
params[command] = []
try:
params[command] = list(module_data[command].keys())
except AttributeError:
pass
if get_commands and get_params:
return commands, params
if get_commands and not get_params:
return commands
if not get_commands and get_params:
return params
return None
def get_tested_commands(test_file: str) -> list:
"""Obtain all functions that are called in a given test file."""
tested_commands = []
with open(test_file) as f:
for line in f:
try:
function = line.split()[0]
except IndexError:
continue
if function != "#":
tested_commands.append(function)
tested_commands = list(dict.fromkeys(tested_commands))
return list(tested_commands)
def get_tested_command_params(test_file: str) -> list:
"""Obtain all function parameters that are called in a given test file."""
tested_commands = []
with open(test_file) as f:
for line in f:
try:
function = line.split()[0]
except IndexError:
continue
params = re.findall(r"-\w+", line)
tested_commands.append((function, params))
return tested_commands
def get_missing_params(
tested_command_params: list, controller: str, module_dict: dict
) -> dict:
"""Get missing parameters for a given function."""
missing_params = {}
for function, params in tested_command_params:
if function != controller:
try:
all_params = module_dict[function]
all_params = [param.replace("--", "-") for param in all_params]
except KeyError:
# this catches the (sub)menu
continue
if set(params).issubset(set(all_params)):
all_params = [param for param in all_params if param not in params]
for param in params:
if param in all_params:
all_params.remove(param)
else:
unpacked_params = []
used_unabbreviated_params = []
for param in params:
unpacked_params.append(param)
for param in unpacked_params:
for unabbreviated_param in all_params:
if unabbreviated_param.startswith(param):
used_unabbreviated_params.append(unabbreviated_param)
missing_params[function] = [
param for param in all_params if param not in params
]
for param in all_params:
for param2 in used_unabbreviated_params:
if param2.startswith(param):
try:
missing_params[function].remove(param)
missing_params[function].remove(param2)
except ValueError:
pass
missing_params[function] = [
param
for param in missing_params[function]
if param not in used_unabbreviated_params
]
try:
missing_params[function] = list(dict.fromkeys(missing_params[function]))
except KeyError:
# this catches the (sub)menu
continue
for key, value in missing_params.items():
missing_params[key] = list(value)
for param in value:
for param2 in value:
if param != param2 and param2.startswith(param):
try:
missing_params[key].remove(param)
except ValueError:
pass
missing_params = {k: v for k, v in missing_params.items() if v}
return missing_params
def calculate_parameter_coverage(
tested_function: str, module: object
) -> tuple[dict, dict]:
"""Calculate the parameter coverage for controller commands."""
module_dict = get_commands_and_params(module, get_commands=False)
tested_command_params = get_tested_command_params(tested_function)
for key, value in dict(tested_command_params).items():
dict(tested_command_params)[key] = list(set(value))
tested_command_params = list(dict(tested_command_params).items())
try:
controller = str(module).split(".")[3].split("_")[0]
except IndexError:
controller = str(module).split(".")[2].split("_")[0]
missing_params = get_missing_params(tested_command_params, controller, module_dict)
coverage_dict = {}
for key, value in missing_params.items():
try:
coverage_dict[key] = round(
(len(module_dict[key]) - len(value)) / len(module_dict[key]) * 100, 2
)
except ZeroDivisionError:
coverage_dict[key] = 100
for key, value in module_dict.items():
if key not in coverage_dict:
coverage_dict[key] = 100
return missing_params, coverage_dict
def display_command_coverage(
available_commands: list,
tested_commands: list,
untested_commands: list,
coverage: float,
output_table: bool = False,
limit: int = 10,
) -> None:
"""Display coverage."""
if output_table:
df = pd.DataFrame({"function": available_commands})
df["tested"] = df["function"].apply(lambda x: x in tested_commands)
df = df.sort_values(by=["function"])
df = df.astype({"tested": "str"})
df["tested"] = df["tested"].apply(
lambda x: "[red]False[/red]" if x == "False" else "[green]True[/green]"
)
df = df.sort_values(by=["tested"], ascending=False)
print_rich_table(
df,
headers=[x.title() for x in df.columns],
show_index=False,
title=f"* Integration test command coverage: {coverage}%",
automatic_coloring=True,
columns_to_auto_color=["tested"],
limit=limit,
)
else:
console.print(f"* Integration test command coverage: {coverage}%\n")
console.print(f"[red]Untested commands: {untested_commands}[/red]")
console.print(f"[green]Tested commands: {tested_commands}[/green]\n")
def calculate_command_coverage(
tested_commands: list, available_commands: list
) -> tuple[float, list]:
"""Calculate the integration test coverage."""
untested_commands = []
for function in available_commands:
if function not in tested_commands:
untested_commands.append(function)
try:
coverage = round(
(len(available_commands) - len(untested_commands))
/ len(available_commands)
* 100,
2,
)
except ZeroDivisionError:
coverage = 100
return coverage, untested_commands
def get_coverage_all_controllers(output_table: bool = True) -> None:
"""Get integration test coverage for all controllers."""
summary = {}
parent_dir = os.path.join(Path(__file__).parent)
with open(os.path.join(parent_dir, "controllers.json")) as f:
controller_name_mapping = json.load(f)
controllers = find_controllers()
tests = find_integration_tests()
matched = match_controller_with_test(controllers, tests)
for controller, integration_test in matched.items():
if "/" in controller:
path = controller.replace("/", ".")[2:-3]
elif "\\" in controller:
path = controller.replace("\\", ".")[2:-3]
try:
module = get_module(path)
except ModuleNotFoundError:
module_name = controller_name_mapping.get(path, "")
try:
module = get_module(path, module_name=module_name)
except ModuleNotFoundError:
console.print(f"Module {module_name} not found!")
continue
except OSError as e:
console.print(e)
continue
try:
available_commands = get_commands_and_params(module, get_params=False)
except TypeError:
continue
file_path = os.path.join(INTEGRATION_PATH, integration_test)
if not os.path.isfile(file_path):
file_path = os.path.join(INTEGRATION_PATH, integration_test[1:])
tested_commands = get_tested_commands(file_path)
command_coverage, untested_commands = calculate_command_coverage(
tested_commands, available_commands
)
missing_params, coverage_dict = calculate_parameter_coverage(
tested_function=file_path,
module=module,
)
missing_params = validate_missing_params(missing_params, test_file=file_path)
try:
average_parameter_coverage = round(
sum(coverage_dict.values()) / len(coverage_dict), 2
)
except ZeroDivisionError:
average_parameter_coverage = 100
controller_coverage = calculate_controller_coverage(
command_coverage=command_coverage,
average_parameter_coverage=average_parameter_coverage,
)
summary[controller] = {
"Controller coverage": controller_coverage,
"Command coverage": command_coverage,
"Parameter coverage": average_parameter_coverage,
}
console.print(to_section_title(controller), "\n")
console.print(
f"Controller coverage: {controller_coverage}%\n"
+ f"Command coverage: {command_coverage}%\n"
+ f"Parameter coverage: {average_parameter_coverage}%"
)
display_uncovered_commands(
missing_params,
untested_commands,
coverage_dict,
output_table,
)
console.print(
f"* Finished calculating integration test coverage for {controller}\n\n"
)
display_coverage_summary(summary)
def display_coverage_summary(summary: dict) -> None:
"""Display the coverage summary.
Parameters
----------
summary: dict
Summary of the integration test coverage.
"""
console.print(to_section_title("Integration Coverage Summary"))
summary = dict(
sorted(
summary.items(), key=lambda item: item[1]["Controller coverage"], reverse=False # type: ignore
)
)
for controller_name, value in summary.items():
controller_coverage = value["Controller coverage"]
command_coverage = value["Command coverage"]
parameter_coverage = value["Parameter coverage"]
len_res = len(str(controller_coverage)) + len("Controller coverage: %")
spaces = SECTION_LENGTH - len(controller_name) - len_res
console.print(
f"{controller_name}" + spaces * " ",
f"Controller coverage: {controller_coverage}%",
f"\n* Command coverage: {command_coverage}%",
f"\n* Parameter coverage: {parameter_coverage}%",
)
def display_uncovered_commands(
missing_params: dict,
untested_commands: list,
coverage_dict: dict,
output_table: bool,
) -> None:
"""Display the uncovered commands."""
missing_commands = []
for command in missing_params:
if command not in untested_commands:
missing_commands.append(command)
for command in untested_commands:
if command not in missing_params:
missing_commands.append(command)
if output_table:
df = pd.DataFrame()
df["Command"] = missing_commands
df["Missing params"] = [ # pylint: disable=unsupported-assignment-operation
"all params missing"
if command in untested_commands
else missing_params[command]
for command in missing_commands
]
df["Coverage"] = [
0 if command in untested_commands else coverage_dict[command]
for command in missing_commands
]
df = df.sort_values(by=["Coverage"], ascending=True)
df["Coverage"].astype(str)
df["Coverage"] = df[ # pylint: disable=unsupported-assignment-operation
"Coverage"
].apply(lambda x: f"[red]{x}[/red]" if x < 80 else f"[green]{x}[/green]")
df = df[["Command", "Coverage", "Missing params"]]
try:
print_rich_table(df, title="Uncovered commands and parameters")
except IndexError:
console.print("\nNo uncovered commands found!")
def calculate_controller_coverage(
command_coverage: float,
average_parameter_coverage: float,
) -> float:
"""Calculate the global coverage."""
controller_coverage = (command_coverage * average_parameter_coverage) / 100
return round(controller_coverage, 2)
def get_coverage_single_controller(
controller: str,
integration_test: str,
module_name: str = "",
output_table: bool = True,
) -> None:
"""Get single controller integration test coverage.
Parameters
----------
controller (str): Controller to test.
integration_test (str): Integration test to use.
output_table (bool): Output a table with the detailed results.
Example:
--------
test_single_controller(
"openbb_terminal.stocks.stocks_controller",
"/stocks/test_stocks.openbb"
)
"""
module = get_module(controller, module_name=module_name)
available_commands = get_commands_and_params(module, get_params=False)
file_path = os.path.join(INTEGRATION_PATH, integration_test)
if not os.path.isfile(file_path):
file_path = os.path.join(INTEGRATION_PATH, integration_test[1:])
tested_commands = get_tested_commands(file_path)
command_coverage, untested_commands = calculate_command_coverage(
tested_commands, available_commands
)
missing_params, coverage_dict = calculate_parameter_coverage(
tested_function=file_path,
module=module,
)
missing_params = validate_missing_params(missing_params, test_file=file_path)
try:
average_parameter_coverage = round(
sum(coverage_dict.values()) / len(coverage_dict), 2
)
except ZeroDivisionError:
average_parameter_coverage = 100
controller_coverage = calculate_controller_coverage(
command_coverage=command_coverage,
average_parameter_coverage=average_parameter_coverage,
)
console.print(to_section_title(controller), "\n")
console.print(
f"Controller coverage: {controller_coverage}%\n"
+ f"Command coverage: {command_coverage}%\n"
+ f"Parameter coverage: {round(average_parameter_coverage, 2)}%"
)
display_uncovered_commands(
missing_params,
untested_commands,
coverage_dict,
output_table,
)
if __name__ == "__main__":
get_coverage_all_controllers()

View File

@@ -0,0 +1,141 @@
# IMPORT STANDARD
import inspect
import re
# IMPORT THIRD PARTY
# IMPORT INTERNAL
SECTION_LENGTH = 90
STYLES = [
"[bold]",
"[/bold]",
"[red]",
"[/red]",
"[green]",
"[/green]",
"[bold red]",
"[/bold red]",
]
def to_section_title(title: str, char: str = "=") -> str:
"""Format title for test mode.
Parameters
----------
title: str
The title to format
Returns
-------
str
The formatted title
"""
title = " " + title + " "
len_styles = 0
for style in STYLES:
if style in title:
len_styles += len(style)
n = int((SECTION_LENGTH - len(title) + len_styles) / 2)
formatted_title = char * n + title + char * n
formatted_title = formatted_title + char * (
SECTION_LENGTH - len(formatted_title) + len_styles
)
return formatted_title
def get_submodule_commands(module: object) -> list:
"""Obtain all functions from a given module."""
filter_func = [
"about",
"cls",
"home",
"record",
"resources",
"screenshot",
"stop",
"support",
"whoami",
"wiki",
"ad",
]
functions = []
for name in dir(module):
if name.startswith("call_"):
functions.append(name[5:])
functions = [function for function in functions if function not in filter_func]
return functions
def find_all_calls(module) -> list:
"""Find all function calls in a module."""
calls = []
module_dict = module.__dict__
for key, _ in module_dict.items():
if "call_" in key:
calls.append(key)
return calls
def parse_args(module, func) -> list:
"""Parse the arguments of a given module function."""
filters = [
"-ape",
"-yacht",
"-club",
"-h",
"-to",
"-seeking",
"-sloping",
"-neutral",
"-known",
"-yields",
"-level",
"-rating",
"-traded",
"-frens",
"-ad",
"-sheet",
"-flow",
"-weighted",
"-curve",
"-year",
]
source = inspect.getsource(module.__dict__[func])
params = re.findall(r"-\w+", source)
params = [param for param in params if not param[1:].isupper()]
params = [param for param in params if not param[1:].isdigit()]
params = [param for param in params if not param[1].isupper()]
params = [param for param in params if param not in filters]
params = list(dict.fromkeys(params))
return params
def map_module_to_calls(module) -> dict:
"""Map module to its function calls and parameters."""
calls = find_all_calls(module)
calls_dict = {}
for call in calls:
calls_dict[call] = parse_args(module, call)
calls_dict = {k[5:]: v for k, v in calls_dict.items()}
return calls_dict
def validate_missing_params(missing_params: dict, test_file: str) -> dict:
"""Validate missing parameters."""
with open(test_file) as f:
lines = f.readlines()
for key, values in missing_params.items():
for line in lines:
for value in values:
if key in line and value in line:
missing_params[key].remove(value)
missing_params = {k: v for k, v in missing_params.items() if v}
return missing_params

View File

View File

@@ -10,3 +10,6 @@ generate
show
revoke
logout
help
reset
exit

View File

@@ -5,4 +5,6 @@ sh ${repo=openbb-finance/openbbterminal}
tr
rossidx
rossidx --sortby Raised [$M] --reverse
help
reset
exit

View File

@@ -4,3 +4,7 @@ sales -p CV67EB --limit 3
?
regionstats -r york
townsales -t London -s 2022-01-01 -e 2022-01-03
townsales -town London -startdate 2022-01-01 -enddate 2022-01-03 -limit 3
reset
help
exit

View File

@@ -2,7 +2,7 @@ crypto
load ${crypto=eth}
# # Find
# find ${crypto=eth}
find ${crypto=eth}
# find ${crypto=eth} --key symbol
# find ${crypto=eth} --key id
# find ${crypto_full=ethereum} --key name
@@ -20,12 +20,12 @@ load ${crypto=eth}
# price ${crypto=eth}
# # Headlines
# headlines
# headlines ${crypto=ETH}
headlines
headlines ${crypto=ETH}
# # Candle
# candle
# candle --log
candle
candle --log
# PRT
# prt --vs ${crypto_vs=btc}
@@ -34,4 +34,26 @@ load ${crypto=eth}
# Bug where --price doesn't seem to do anything
# prt --vs ${crypto_vs=btc} --price 1000
dd
..
defi
..
disc
..
forecast
..
nft
..
onchain
..
ov
..
qa
..
ta
..
tools
..
help
reset
exit

View File

@@ -1,5 +0,0 @@
crypto
onchain
dt
ds uniswap-v3
tvl -u 0xa5407eae9ba41422680e2e00537571bcc53efbfd -i 12

View File

@@ -49,6 +49,7 @@ tk
fr
rm
events
events --limit 3 --sort name --urls
events --sort date -r
@@ -63,8 +64,18 @@ twitter
twitter --limit 3 --sort retweet_count
twitter --sort date -r
active -s 2021-05-02 -u 2021-10-10
active -s 2021-05-02 -u 2021-10-10 -i 1w
active -s 2021-05-02 -u 2021-10-10 -i 1month
change -s 2021-05-02 -u 2021-10-10
eb -s 2021-05-02 -u 2021-10-10
nonzero -s 2021-05-02 -u 2021-10-10
social
score
dev
gh
help
reset
exit

View File

@@ -20,6 +20,8 @@ tokens --sort txCount --reverse
# stats
stats
lcsc -d 2 -l 10
# pairs
pairs --limit 3 --vol 150 --tx 150 --days 15 --sort volumeUSD
pairs --sort volumeUSD -r
@@ -50,6 +52,7 @@ stvl --limit 3
dtvl --dapps anchor
# sinfo
sinfo -a terra13kc0x8kr3sq8226myf4nmanmn2mrk9s5s9wsnz -l 10
# validators
validators --sort validatorName --reverse

View File

@@ -9,6 +9,9 @@ fp --slug cryptopunks --limit 3 --raw
# collections
collections --limit 3 --fp
collections -fp
collections --limit 3 --sales
help
reset
exit

View File

@@ -16,6 +16,9 @@ whales --address # this is bugged
whales --min 5000000 --limit 3 --sort amount
whales --sort amount_usd -r
baas -c ETH -vs BTC -l 10 -s date -r
lt
lt --kind time --vs ${crypto=ETH} --limit 3 --days 7 --sort tradeAmount
@@ -75,7 +78,7 @@ address 0x9dc7b43ad4288c624fdd236b2ecb9f2b81c93e706b2ffd1d19b112c1df7849e6 -tx
hr -i 1w
# tx
tx
help
reset

View File

@@ -10,6 +10,7 @@ defi
# stables
stables
stables -p
stables --pie
stables --sortbyby Price_[$] --reverse --limit 3
@@ -19,6 +20,7 @@ exchanges --source CoinGecko
exchanges --source CoinPaprika
exchanges --urls
exchanges --vs ${crypto_vs=ETH}
exchanges -vs ${crypto_vs=ETH}
exchanges --source CoinGecko --limit 3 --sortby Trust_Score -r
# exrates
@@ -95,6 +97,7 @@ btcrb
# ch
ch
ch --slug polynetwork-rekt
ch -s wormhole-rekt
ch --sortbyby Amount [$] -r --limit 3
# cr
@@ -106,4 +109,6 @@ cr --cryptocurrrencies ZRX --platforms Compound
fun --metric market_cap
help
reset
exit

View File

@@ -14,6 +14,8 @@ unitroot -r ct -k ct
# line
line
line -l
line -log
line --log
line --ml 2022-01-01 --ms 2022-06-06
@@ -68,4 +70,8 @@ decompose -m
cusum
cusum -t 120 -d 60
pick -t Open
help
reset
exit

View File

@@ -40,6 +40,12 @@ aroon
aroon -l 10 -s 10
###### Volatility
atr
atr -l 10 -o 1 -m sma
atr -m ema
atr -m wma
atr -m hma
atr -m zlma
bbands
bbands -l 10 -s 0.5 -m ema
cones
@@ -59,4 +65,6 @@ obv
fib
fib --period 10 --start 2022-01-01 --end 2022-03-03
help
reset
exit

View File

@@ -7,4 +7,6 @@ aprtoapy --narrative --apr 50 --compounding 6
il
il --narrative --priceChangeA 5 --priceChangeB 3 --proportion 2 --value 10
help
reset
exit

View File

@@ -10,14 +10,14 @@ futures -c metals --source Finviz
futures -c meats --source Finviz
futures -c grains --source Finviz
futures -c softs --source Finviz
futures -c energy -s ticker --reverse --source Finviz
futures -c energy -s ticker -r --source Finviz --sortby ticker
map
map -p 1w -t world
bigmac
bigmac -c USA,ESP,LVA,DEU,FRA,FIN
events
events --country united_states -s 2022-07-07 -e 2022-07-08
events -c spain,canada
events --countries united_states -s 2022-07-07 -e 2022-07-08
events -c united_states,united_kingdom
events -n
edebt --limit 10
rtps
@@ -25,6 +25,7 @@ valuation
valuation --group sector --sortby MarketCap --reverse
performance
performance -g basic_materials -s Month -a
performance -group energy --sortby YTD --reverse
gdp
rgdp
fgdp
@@ -54,10 +55,12 @@ treasury
treasury --maturity 20y --type nominal
treasury --raw
treasury --type inflation --start 2020-01-01 --limit 10
treasury -m 10y --show --freq monthly
fred -p T10Y2Y,DFII10 -s 2019-01-01 -e 2022-01-01
fred -q treasury
fred -p T10Y2Y,FII10 -s 2005-01-01
fred -p T5YIEM,T10YIEM,T20YIEM,T30YIEM
fred -h
index -i nasdaq,dow_djus,sp500 -s 2005-01-01
macro -p URATE -c Netherlands,Germany -s 2005-01-01
index -i sp500 -s 2005-01-01
@@ -90,4 +93,6 @@ kurtosis --window 9
raw --limit 2 --reverse
cusum --threshold 10 --drift 10
decompose
help
reset
exit

View File

@@ -0,0 +1,11 @@
etf
disc
gainers
decliners
active
<<<<<<< Updated upstream:openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_disc.openbb
=======
help
reset
>>>>>>> Stashed changes:openbb_terminal/miscellaneous/integration_tests_scripts/etf/test_etf_disc.openbb
exit

View File

@@ -7,7 +7,9 @@ weights --raw
candle
candle --raw
candle --sort adjclose --reverse --raw
candle -ma
candle -t --ma 4
candle -t -ma
news
compare spy,ivv
disc

View File

@@ -1,6 +0,0 @@
etf
disc
gainers
decliners
active
exit

View File

@@ -49,4 +49,6 @@ adosc --open --fast 2 --slow 1
obv
fib
fib --period 30 --start 2021-01-01 --end 2023-01-01
exit
help
reset
exit

View File

@@ -1,6 +1,11 @@
fixedincome
estr -s 2022-01-01 -e 2023-01-01
sofr -s 2022-01-01 -e 2023-01-01
sofr -s 2022-01-01 -e 2023-01-01 -p overnight
sofr -s 2022-01-01 -e 2023-01-01 -p 30_day_average
sofr -s 2022-01-01 -e 2023-01-01 -p 90_day_average
sofr -s 2022-01-01 -e 2023-01-01 -p 180_day_average
sofr -s 2022-01-01 -e 2023-01-01 -p index
sonia -s 2022-01-01 -e 2023-01-01
ameribor -s 2022-01-01 -e 2023-01-01
fed -s 2022-01-01 -e 2023-01-01
@@ -8,16 +13,23 @@ iorb -s 2022-01-01 -e 2023-01-01
projection
dwpcr -s 2022-01-01 -e 2023-01-01
ecb -s 2022-01-01 -e 2023-01-01
treasury --short australia --long united_kingdom,united_states -s 2022-01-01 -e 2023-01-01
treasury --short australia --long united_kingdom,united_states -s 2022-01-01 -e 2023-01-01 --forecast
usrates -s 2022-01-01 -e 2023-01-01
ycrv -d 2023-02-17
ycrv -d 2023-02-17 --inflation-adjusted
ecbycrv -d 2023-02-17
ecbycrv -d 2023-02-17 --any-rating --raw
icebofa -s 2022-01-01 -e 2023-01-01
moody -s 2022-01-01 -e 2023-01-01
moody -s 2022-01-01 -e 2023-01-01 --spread treasury --raw
cp -s 2022-01-01 -e 2023-01-01
cp -s 2022-01-01 -e 2023-01-01 -c asset_backend
cp -s 2022-01-01 -e 2023-01-01 -c non_financial
cp -s 2022-01-01 -e 2023-01-01 -c spread -o -d
spot -s 2022-01-01 -e 2023-01-01
hqm -d 2023-02-17
hqm -d 2023-02-17 -p --raw
icespread -s 2022-01-01 -e 2023-01-01
tmc -s 2022-01-01 -e 2023-01-01
ffrmc -s 2022-01-01 -e 2023-01-01
tbffr -s 2022-01-01 -e 2023-01-01
help
reset
exit

View File

@@ -27,3 +27,21 @@ nbeats GME
tcn GME
trans GME
tft GME
..
# Test Forecast Advanced
stocks
load GME
forecast
q
load TSLA
forecast
show TSLA
q
load AMC
forecast
show AMC
combine GME -c TSLA.close,AMC.close
brnn GME --past-covariates=volume,TSLA_close,AMC_close
help
reset
exit

View File

@@ -1,13 +0,0 @@
stocks
load GME
forecast
q
load TSLA
forecast
show TSLA
q
load AMC
forecast
show AMC
combine GME -c TSLA.close,AMC.close
brnn GME --past-covariates=volume,TSLA_close,AMC_close

View File

@@ -1,4 +1,7 @@
forex
load EURUSD --source YahooFinance
load EURUSD --source Polygon
load EURUSD --source AlphaVantage
load ${currency=EUR}${currency_vs=USD}
load -t ${currency=EUR}${currency_vs=USD} -r d -i 1day -s 2000-01-01
load -t ${currency=EUR}${currency_vs=USD} -r i -i 30min
@@ -8,6 +11,7 @@ quote --source YahooFinance
quote --source AlphaVantage
candle --sort open -r --raw --limit 10
candle -t
candle --ma 14 --log
help
oanda
qa

View File

@@ -1,5 +0,0 @@
forex
load EURUSD --source YahooFinance
load EURUSD --source Polygon
load EURUSD --source AlphaVantage
exit

View File

@@ -1,14 +0,0 @@
forex
oanda
h
help
?
q
oanda
..
..
forex
oanda
quit
oanda
exit

View File

@@ -58,4 +58,15 @@ to ${currency_vs=jpy}
calendar
to gbp
calendar
h
help
?
q
oanda
..
..
forex
oanda
quit
oanda
exit

View File

@@ -1,5 +1,45 @@
portfolio
po
load --example
file
maxsharpe -s 2022-10-20
minrisk -r 0.05
maxutil -a 0.1 -ra 2 -tk 0.4 -tr 8
maxret -e 2022-11-20
maxdiv -mn 0.1
maxdecorr -lr
ef -t -se 69 -n 10
equal -th 0.1
riskparity -p 1y
relriskparity -de 0.9
hrp -cd spearman
herc -at 0.1
nco -rm MAD
show RP_1
plot -pf MAXSHARPE_0 -pi
plot -pf MAXSHARPE_0 -hi
plot -pf MAXSHARPE_0 -dd
plot -pf MAXSHARPE_0 -rc
plot -pf MAXSHARPE_0 -he
rpf NAME_6
..
load --example
po
equal
maxsharpe -s 1995-05-07
minrisk -r 0.08
maxutil -a 0.1 -ra 2 -tk 0.4 -tr 8
maxret -e 2022-11-20
maxdiv -mn 0.1
maxdecorr -lr -p max
ef -t -se 420 -n 0
equal -th 0.1
riskparity -p 1y
relriskparity -de 0.9
hrp -cd pearson
herc -at 0.1
..
po
show
rpf
show -pf a,b
@@ -52,4 +92,8 @@ plot -pf name_0 -ct sector -he
maxsharpe
rpf -pf a,b,c
rpf -pf name_0,maxsharpe_1
params
..
help
reset
exit

View File

@@ -117,4 +117,10 @@ es -p 50
om
om --start 0.05 --end 0.2
q
bro
..
po
..
help
reset
exit

View File

@@ -1,41 +0,0 @@
portfolio
po
load --example
file
maxsharpe -s 2022-10-20
minrisk -r 0.05
maxutil -a 0.1 -ra 2 -tk 0.4 -tr 8
maxret -e 2022-11-20
maxdiv -mn 0.1
maxdecorr -lr
ef -t -se 69 -n 10
equal -th 0.1
riskparity -p 1y
relriskparity -de 0.9
hrp -cd spearman
herc -at 0.1
nco -rm MAD
show RP_1
plot -pf MAXSHARPE_0 -pi
plot -pf MAXSHARPE_0 -hi
plot -pf MAXSHARPE_0 -dd
plot -pf MAXSHARPE_0 -rc
plot -pf MAXSHARPE_0 -he
rpf NAME_6
..
load --example
po
equal
maxsharpe -s 1995-05-07
minrisk -r 0.08
maxutil -a 0.1 -ra 2 -tk 0.4 -tr 8
maxret -e 2022-11-20
maxdiv -mn 0.1
maxdecorr -lr -p max
ef -t -se 420 -n 0
equal -th 0.1
riskparity -p 1y
relriskparity -de 0.9
hrp -cd pearson
herc -at 0.1
exit

View File

@@ -0,0 +1,43 @@
reports
h
help
?
q
reports
..
reports
crypto
crypto --symbol ${crypto=BTC}
economy
equity
equity --symbol ${ticker=AAPL}
etf
etf --symbol VEA
etf --symbol VWCE
etf --symbol ABCEF
forecast
forecast --symbol AAPL
forecast --symbol NXPI
forecast --symbol PETR3.SA
forex
forex --symbol USDEUR
forex --symbol USDVND
forex --symbol USDABCXYZ
portfolio --transactions holdings_example.xlsx
portfolio --transactions holdings_test.xlsx
run
run --file dummyfile.csv
run --file dummyfile.ipynb
help
reset
exit

View File

@@ -1,13 +0,0 @@
reports
h
help
?
q
reports
..
reports
..
reports
home
reports
exit

View File

@@ -1,8 +0,0 @@
reports
## defaults
crypto
crypto --symbol ${crypto=BTC}
exit

View File

@@ -1,9 +0,0 @@
reports
# economy
# Test with random tickers, that should be ignored by the report.
# economy ${ticker=AAPL}
# economy --symbol ${ticker=MSFT}
exit

View File

@@ -1,9 +0,0 @@
reports
## defaults
equity
## simple ticker
equity --symbol ${ticker=AAPL}
exit

View File

@@ -1,13 +0,0 @@
reports
## defaults
etf
etf --symbol VEA
etf --symbol VWCE
# test with random pairs
etf --symbol ABCEF
exit

View File

@@ -1,15 +0,0 @@
reports
## defaults
forecast
## simple ticker
forecast --symbol AAPL
## Non-US ticker
forecast --symbol NXPI
## Non-US ticker with suffix
forecast --symbol PETR3.SA
exit

View File

@@ -1,14 +0,0 @@
reports
## defaults
forex
forex --symbol USDEUR
# Test with emerging pairs
forex --symbol USDVND
# Test with invalid pairs
forex --symbol USDABCXYZ
exit

View File

@@ -1,8 +0,0 @@
reports
portfolio --transactions holdings_example.xlsx
# Test with invalid path
portfolio --transactions holdings_test.xlsx
exit

View File

@@ -1,10 +0,0 @@
reports
## defaults
run
## Invalid inputs
run --file dummyfile.csv
run --file dummyfile.ipynb
exit

View File

@@ -100,4 +100,6 @@ rsi
rsi -l 20 -u 80 -p 12
rsi --spy
rsi --no_bench --no_short
help
reset
exit

View File

@@ -29,4 +29,5 @@ ticker -t ${ticker=tsla}
get --source Polygon
get --source Finnhub
get
add -s 2
exit

View File

@@ -28,6 +28,7 @@ lowfloat
hotpenny
rtat
divcal -s Dividend -a
divcal -d 2023-01-01 -s record_date -r
heatmap
reset
r

View File

@@ -3,6 +3,7 @@ dps
shorted
ctb
ctb -source stocksera
ctb --raw
hsi
pos
sidtc
@@ -13,7 +14,7 @@ ftd
load ${ticker=aapl} -i 5
ftd -n 5 --raw
dpotc
# prom -t T1
prom -t T1
help
reset
exit

View File

@@ -73,4 +73,7 @@ supplier
customer
load ${ticker=tsla}
arktrades
dcf -t aapl -g -a --no-filter
help
reset
exit

View File

@@ -0,0 +1,3 @@
stocks
forecast
exit

View File

@@ -0,0 +1,16 @@
stocks
options
load AAPL
exp --index 0
hedge
pick --pick 50_long_call
list
add -i 0 --short
add -i 1 --short --put
rmv Option A
sop
plot
help
reset
exit

View File

@@ -28,7 +28,16 @@ vsurf
load NDX --source Nasdaq
exp 0
chains --source Nasdaq
chains -M -1 -m -1 -e 2025-12-19
eodchain -d 2025-12-19
oi
vol
voi
hedge
..
load --help
help
reset
exit

View File

@@ -10,6 +10,9 @@ financial -l 3
ownership
performance
technical
set --preset cheap_dividend
ca
exit
help
reset
exit

View File

@@ -1,5 +1,6 @@
stocks
search -c United States -s Consumer Cyclical -i Specialty Retail -e USA -l 5
search -c united_states -m united_states
load ${ticker=aapl} -i 5
load ${ticker=aapl} -s 2022-10-01
quote
@@ -12,6 +13,7 @@ candle --raw
candle --sort AdjClose -r --raw
codes
news -l 3
news -h
th
..
options
@@ -42,6 +44,11 @@ forecast
..
res
..
# below two submenus are deprecated
dd
sia
help
reset
exit

View File

@@ -1,7 +0,0 @@
stocks
load ${ticker=aapl}
fa
h
help
?
exit

View File

@@ -1,5 +0,0 @@
stocks
load aapl
fa
epsfc
exit

View File

@@ -1,5 +0,0 @@
stocks
load aapl
fa
revfc
exit

View File

@@ -1,13 +0,0 @@
stocks
options
load aapl
exp 0
payoff
list
pick long
add 20
add 15
add 10 -p
sop
plot
exit

View File

@@ -1,13 +0,0 @@
stocks
options
load aapl
exp 0
pricing
add 150 -c 3
rmv 150
add 150 -c 0.3
add 170 -c 0.5
add 180 -c 0.2
show
rnval
exit

View File

@@ -1,42 +0,0 @@
stocks
sia
q
sia
..
sia
quit
sia
help
h
?
clear
..
load ${ticker=tsla}
sia
clear industry
clear sector
clear sector
clear country
clear mktcap
exchange
exchange
mktcap Large
country United States
industry Financial Data & Stock Exchanges
satma
satma is
satma bs
satma cf
vis se
vis ninc
vis re
cps
cpic
cpis
cpcs
cpci
sama
metric roa
reset
r
exit

View File

@@ -1,13 +0,0 @@
jupyter
h
help
?
q
jupter
..
jupyer
..
jupyter
home
jupyter
exit