Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
version: 2
version: 2.1

shared: &shared
working_directory: ~/finsim
Expand Down Expand Up @@ -28,11 +28,6 @@ shared: &shared


jobs:
py310:
<<: *shared
docker:
- image: cimg/python:3.10

py311:
<<: *shared
docker:
Expand All @@ -48,11 +43,16 @@ jobs:
docker:
- image: cimg/python:3.13

py314:
<<: *shared
docker:
- image: cimg/python:3.14

workflows:
version: 2
build:
jobs:
- py310
- py311
- py312
- py313
- py314
2 changes: 1 addition & 1 deletion .github/workflows/tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
continue-on-error: true
strategy:
matrix:
python-version: ['3.10', '3.11', '3.12', '3.13']
python-version: ['3.11', '3.12', '3.13', '3.14']

steps:
- uses: actions/checkout@v5
Expand Down
4 changes: 2 additions & 2 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 ---------------------------------------------------
Expand Down
14 changes: 9 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -4,23 +4,23 @@ build-backend = "setuptools.build_meta"

[project]
name = "finsim"
version = "1.2.2"
version = "2.0.0a1"
authors = [
{name = "Kwan Yuet Stephen Ho", email = "stephenhky@yahoo.com.hk"}
]
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",
"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",
"Programming Language :: Python :: 3.14",
"Intended Audience :: Science/Research",
"Intended Audience :: Developers",
"Intended Audience :: Financial and Insurance Industry"
Expand All @@ -36,8 +36,8 @@ dependencies = [
"yfinance>=0.2.59",
"openpyxl>=3.1.0",
"numba",
"typing-extensions",
"pydantic"
"pydantic",
"click>=8.0.0"
]

[project.urls]
Expand Down Expand Up @@ -69,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"]

7 changes: 1 addition & 6 deletions src/finsim/portfolio/dynamic.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
7 changes: 1 addition & 6 deletions src/finsim/portfolio/portfolio.py
Original file line number Diff line number Diff line change
@@ -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
Expand Down
59 changes: 27 additions & 32 deletions src/finsim/retrieve_stock_symbols_cli/cli.py
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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))
raise IOError('Extension {} not recognized.'.format(extension))


if __name__ == '__main__':
main_cli()
Loading