quantflow 0.2.5__tar.gz → 0.2.7__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {quantflow-0.2.5 → quantflow-0.2.7}/PKG-INFO +16 -7
- {quantflow-0.2.5 → quantflow-0.2.7}/pyproject.toml +29 -34
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/__init__.py +1 -1
- quantflow-0.2.7/quantflow/cli/__init__.py +131 -0
- quantflow-0.2.7/quantflow/cli/settings.py +11 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/data/client.py +2 -2
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/data/fmp.py +11 -5
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/poisson.py +1 -1
- quantflow-0.2.7/quantflow/utils/df.py +72 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/plot.py +15 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/types.py +3 -2
- {quantflow-0.2.5 → quantflow-0.2.7}/readme.md +6 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/LICENSE +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/data/__init__.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/options/__init__.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/options/bs.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/options/calibration.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/options/inputs.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/options/pricer.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/options/surface.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/py.typed +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/__init__.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/base.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/bns.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/cir.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/copula.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/dsp.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/heston.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/jump_diffusion.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/ou.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/sp/weiner.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/__init__.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/bins.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/dates.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/distributions.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/functions.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/interest_rates.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/marginal.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/numbers.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/paths.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/transforms.py +0 -0
- {quantflow-0.2.5 → quantflow-0.2.7}/quantflow/utils/volatility.py +0 -0
|
@@ -1,23 +1,26 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: quantflow
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.7
|
|
4
4
|
Summary: quantitative analysis
|
|
5
5
|
License: BSD-3-Clause
|
|
6
6
|
Author: Luca
|
|
7
7
|
Author-email: luca@quantmind.com
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.11
|
|
9
9
|
Classifier: License :: OSI Approved :: BSD License
|
|
10
10
|
Classifier: Programming Language :: Python :: 3
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.11
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
14
|
Provides-Extra: data
|
|
15
15
|
Requires-Dist: aiohttp (>=3.8.1,<4.0.0) ; extra == "data"
|
|
16
|
-
Requires-Dist:
|
|
17
|
-
Requires-Dist:
|
|
18
|
-
Requires-Dist:
|
|
16
|
+
Requires-Dist: asciichart (>=0.1,<0.2)
|
|
17
|
+
Requires-Dist: asciichartpy (>=1.5.25,<2.0.0)
|
|
18
|
+
Requires-Dist: ccy[cli] (==1.6.0)
|
|
19
|
+
Requires-Dist: polars[pandas,pyarrow] (==1.11.0)
|
|
20
|
+
Requires-Dist: prompt-toolkit (>=3.0.43,<4.0.0)
|
|
19
21
|
Requires-Dist: pydantic (>=2.0.2,<3.0.0)
|
|
20
|
-
Requires-Dist:
|
|
22
|
+
Requires-Dist: python-dotenv (>=1.0.1,<2.0.0)
|
|
23
|
+
Requires-Dist: scipy (>=1.14.1,<2.0.0)
|
|
21
24
|
Project-URL: Documentation, https://quantmind.github.io/quantflow/
|
|
22
25
|
Project-URL: Homepage, https://github.com/quantmind/quantflow
|
|
23
26
|
Project-URL: Repository, https://github.com/quantmind/quantflow
|
|
@@ -50,3 +53,9 @@ pip install quantflow
|
|
|
50
53
|
* [quantflow.options](https://github.com/quantmind/quantflow/tree/main/quantflow/options) option pricing and calibration
|
|
51
54
|
* [quantflow.sp](https://github.com/quantmind/quantflow/tree/main/quantflow/sp) stochastic process primitives
|
|
52
55
|
|
|
56
|
+
|
|
57
|
+
## Command line tools
|
|
58
|
+
|
|
59
|
+
When installing with the extra `data` dependencies, it is possible to use the command line tool `qf`
|
|
60
|
+
|
|
61
|
+
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "quantflow"
|
|
3
|
-
version = "0.2.
|
|
3
|
+
version = "0.2.7"
|
|
4
4
|
description = "quantitative analysis"
|
|
5
5
|
authors = ["Luca <luca@quantmind.com>"]
|
|
6
6
|
license = "BSD-3-Clause"
|
|
@@ -12,21 +12,24 @@ Repository = "https://github.com/quantmind/quantflow"
|
|
|
12
12
|
Documentation = "https://quantmind.github.io/quantflow/"
|
|
13
13
|
|
|
14
14
|
[tool.poetry.dependencies]
|
|
15
|
-
python = ">=3.
|
|
16
|
-
|
|
17
|
-
scipy = "^1.10.1"
|
|
18
|
-
pandas = "^2.0.1"
|
|
15
|
+
python = ">=3.11"
|
|
16
|
+
scipy = "^1.14.1"
|
|
19
17
|
aiohttp = {version = "^3.8.1", optional = true}
|
|
20
18
|
pydantic = "^2.0.2"
|
|
21
|
-
|
|
19
|
+
ccy = {version="1.6.0", extras=["cli"]}
|
|
20
|
+
asciichart = "^0.1"
|
|
21
|
+
python-dotenv = "^1.0.1"
|
|
22
|
+
asciichartpy = "^1.5.25"
|
|
23
|
+
prompt-toolkit = "^3.0.43"
|
|
24
|
+
polars = {version = "1.11.0", extras=["pandas", "pyarrow"]}
|
|
22
25
|
|
|
23
26
|
[tool.poetry.group.dev.dependencies]
|
|
24
27
|
black = "^24.1.1"
|
|
25
|
-
pytest-cov = "^
|
|
26
|
-
mypy = "^1.
|
|
28
|
+
pytest-cov = "^5.0.0"
|
|
29
|
+
mypy = "^1.13.0"
|
|
27
30
|
ghp-import = "^2.0.2"
|
|
28
|
-
ruff = "^0.1
|
|
29
|
-
pytest-asyncio = "^0.
|
|
31
|
+
ruff = "^0.7.1"
|
|
32
|
+
pytest-asyncio = "^0.24.0"
|
|
30
33
|
|
|
31
34
|
|
|
32
35
|
[tool.poetry.extras]
|
|
@@ -36,14 +39,17 @@ data = ["aiohttp"]
|
|
|
36
39
|
optional = true
|
|
37
40
|
|
|
38
41
|
[tool.poetry.group.book.dependencies]
|
|
39
|
-
jupyter-book = "^0.
|
|
40
|
-
nbconvert = "^
|
|
42
|
+
jupyter-book = "^1.0.0"
|
|
43
|
+
nbconvert = "^7.16.3"
|
|
41
44
|
jupytext = "^1.13.8"
|
|
42
|
-
plotly = "^5.
|
|
45
|
+
plotly = "^5.20.0"
|
|
43
46
|
jupyterlab = "^4.0.2"
|
|
44
47
|
sympy = "^1.12"
|
|
45
48
|
ipywidgets = "^8.0.7"
|
|
46
49
|
|
|
50
|
+
[tool.poetry.scripts]
|
|
51
|
+
qf = "quantflow.cli:main"
|
|
52
|
+
|
|
47
53
|
[build-system]
|
|
48
54
|
requires = ["poetry-core>=1.0.0"]
|
|
49
55
|
build-backend = "poetry.core.masonry.api"
|
|
@@ -56,15 +62,12 @@ asyncio_mode = "auto"
|
|
|
56
62
|
testpaths = [
|
|
57
63
|
"quantflow_tests"
|
|
58
64
|
]
|
|
59
|
-
filterwarnings = [
|
|
60
|
-
"ignore::DeprecationWarning:dateutil.*:"
|
|
61
|
-
]
|
|
62
65
|
|
|
63
66
|
[tool.isort]
|
|
64
67
|
profile = "black"
|
|
65
68
|
|
|
66
69
|
[tool.ruff]
|
|
67
|
-
select = ["E", "F"]
|
|
70
|
+
lint.select = ["E", "F"]
|
|
68
71
|
extend-exclude = ["fluid_apps/db/migrations"]
|
|
69
72
|
line-length = 88
|
|
70
73
|
|
|
@@ -78,21 +81,13 @@ disallow_untyped_defs = true
|
|
|
78
81
|
warn_no_return = true
|
|
79
82
|
|
|
80
83
|
[[tool.mypy.overrides]]
|
|
81
|
-
module =
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
module = "pandas.*"
|
|
90
|
-
ignore_missing_imports = true
|
|
91
|
-
|
|
92
|
-
[[tool.mypy.overrides]]
|
|
93
|
-
module = "plotly.*"
|
|
94
|
-
ignore_missing_imports = true
|
|
95
|
-
|
|
96
|
-
[[tool.mypy.overrides]]
|
|
97
|
-
module = "scipy.*"
|
|
84
|
+
module = [
|
|
85
|
+
"asciichartpy.*",
|
|
86
|
+
"quantflow_tests.*",
|
|
87
|
+
"IPython.*",
|
|
88
|
+
"pandas.*",
|
|
89
|
+
"plotly.*",
|
|
90
|
+
"scipy.*"
|
|
91
|
+
]
|
|
98
92
|
ignore_missing_imports = true
|
|
93
|
+
disallow_untyped_defs = false
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
import os
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
import dotenv
|
|
8
|
+
import pandas as pd
|
|
9
|
+
from asciichartpy import plot
|
|
10
|
+
from ccy.cli.console import df_to_rich
|
|
11
|
+
from prompt_toolkit import PromptSession
|
|
12
|
+
from prompt_toolkit.history import FileHistory
|
|
13
|
+
from rich.console import Console
|
|
14
|
+
from rich.text import Text
|
|
15
|
+
|
|
16
|
+
from quantflow.data.fmp import FMP
|
|
17
|
+
|
|
18
|
+
from . import settings
|
|
19
|
+
|
|
20
|
+
dotenv.load_dotenv()
|
|
21
|
+
|
|
22
|
+
FREQUENCIES = tuple(FMP().historical_frequencies())
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
@click.group()
|
|
26
|
+
def qf() -> None:
|
|
27
|
+
pass
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@qf.command()
|
|
31
|
+
@click.argument("symbol")
|
|
32
|
+
def profile(symbol: str) -> None:
|
|
33
|
+
"""Company profile"""
|
|
34
|
+
data = asyncio.run(get_profile(symbol))[0]
|
|
35
|
+
main.print(data.pop("description"))
|
|
36
|
+
df = pd.DataFrame(data.items(), columns=["Key", "Value"])
|
|
37
|
+
main.print(df_to_rich(df))
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@qf.command()
|
|
41
|
+
@click.argument("symbol")
|
|
42
|
+
@click.option(
|
|
43
|
+
"-h",
|
|
44
|
+
"--height",
|
|
45
|
+
type=int,
|
|
46
|
+
default=20,
|
|
47
|
+
show_default=True,
|
|
48
|
+
help="Chart height",
|
|
49
|
+
)
|
|
50
|
+
@click.option(
|
|
51
|
+
"-l",
|
|
52
|
+
"--length",
|
|
53
|
+
type=int,
|
|
54
|
+
default=100,
|
|
55
|
+
show_default=True,
|
|
56
|
+
help="Number of data points",
|
|
57
|
+
)
|
|
58
|
+
@click.option(
|
|
59
|
+
"-f",
|
|
60
|
+
"--frequency",
|
|
61
|
+
type=click.Choice(FREQUENCIES),
|
|
62
|
+
default="",
|
|
63
|
+
help="Number of data points",
|
|
64
|
+
)
|
|
65
|
+
def chart(symbol: str, height: int, length: int, frequency: str) -> None:
|
|
66
|
+
"""Symbol chart"""
|
|
67
|
+
df = asyncio.run(get_prices(symbol, frequency))
|
|
68
|
+
data = list(reversed(df["close"].tolist()[:length]))
|
|
69
|
+
print(plot(data, {"height": height}))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
async def get_prices(symbol: str, frequency: str) -> pd.DataFrame:
|
|
73
|
+
async with FMP() as cli:
|
|
74
|
+
return await cli.prices(symbol, frequency)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
async def get_profile(symbol: str) -> list[dict]:
|
|
78
|
+
async with FMP() as cli:
|
|
79
|
+
return await cli.profile(symbol)
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class App:
|
|
84
|
+
console: Console = field(default_factory=Console)
|
|
85
|
+
|
|
86
|
+
def __call__(self) -> None:
|
|
87
|
+
os.makedirs(settings.SETTINGS_DIRECTORY, exist_ok=True)
|
|
88
|
+
history = FileHistory(str(settings.HIST_FILE_PATH))
|
|
89
|
+
session: PromptSession = PromptSession(history=history)
|
|
90
|
+
|
|
91
|
+
self.print("Welcome to QuantFlow!", style="bold green")
|
|
92
|
+
self.handle_command("help")
|
|
93
|
+
|
|
94
|
+
try:
|
|
95
|
+
while True:
|
|
96
|
+
try:
|
|
97
|
+
text = session.prompt("quantflow> ")
|
|
98
|
+
except KeyboardInterrupt:
|
|
99
|
+
break
|
|
100
|
+
else:
|
|
101
|
+
self.handle_command(text)
|
|
102
|
+
except click.Abort:
|
|
103
|
+
self.console.print(Text("Bye!", style="bold magenta"))
|
|
104
|
+
|
|
105
|
+
def print(self, text_alike: Any, style: str = "") -> None:
|
|
106
|
+
if isinstance(text_alike, str):
|
|
107
|
+
style = style or "cyan"
|
|
108
|
+
text_alike = Text(f"\n{text_alike}\n", style="cyan")
|
|
109
|
+
self.console.print(text_alike)
|
|
110
|
+
|
|
111
|
+
def error(self, err: str | Exception) -> None:
|
|
112
|
+
self.console.print(Text(f"\n{err}\n", style="bold red"))
|
|
113
|
+
|
|
114
|
+
def handle_command(self, text: str) -> None:
|
|
115
|
+
self.current_command = text.split(" ")[0].strip()
|
|
116
|
+
if not text:
|
|
117
|
+
return
|
|
118
|
+
elif text == "help":
|
|
119
|
+
return qf.main(["--help"], standalone_mode=False)
|
|
120
|
+
elif text == "exit":
|
|
121
|
+
raise click.Abort()
|
|
122
|
+
|
|
123
|
+
try:
|
|
124
|
+
qf.main(text.split(), standalone_mode=False)
|
|
125
|
+
except click.exceptions.MissingParameter as e:
|
|
126
|
+
self.error(e)
|
|
127
|
+
except click.exceptions.NoSuchOption as e:
|
|
128
|
+
self.error(e)
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
main = App()
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# IMPORTATION STANDARD
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
4
|
+
# Installation related paths
|
|
5
|
+
HOME_DIRECTORY = Path.home()
|
|
6
|
+
PACKAGE_DIRECTORY = Path(__file__).parent.parent.parent
|
|
7
|
+
REPOSITORY_DIRECTORY = PACKAGE_DIRECTORY.parent
|
|
8
|
+
|
|
9
|
+
SETTINGS_DIRECTORY = HOME_DIRECTORY / ".quantflow"
|
|
10
|
+
SETTINGS_ENV_FILE = SETTINGS_DIRECTORY / ".env"
|
|
11
|
+
HIST_FILE_PATH = SETTINGS_DIRECTORY / ".quantflow.his"
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import json
|
|
2
2
|
import os
|
|
3
3
|
from dataclasses import dataclass
|
|
4
|
-
from typing import Any
|
|
4
|
+
from typing import Any, Self
|
|
5
5
|
|
|
6
6
|
from aiohttp import ClientResponse, ClientSession
|
|
7
7
|
from aiohttp.client_exceptions import ContentTypeError
|
|
@@ -52,7 +52,7 @@ class HttpClient:
|
|
|
52
52
|
await self.session.close()
|
|
53
53
|
self.session = None
|
|
54
54
|
|
|
55
|
-
async def __aenter__(self) ->
|
|
55
|
+
async def __aenter__(self) -> Self:
|
|
56
56
|
return self
|
|
57
57
|
|
|
58
58
|
async def __aexit__(self, *args: Any) -> None:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import os
|
|
2
|
-
from dataclasses import dataclass
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
3
|
from datetime import date, timedelta
|
|
4
4
|
from typing import Any, cast
|
|
5
5
|
|
|
@@ -12,7 +12,7 @@ from .client import HttpClient, compact
|
|
|
12
12
|
@dataclass
|
|
13
13
|
class FMP(HttpClient):
|
|
14
14
|
url: str = "https://financialmodelingprep.com/api"
|
|
15
|
-
key: str = os.environ.get("FMP_API_KEY", "")
|
|
15
|
+
key: str = field(default_factory=lambda: os.environ.get("FMP_API_KEY", ""))
|
|
16
16
|
|
|
17
17
|
async def stocks(self, **kw: Any) -> list[dict]:
|
|
18
18
|
return await self.get_path("v3/stock/list", **kw)
|
|
@@ -62,7 +62,7 @@ class FMP(HttpClient):
|
|
|
62
62
|
# Rating
|
|
63
63
|
|
|
64
64
|
async def rating(self, ticker: str, **kw: Any) -> list[dict]:
|
|
65
|
-
"""Company
|
|
65
|
+
"""Company rating - real time"""
|
|
66
66
|
return await self.get_path(f"v3/rating/{ticker}", **kw)
|
|
67
67
|
|
|
68
68
|
async def etf_holders(self, ticker: str, **kw: Any) -> list[dict]:
|
|
@@ -111,7 +111,9 @@ class FMP(HttpClient):
|
|
|
111
111
|
**self.params(compact(query=query, exchange=exchange, limit=limit), **kw),
|
|
112
112
|
)
|
|
113
113
|
|
|
114
|
-
async def prices(
|
|
114
|
+
async def prices(
|
|
115
|
+
self, ticker: str, frequency: str = "", to_date: bool = False, **kw: Any
|
|
116
|
+
) -> pd.DataFrame:
|
|
115
117
|
base = (
|
|
116
118
|
"historical-price-full/"
|
|
117
119
|
if not frequency
|
|
@@ -121,10 +123,14 @@ class FMP(HttpClient):
|
|
|
121
123
|
if isinstance(data, dict):
|
|
122
124
|
data = data.get("historical", [])
|
|
123
125
|
df = pd.DataFrame(data)
|
|
124
|
-
if "date" in df.columns:
|
|
126
|
+
if to_date and "date" in df.columns:
|
|
125
127
|
df["date"] = pd.to_datetime(df["date"])
|
|
126
128
|
return df
|
|
127
129
|
|
|
130
|
+
# forex
|
|
131
|
+
async def forex_list(self) -> list[dict]:
|
|
132
|
+
return await self.get_path("v3/symbol/available-forex-currency-pairs")
|
|
133
|
+
|
|
128
134
|
def historical_frequencies(self) -> dict:
|
|
129
135
|
return {
|
|
130
136
|
"1min": 1,
|
|
@@ -202,6 +202,6 @@ class MarginalDiscrete1D(StochasticProcess1DMarginal):
|
|
|
202
202
|
if simpson_rule:
|
|
203
203
|
result.append(a * simpson(f, x=frequency))
|
|
204
204
|
else:
|
|
205
|
-
result.append(a * np.
|
|
205
|
+
result.append(a * np.trapezoid(f, frequency))
|
|
206
206
|
pdf = np.maximum(np.diff(result, prepend=0), 0)
|
|
207
207
|
return TransformResult(x=x, y=np.cumsum(pdf)) # type: ignore[arg-type]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
from typing import Self, TypeAlias
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
import polars as pl
|
|
5
|
+
import pandas as pd
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
DataFrame: TypeAlias = pl.DataFrame | pd.DataFrame
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def to_polars(df: DataFrame) -> pl.DataFrame:
|
|
12
|
+
if isinstance(df, pd.DataFrame):
|
|
13
|
+
return pl.DataFrame(df)
|
|
14
|
+
return df
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class DFutils:
|
|
18
|
+
def __init__(self, df: DataFrame, period: float = 1) -> None:
|
|
19
|
+
self.df = to_polars(df)
|
|
20
|
+
self.period = period
|
|
21
|
+
|
|
22
|
+
def __repr__(self) -> str:
|
|
23
|
+
return repr(self.df)
|
|
24
|
+
|
|
25
|
+
def __str__(self) -> str:
|
|
26
|
+
return str(self.df)
|
|
27
|
+
|
|
28
|
+
def clone(self, df: DataFrame) -> Self:
|
|
29
|
+
return self.__class__(df, self.period)
|
|
30
|
+
|
|
31
|
+
def with_mid(self) -> Self:
|
|
32
|
+
"""Adds mid and spread columns to the dataframe"""
|
|
33
|
+
df = self.df
|
|
34
|
+
return self.clone(
|
|
35
|
+
self.df.with_columns(
|
|
36
|
+
mid=0.5 * (df["ask_price"] + df["bid_price"]),
|
|
37
|
+
mid_volume=0.5 * (df["ask_amount"] + df["bid_amount"]),
|
|
38
|
+
spread=20000
|
|
39
|
+
* (df["ask_price"] - df["bid_price"])
|
|
40
|
+
/ (df["ask_price"] + df["bid_price"]),
|
|
41
|
+
)
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def with_parkinson(self) -> Self:
|
|
45
|
+
"""Adds parkinson volatility column to the dataframe
|
|
46
|
+
|
|
47
|
+
This requires the high and low columns to be present
|
|
48
|
+
"""
|
|
49
|
+
c = 10000 / np.sqrt(4 * self.period * np.log(2))
|
|
50
|
+
return self.clone(
|
|
51
|
+
df=self.df.with_columns(
|
|
52
|
+
pk=c * (pl.col("high") / pl.col("low")).map_elements(np.log)
|
|
53
|
+
)
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
def with_rogers_satchel(self) -> Self:
|
|
57
|
+
"""Adds Rogers-Satchel volatility column to the dataframe
|
|
58
|
+
|
|
59
|
+
This requires the high and low columns to be present
|
|
60
|
+
"""
|
|
61
|
+
c = 10000 / np.sqrt(self.period)
|
|
62
|
+
return self.clone(
|
|
63
|
+
df=self.df.with_columns(
|
|
64
|
+
rs=c
|
|
65
|
+
* (
|
|
66
|
+
(pl.col("high") / pl.col("open")).map_elements(np.log)
|
|
67
|
+
* (pl.col("high") / pl.col("close")).map_elements(np.log)
|
|
68
|
+
+ (pl.col("low") / pl.col("open")).map_elements(np.log)
|
|
69
|
+
* (pl.col("low") / pl.col("close")).map_elements(np.log)
|
|
70
|
+
).map_elements(np.sqrt)
|
|
71
|
+
)
|
|
72
|
+
)
|
|
@@ -183,3 +183,18 @@ def plot3d(
|
|
|
183
183
|
if kwargs:
|
|
184
184
|
fig.update_layout(**kwargs)
|
|
185
185
|
return fig
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def candlestick_plot(df: pd.DataFrame, slider: bool = True) -> Any:
|
|
189
|
+
fig = go.Figure(
|
|
190
|
+
data=go.Candlestick(
|
|
191
|
+
x=df["date"],
|
|
192
|
+
open=df["open"],
|
|
193
|
+
high=df["high"],
|
|
194
|
+
low=df["low"],
|
|
195
|
+
close=df["close"],
|
|
196
|
+
)
|
|
197
|
+
)
|
|
198
|
+
if slider is False:
|
|
199
|
+
fig.update_layout(xaxis_rangeslider_visible=False)
|
|
200
|
+
return fig
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
from decimal import Decimal
|
|
2
2
|
from typing import Optional, Union
|
|
3
3
|
|
|
4
|
+
import pandas as pd
|
|
4
5
|
import numpy as np
|
|
5
6
|
import numpy.typing as npt
|
|
6
7
|
|
|
7
8
|
Number = Decimal
|
|
8
9
|
Numbers = Union[int, float, np.number]
|
|
9
10
|
NumberType = Union[float, int, str, Number]
|
|
10
|
-
Vector = Union[int, float, complex, np.ndarray]
|
|
11
|
-
FloatArray = npt.NDArray[np.
|
|
11
|
+
Vector = Union[int, float, complex, np.ndarray, pd.Series]
|
|
12
|
+
FloatArray = npt.NDArray[np.float64]
|
|
12
13
|
IntArray = npt.NDArray[np.int_]
|
|
13
14
|
FloatArrayLike = FloatArray | float
|
|
14
15
|
|
|
@@ -24,3 +24,9 @@ pip install quantflow
|
|
|
24
24
|
* [quantflow.data](https://github.com/quantmind/quantflow/tree/main/quantflow/data) data APIs (requires `quantflow[data]`)
|
|
25
25
|
* [quantflow.options](https://github.com/quantmind/quantflow/tree/main/quantflow/options) option pricing and calibration
|
|
26
26
|
* [quantflow.sp](https://github.com/quantmind/quantflow/tree/main/quantflow/sp) stochastic process primitives
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Command line tools
|
|
30
|
+
|
|
31
|
+
When installing with the extra `data` dependencies, it is possible to use the command line tool `qf`
|
|
32
|
+
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|