From ae22b5fa3f731f3ca812967b3e45d8c0ef819eed Mon Sep 17 00:00:00 2001 From: Kwan Yuet Stephen Ho Date: Thu, 1 Oct 2026 10:28:39 -0400 Subject: [PATCH 1/7] test for Python 3.14 --- .circleci/config.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.circleci/config.yml b/.circleci/config.yml index f7c4bf5..146e427 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -48,6 +48,11 @@ jobs: docker: - image: cimg/python:3.13 + py314: + <<: *shared + docker: + - image: cimg/python:3.14 + workflows: version: 2 build: @@ -56,3 +61,4 @@ workflows: - py311 - py312 - py313 + - py314 From 3a60d4c7bd991af131aa6cdec578640d40d4e24f Mon Sep 17 00:00:00 2001 From: Kwan Yuet Stephen Ho Date: Thu, 1 Oct 2026 10:29:18 -0400 Subject: [PATCH 2/7] test for Python 3.14 --- .github/workflows/tests.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 7571426..8ae9dca 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -13,7 +13,7 @@ jobs: continue-on-error: true strategy: matrix: - python-version: ['3.10', '3.11', '3.12', '3.13'] + python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] steps: - uses: actions/checkout@v5 From e5d5ea44e8f7364a47cf457734cf9f7a73ae196a Mon Sep 17 00:00:00 2001 From: Kwan Yuet Stephen Ho Date: Thu, 1 Oct 2026 10:30:50 -0400 Subject: [PATCH 3/7] update CircleCI config version --- .circleci/config.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 146e427..8fdb628 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -1,4 +1,4 @@ -version: 2 +version: 2.1 shared: &shared working_directory: ~/finsim From 5901fd5ea9d1584b13bf86298fc17cc8225e0b81 Mon Sep 17 00:00:00 2001 From: Kwan Yuet Stephen Ho Date: Thu, 1 Oct 2026 10:46:29 -0400 Subject: [PATCH 4/7] updated framework --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 51b227b..1bd5e65 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "finsim" -version = "1.2.2" +version = "1.2.3a1" authors = [ {name = "Kwan Yuet Stephen Ho", email = "stephenhky@yahoo.com.hk"} ] @@ -21,6 +21,7 @@ classifiers = [ "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", + "Programming Language :: Python :: 3.14", "Intended Audience :: Science/Research", "Intended Audience :: Developers", "Intended Audience :: Financial and Insurance Industry" From ac9e26533caee20d2ec84a646d40395ebb5f1761 Mon Sep 17 00:00:00 2001 From: Kwan Yuet Stephen Ho <3810067+stephenhky@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:40:04 -0400 Subject: [PATCH 5/7] started removing Python 3.10 support --- .circleci/config.yml | 6 ------ .github/workflows/tests.yaml | 2 +- pyproject.toml | 3 +-- 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 8fdb628..2f155a6 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -28,11 +28,6 @@ shared: &shared jobs: - py310: - <<: *shared - docker: - - image: cimg/python:3.10 - py311: <<: *shared docker: @@ -57,7 +52,6 @@ workflows: version: 2 build: jobs: - - py310 - py311 - py312 - py313 diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml index 8ae9dca..606e6f0 100644 --- a/.github/workflows/tests.yaml +++ b/.github/workflows/tests.yaml @@ -13,7 +13,7 @@ jobs: continue-on-error: true strategy: matrix: - python-version: ['3.10', '3.11', '3.12', '3.13', '3.14'] + python-version: ['3.11', '3.12', '3.13', '3.14'] steps: - uses: actions/checkout@v5 diff --git a/pyproject.toml b/pyproject.toml index 1bd5e65..79d783b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "finsim" -version = "1.2.3a1" +version = "2.0.0a1" authors = [ {name = "Kwan Yuet Stephen Ho", email = "stephenhky@yahoo.com.hk"} ] @@ -17,7 +17,6 @@ classifiers = [ "Topic :: Scientific/Engineering :: Mathematics", "Topic :: Software Development :: Libraries :: Python Modules", "Topic :: Software Development :: Version Control :: Git", - "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", From 7a07da1827cab119d37724619fc113c4c3ab38a4 Mon Sep 17 00:00:00 2001 From: Kwan Yuet Stephen Ho <3810067+stephenhky@users.noreply.github.com> Date: Thu, 1 Oct 2026 22:58:14 -0400 Subject: [PATCH 6/7] removed support for Python 3.10 --- pyproject.toml | 3 +-- src/finsim/portfolio/dynamic.py | 7 +------ src/finsim/portfolio/portfolio.py | 7 +------ 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 79d783b..2b653b5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,7 +12,7 @@ description = "Financial simulation and inference" readme = {file = "README.md", content-type = "text/markdown"} license = {text = "LGPL"} keywords = ["simulation", "finance", "quantitative finance", "inference", "portfolio analysis"] -requires-python = ">=3.10" +requires-python = ">=3.11" classifiers = [ "Topic :: Scientific/Engineering :: Mathematics", "Topic :: Software Development :: Libraries :: Python Modules", @@ -36,7 +36,6 @@ dependencies = [ "yfinance>=0.2.59", "openpyxl>=3.1.0", "numba", - "typing-extensions", "pydantic" ] diff --git a/src/finsim/portfolio/dynamic.py b/src/finsim/portfolio/dynamic.py index 94b52b5..62ef60c 100644 --- a/src/finsim/portfolio/dynamic.py +++ b/src/finsim/portfolio/dynamic.py @@ -4,15 +4,10 @@ from collections import defaultdict import logging import json -import sys -from typing import Any, Optional +from typing import Any, Optional, Self from os import PathLike from io import TextIOWrapper import warnings -if sys.version_info < (3, 11): - from typing_extensions import Self -else: - from typing import Self import numpy as np import pandas as pd diff --git a/src/finsim/portfolio/portfolio.py b/src/finsim/portfolio/portfolio.py index 10c8482..52bf6d4 100644 --- a/src/finsim/portfolio/portfolio.py +++ b/src/finsim/portfolio/portfolio.py @@ -1,15 +1,10 @@ import json import logging -import sys from collections import defaultdict -from typing import Any, Optional, Literal, Annotated +from typing import Any, Optional, Literal, Annotated, Self from os import PathLike from io import TextIOWrapper -if sys.version_info < (3, 11): - from typing_extensions import Self -else: - from typing import Self import numpy as np from numpy.typing import NDArray From fe230f7e8583c4c43f3ba177494cb1c1f3f14d45 Mon Sep 17 00:00:00 2001 From: Kwan Yuet Stephen Ho <3810067+stephenhky@users.noreply.github.com> Date: Fri, 2 Oct 2026 13:43:20 -0400 Subject: [PATCH 7/7] use click --- docs/conf.py | 4 +- pyproject.toml | 7 ++- src/finsim/retrieve_stock_symbols_cli/cli.py | 59 +++++++++----------- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/docs/conf.py b/docs/conf.py index 5d802b9..5e995c8 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,8 +23,8 @@ author = 'Kwan Yuet Stephen Ho' # The full version, including alpha/beta/rc tags -version = '1.2' -release = '1.2.2' +version = '2.0' +release = '2.0.0' # -- General configuration --------------------------------------------------- diff --git a/pyproject.toml b/pyproject.toml index 2b653b5..0fd7d0f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,7 +36,8 @@ dependencies = [ "yfinance>=0.2.59", "openpyxl>=3.1.0", "numba", - "pydantic" + "pydantic", + "click>=8.0.0" ] [project.urls] @@ -68,3 +69,7 @@ retrieve_stock_symbols = "finsim.retrieve_stock_symbols_cli:main_cli" [project.optional-dependencies] test = ["unittest2", "pytest"] docs = ["sphinx>=4.0.0", "sphinx-rtd-theme>=1.0.0"] + +[tool.pytest.ini_options] +pythonpath = ["src"] + diff --git a/src/finsim/retrieve_stock_symbols_cli/cli.py b/src/finsim/retrieve_stock_symbols_cli/cli.py index d1e56d9..d6f6a4e 100644 --- a/src/finsim/retrieve_stock_symbols_cli/cli.py +++ b/src/finsim/retrieve_stock_symbols_cli/cli.py @@ -1,47 +1,38 @@ - -from argparse import ArgumentParser import os +import click import pandas as pd from ..data.finnhub import FinnHubStockReader -def get_argparser() -> ArgumentParser: - """Create and configure the argument parser for the CLI. - - Returns: - ArgumentParser: Configured argument parser - """ - argparser = ArgumentParser(description='Retrieve stock symbols from Finnhub') - argparser.add_argument('outputpath', help='path of the stock symbols (*.json, *.h5, *.xlsx, *.csv)') - argparser.add_argument('--finnhubtokenpath', help='path of Finnhub tokens') - argparser.add_argument('--useenvtoken', help='Use the environment variable FINNHUBTOKEN as the tokens') - argparser.add_argument('--shorten', default=False, action='store_true', help='shorten list of symbols') - return argparser - - -def main_cli(): +@click.command(help='Retrieve stock symbols from Finnhub') +@click.argument('outputpath') +@click.option('--finnhubtokenpath', default=None, help='path of Finnhub tokens') +@click.option('--useenvtoken', is_flag=True, default=False, help='Use the environment variable FINNHUBTOKEN as the tokens') +@click.option('--shorten', is_flag=True, default=False, help='shorten list of symbols') +def main_cli(outputpath, finnhubtokenpath=None, useenvtoken=False, shorten=False): """Main CLI function to retrieve stock symbols from Finnhub and save them to a file. This function parses command line arguments, retrieves stock symbols from Finnhub, optionally filters them, and saves them to a file in various formats. """ - # parsing argument - args = get_argparser().parse_args() - extension = os.path.splitext(args.outputpath)[-1] + extension = os.path.splitext(outputpath)[-1] # check if the output directory exists - dirname = os.path.dirname(args.outputpath) - if not os.path.isdir(dirname): + dirname = os.path.dirname(outputpath) + if dirname and not os.path.isdir(dirname): raise FileNotFoundError('Directory {} does not exist!'.format(dirname)) # get Finnhub tokens - if args.useenvtoken: + if useenvtoken: finnhub_token = os.getenv('FINNHUBTOKEN') if finnhub_token is None: raise ValueError('Finnhub tokens not found in the environment variable $FINNHUBTOKEN.') else: - finnhub_token = open(args.finnhubtokenpath, 'r').read().strip() + if finnhubtokenpath is None: + raise ValueError('Either --finnhubtokenpath or --useenvtoken must be specified.') + with open(finnhubtokenpath, 'r') as f: + finnhub_token = f.read().strip() # initialize FinnHub reader finnreader = FinnHubStockReader(finnhub_token) @@ -50,21 +41,25 @@ def main_cli(): allsym = finnreader.get_all_US_symbols() allsymdf = pd.DataFrame(allsym) - if args.shorten: + if shorten: filtered_symdf = allsymdf[allsymdf['mic'].isin(['XNAS', 'XNYS', 'ARCX'])] filtered_symdf = filtered_symdf[~filtered_symdf['type'].isin(['PUBLIC'])] - filtered_symdf = filtered_symdf[~filtered_symdf['symbol'].str.contains('\.')] + filtered_symdf = filtered_symdf[~filtered_symdf['symbol'].str.contains(r'\.')] allsymdf = filtered_symdf if extension == '.h5': - allsymdf.to_hdf(args.outputpath, key='fintable') + allsymdf.to_hdf(outputpath, key='fintable') elif extension == '.json': - allsymdf.to_json(args.outputpath, orient='records') + allsymdf.to_json(outputpath, orient='records') elif extension == '.xlsx': - allsymdf.to_excel(args.outputpath) + allsymdf.to_excel(outputpath) elif extension == '.csv': - allsymdf.to_csv(args.outputpath) + allsymdf.to_csv(outputpath) elif extension == '.pickle' or extension == '.pkl': - allsymdf.to_pickle(args.outputpath) + allsymdf.to_pickle(outputpath) else: - raise IOError('Extension {} not recognized.'.format(extension)) \ No newline at end of file + raise IOError('Extension {} not recognized.'.format(extension)) + + +if __name__ == '__main__': + main_cli() \ No newline at end of file