finmiti 0.0.1__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.
- finmiti-0.0.1/PKG-INFO +136 -0
- finmiti-0.0.1/README.md +100 -0
- finmiti-0.0.1/pyproject.toml +74 -0
- finmiti-0.0.1/setup.cfg +4 -0
- finmiti-0.0.1/src/finmiti/__init__.py +12 -0
- finmiti-0.0.1/src/finmiti/backtest_handler/__init__.py +2 -0
- finmiti-0.0.1/src/finmiti/backtest_handler/base.py +19 -0
- finmiti-0.0.1/src/finmiti/clients/__init__.py +2 -0
- finmiti-0.0.1/src/finmiti/clients/client_5paisa.py +242 -0
- finmiti-0.0.1/src/finmiti/constants.py +133 -0
- finmiti-0.0.1/src/finmiti/executioners.py +49 -0
- finmiti-0.0.1/src/finmiti/portfolio_handler/__init__.py +2 -0
- finmiti-0.0.1/src/finmiti/portfolio_handler/base.py +231 -0
- finmiti-0.0.1/src/finmiti/stocks_handler/__init__.py +3 -0
- finmiti-0.0.1/src/finmiti/stocks_handler/stock.py +209 -0
- finmiti-0.0.1/src/finmiti/stocks_handler/stockdict.py +109 -0
- finmiti-0.0.1/src/finmiti/strategy_handler/__init__.py +2 -0
- finmiti-0.0.1/src/finmiti/strategy_handler/base.py +36 -0
- finmiti-0.0.1/src/finmiti/utils.py +7 -0
- finmiti-0.0.1/src/finmiti.egg-info/PKG-INFO +136 -0
- finmiti-0.0.1/src/finmiti.egg-info/SOURCES.txt +22 -0
- finmiti-0.0.1/src/finmiti.egg-info/dependency_links.txt +1 -0
- finmiti-0.0.1/src/finmiti.egg-info/requires.txt +20 -0
- finmiti-0.0.1/src/finmiti.egg-info/top_level.txt +1 -0
finmiti-0.0.1/PKG-INFO
ADDED
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: finmiti
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Persnal project for stock market data analysis
|
|
5
|
+
Author-email: Darshan Rathod <darshan.rathod1994@gmail.com>
|
|
6
|
+
Maintainer-email: Darshan Rathod <darshan.rathod1994@gmail.com>
|
|
7
|
+
License: MIT
|
|
8
|
+
Project-URL: Homepage, https://dev-ddr.github.io/finmiti/
|
|
9
|
+
Project-URL: Repository, https://github.com/dev-ddr/finmiti
|
|
10
|
+
Project-URL: Issues, https://github.com/dev-ddr/finmiti/issues
|
|
11
|
+
Keywords: finance,stock-market,quant,research,personal
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Operating System :: OS Independent
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
Requires-Dist: numpy==2.3.5
|
|
18
|
+
Requires-Dist: pandas==2.3.3
|
|
19
|
+
Requires-Dist: pyyaml==6.0.3
|
|
20
|
+
Requires-Dist: duckdb==1.4.2
|
|
21
|
+
Requires-Dist: pyarrow==22.0.0
|
|
22
|
+
Requires-Dist: py5paisa==0.7.21.2
|
|
23
|
+
Requires-Dist: pydantic
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest; extra == "dev"
|
|
26
|
+
Requires-Dist: ruff; extra == "dev"
|
|
27
|
+
Requires-Dist: jupyter; extra == "dev"
|
|
28
|
+
Requires-Dist: notebook; extra == "dev"
|
|
29
|
+
Requires-Dist: sphinx; extra == "dev"
|
|
30
|
+
Requires-Dist: mkdocs; extra == "dev"
|
|
31
|
+
Requires-Dist: mkdocs-material; extra == "dev"
|
|
32
|
+
Requires-Dist: mkdocstrings[python]; extra == "dev"
|
|
33
|
+
Requires-Dist: nbconvert; extra == "dev"
|
|
34
|
+
Requires-Dist: matplotlib; extra == "dev"
|
|
35
|
+
Requires-Dist: plotly; extra == "dev"
|
|
36
|
+
|
|
37
|
+
# Finmiti
|
|
38
|
+
|
|
39
|
+
**This project is developed for my personal use.** I am developing this to keep the documentation, architecture and pipeline consistent so that I can focus more on developing strategies instead of developing pipelines.
|
|
40
|
+
|
|
41
|
+
Visit [Finmiti](https://dev-ddr.github.io/finmetry/) guide for further steps.
|
|
42
|
+
|
|
43
|
+
> This project is solely developed for my personal use. I am publishing this only to keep myself updated and to remove the headache of setting up the framework again and again.
|
|
44
|
+
|
|
45
|
+
---
|
|
46
|
+
|
|
47
|
+
**Finmiti is a research-first quantitative trading framework** designed to keep
|
|
48
|
+
strategy logic, execution logic, and accounting logic strictly separated.
|
|
49
|
+
|
|
50
|
+
It exists to eliminate repeated reinvention of trading pipelines, so you can
|
|
51
|
+
focus on **researching strategies**, not rebuilding infrastructure.
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
## What Finmiti Is (and Is Not)
|
|
55
|
+
|
|
56
|
+
Finmiti is:
|
|
57
|
+
|
|
58
|
+
- a framework for systematic trading research
|
|
59
|
+
- equally suited for backtesting and live trading
|
|
60
|
+
- opinionated by design
|
|
61
|
+
- built around explicit, auditable abstractions
|
|
62
|
+
|
|
63
|
+
Finmiti is **not**:
|
|
64
|
+
|
|
65
|
+
- a strategy library
|
|
66
|
+
- a signal generator
|
|
67
|
+
- a black-box trading system
|
|
68
|
+
|
|
69
|
+
If you want flexibility at the cost of correctness, this framework will feel restrictive.
|
|
70
|
+
That restriction is intentional.
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
## The Core Trading Loop
|
|
74
|
+
|
|
75
|
+
Every strategy in finmiti follows the same explicit loop:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
Market Data → Strategy → Orders → Portfolio → Execution → Accounting
|
|
79
|
+
````
|
|
80
|
+
|
|
81
|
+
Each stage is implemented as a **separate module** with strict responsibilities.
|
|
82
|
+
|
|
83
|
+
This guarantees that:
|
|
84
|
+
|
|
85
|
+
* strategies remain stateless
|
|
86
|
+
* execution assumptions are explicit
|
|
87
|
+
* accounting is consistent
|
|
88
|
+
* backtests can be trusted
|
|
89
|
+
* live trading reuses the same abstractions
|
|
90
|
+
|
|
91
|
+
## Major Modules
|
|
92
|
+
|
|
93
|
+
Finmiti is organized into the following conceptual modules:
|
|
94
|
+
|
|
95
|
+
### [Client Handling](https://dev-ddr.github.io/finmetry/concepts/client_handling_module/)
|
|
96
|
+
|
|
97
|
+
Handles interaction with external systems such as broker APIs and live data feeds.
|
|
98
|
+
Keeps the rest of the framework broker-agnostic.
|
|
99
|
+
|
|
100
|
+
### [Stocks](https://dev-ddr.github.io/finmetry/concepts/stocks_handling_module/)
|
|
101
|
+
|
|
102
|
+
Manages symbols, historical data, and OHLCV storage.
|
|
103
|
+
Acts as the foundation for all market data access.
|
|
104
|
+
|
|
105
|
+
### [Strategy](https://dev-ddr.github.io/finmetry/concepts/strategy_handling_module/)
|
|
106
|
+
|
|
107
|
+
Consumes immutable market snapshots and emits **order intent only**.
|
|
108
|
+
Strategies never manage cash, positions, or execution details.
|
|
109
|
+
|
|
110
|
+
### [Orders](https://dev-ddr.github.io/finmetry/concepts/order/)
|
|
111
|
+
|
|
112
|
+
Orders are the contract between strategy, portfolio, and execution.
|
|
113
|
+
They represent intent, not outcome.
|
|
114
|
+
|
|
115
|
+
### [Executioners](https://dev-ddr.github.io/finmetry/concepts/executioners/)
|
|
116
|
+
|
|
117
|
+
Simulate (or connect to) market reality:
|
|
118
|
+
slippage, brokerage, partial fills, or live execution.
|
|
119
|
+
|
|
120
|
+
### [Portfolio](https://dev-ddr.github.io/finmetry/concepts/portfolio_handling_module/)
|
|
121
|
+
|
|
122
|
+
The single source of truth for positions, cash, and PnL.
|
|
123
|
+
All state mutation happens here.
|
|
124
|
+
|
|
125
|
+
### [Backtesting](https://dev-ddr.github.io/finmetry/concepts/backtesting_handling_module/)
|
|
126
|
+
|
|
127
|
+
Pure orchestration.
|
|
128
|
+
Iterates over time and wires everything together without adding logic.
|
|
129
|
+
|
|
130
|
+
## Final Note
|
|
131
|
+
|
|
132
|
+
> I have built this for my personal use in mind.
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
|
finmiti-0.0.1/README.md
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Finmiti
|
|
2
|
+
|
|
3
|
+
**This project is developed for my personal use.** I am developing this to keep the documentation, architecture and pipeline consistent so that I can focus more on developing strategies instead of developing pipelines.
|
|
4
|
+
|
|
5
|
+
Visit [Finmiti](https://dev-ddr.github.io/finmetry/) guide for further steps.
|
|
6
|
+
|
|
7
|
+
> This project is solely developed for my personal use. I am publishing this only to keep myself updated and to remove the headache of setting up the framework again and again.
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
**Finmiti is a research-first quantitative trading framework** designed to keep
|
|
12
|
+
strategy logic, execution logic, and accounting logic strictly separated.
|
|
13
|
+
|
|
14
|
+
It exists to eliminate repeated reinvention of trading pipelines, so you can
|
|
15
|
+
focus on **researching strategies**, not rebuilding infrastructure.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
## What Finmiti Is (and Is Not)
|
|
19
|
+
|
|
20
|
+
Finmiti is:
|
|
21
|
+
|
|
22
|
+
- a framework for systematic trading research
|
|
23
|
+
- equally suited for backtesting and live trading
|
|
24
|
+
- opinionated by design
|
|
25
|
+
- built around explicit, auditable abstractions
|
|
26
|
+
|
|
27
|
+
Finmiti is **not**:
|
|
28
|
+
|
|
29
|
+
- a strategy library
|
|
30
|
+
- a signal generator
|
|
31
|
+
- a black-box trading system
|
|
32
|
+
|
|
33
|
+
If you want flexibility at the cost of correctness, this framework will feel restrictive.
|
|
34
|
+
That restriction is intentional.
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
## The Core Trading Loop
|
|
38
|
+
|
|
39
|
+
Every strategy in finmiti follows the same explicit loop:
|
|
40
|
+
|
|
41
|
+
```text
|
|
42
|
+
Market Data → Strategy → Orders → Portfolio → Execution → Accounting
|
|
43
|
+
````
|
|
44
|
+
|
|
45
|
+
Each stage is implemented as a **separate module** with strict responsibilities.
|
|
46
|
+
|
|
47
|
+
This guarantees that:
|
|
48
|
+
|
|
49
|
+
* strategies remain stateless
|
|
50
|
+
* execution assumptions are explicit
|
|
51
|
+
* accounting is consistent
|
|
52
|
+
* backtests can be trusted
|
|
53
|
+
* live trading reuses the same abstractions
|
|
54
|
+
|
|
55
|
+
## Major Modules
|
|
56
|
+
|
|
57
|
+
Finmiti is organized into the following conceptual modules:
|
|
58
|
+
|
|
59
|
+
### [Client Handling](https://dev-ddr.github.io/finmetry/concepts/client_handling_module/)
|
|
60
|
+
|
|
61
|
+
Handles interaction with external systems such as broker APIs and live data feeds.
|
|
62
|
+
Keeps the rest of the framework broker-agnostic.
|
|
63
|
+
|
|
64
|
+
### [Stocks](https://dev-ddr.github.io/finmetry/concepts/stocks_handling_module/)
|
|
65
|
+
|
|
66
|
+
Manages symbols, historical data, and OHLCV storage.
|
|
67
|
+
Acts as the foundation for all market data access.
|
|
68
|
+
|
|
69
|
+
### [Strategy](https://dev-ddr.github.io/finmetry/concepts/strategy_handling_module/)
|
|
70
|
+
|
|
71
|
+
Consumes immutable market snapshots and emits **order intent only**.
|
|
72
|
+
Strategies never manage cash, positions, or execution details.
|
|
73
|
+
|
|
74
|
+
### [Orders](https://dev-ddr.github.io/finmetry/concepts/order/)
|
|
75
|
+
|
|
76
|
+
Orders are the contract between strategy, portfolio, and execution.
|
|
77
|
+
They represent intent, not outcome.
|
|
78
|
+
|
|
79
|
+
### [Executioners](https://dev-ddr.github.io/finmetry/concepts/executioners/)
|
|
80
|
+
|
|
81
|
+
Simulate (or connect to) market reality:
|
|
82
|
+
slippage, brokerage, partial fills, or live execution.
|
|
83
|
+
|
|
84
|
+
### [Portfolio](https://dev-ddr.github.io/finmetry/concepts/portfolio_handling_module/)
|
|
85
|
+
|
|
86
|
+
The single source of truth for positions, cash, and PnL.
|
|
87
|
+
All state mutation happens here.
|
|
88
|
+
|
|
89
|
+
### [Backtesting](https://dev-ddr.github.io/finmetry/concepts/backtesting_handling_module/)
|
|
90
|
+
|
|
91
|
+
Pure orchestration.
|
|
92
|
+
Iterates over time and wires everything together without adding logic.
|
|
93
|
+
|
|
94
|
+
## Final Note
|
|
95
|
+
|
|
96
|
+
> I have built this for my personal use in mind.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=68", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "finmiti"
|
|
7
|
+
version = "0.0.1"
|
|
8
|
+
description = "Persnal project for stock market data analysis"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.12"
|
|
11
|
+
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Darshan Rathod", email = "darshan.rathod1994@gmail.com" }
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
maintainers = [
|
|
17
|
+
{ name = "Darshan Rathod", email = "darshan.rathod1994@gmail.com" }
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
license = { text = "MIT" }
|
|
21
|
+
|
|
22
|
+
keywords = ["finance", "stock-market", "quant", "research", "personal"]
|
|
23
|
+
|
|
24
|
+
classifiers = [
|
|
25
|
+
"Development Status :: 4 - Beta",
|
|
26
|
+
"Programming Language :: Python :: 3.12",
|
|
27
|
+
"Operating System :: OS Independent",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
dependencies = [
|
|
31
|
+
"numpy==2.3.5",
|
|
32
|
+
"pandas==2.3.3",
|
|
33
|
+
"pyyaml==6.0.3",
|
|
34
|
+
"duckdb==1.4.2",
|
|
35
|
+
"pyarrow==22.0.0",
|
|
36
|
+
"py5paisa==0.7.21.2",
|
|
37
|
+
"pydantic",
|
|
38
|
+
]
|
|
39
|
+
|
|
40
|
+
[project.optional-dependencies]
|
|
41
|
+
# dev = ["pytest", "pytest-cov", "ruff", "jupyter", "notebook"]
|
|
42
|
+
# docs = ["sphinx", "mkdocs", "nbconvert"]
|
|
43
|
+
# viz = ["matplotlib", "plotly"]
|
|
44
|
+
dev = [
|
|
45
|
+
"pytest", "ruff", "jupyter", "notebook",
|
|
46
|
+
"sphinx", "mkdocs", "mkdocs-material", "mkdocstrings[python]", "nbconvert",
|
|
47
|
+
"matplotlib", "plotly"
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
[project.urls]
|
|
51
|
+
Homepage = "https://dev-ddr.github.io/finmiti/"
|
|
52
|
+
Repository = "https://github.com/dev-ddr/finmiti"
|
|
53
|
+
Issues = "https://github.com/dev-ddr/finmiti/issues"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
[tool.setuptools]
|
|
57
|
+
package-dir = {"" = "src"}
|
|
58
|
+
|
|
59
|
+
# This is crucial for telling setuptools where your actual source code is.
|
|
60
|
+
# It tells setuptools to find packages within the 'src' directory.
|
|
61
|
+
[tool.setuptools.packages.find]
|
|
62
|
+
where = ["src"]
|
|
63
|
+
exclude = ["tests*", "legacy*", "experimental*"]
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
[tool.ruff]
|
|
67
|
+
line-length = 310
|
|
68
|
+
select = ["E", "F", "I"] # Errors, pyflakes, imports
|
|
69
|
+
ignore = ["E501"] # Disable long-line check (black handles it)
|
|
70
|
+
src = ["src"]
|
|
71
|
+
|
|
72
|
+
[tool.ruff.format]
|
|
73
|
+
quote-style = "double"
|
|
74
|
+
|
finmiti-0.0.1/setup.cfg
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from importlib.metadata import version
|
|
2
|
+
__version__ = version("finmetry")
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
from . import clients
|
|
6
|
+
from .stocks_handler import Stock, StockDict
|
|
7
|
+
from . import constants
|
|
8
|
+
from .strategy_handler import StrategyBase, StgDataLoader
|
|
9
|
+
from .portfolio_handler import Portfolio, Account
|
|
10
|
+
from .backtest_handler import Backtester
|
|
11
|
+
|
|
12
|
+
from .utils import *
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from ..portfolio_handler import Portfolio
|
|
2
|
+
from ..executioners import ExecutionModel
|
|
3
|
+
from ..strategy_handler import StgDataLoader, StrategyBase
|
|
4
|
+
|
|
5
|
+
class Backtester:
|
|
6
|
+
def __init__(self, data_loader: StgDataLoader, strategy: StrategyBase, portfolio: Portfolio):
|
|
7
|
+
self.data_loader = data_loader
|
|
8
|
+
self.strategy = strategy
|
|
9
|
+
self.portfolio = portfolio
|
|
10
|
+
|
|
11
|
+
def run(self):
|
|
12
|
+
for market_data in self.data_loader:
|
|
13
|
+
entry_orders = self.strategy(market_data)
|
|
14
|
+
exit_orders = self.portfolio.get_exit_orders(market_data)
|
|
15
|
+
### keeping exit_orders first to avoid cash going negative
|
|
16
|
+
all_orders = exit_orders + entry_orders
|
|
17
|
+
for order in all_orders:
|
|
18
|
+
self.portfolio.on_order(order)
|
|
19
|
+
self.portfolio.mark_to_market(market_data)
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
"""
|
|
2
|
+
This module contains the objects related to 5paisa client
|
|
3
|
+
|
|
4
|
+
@author: Rathod Darshan
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import py5paisa as p5
|
|
8
|
+
import pandas as pd
|
|
9
|
+
import datetime as dtm
|
|
10
|
+
from typing import Union, TypedDict
|
|
11
|
+
|
|
12
|
+
from ..stocks_handler import Stock, StockDict
|
|
13
|
+
|
|
14
|
+
from ..constants import INTERVAL
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ScripMaster:
|
|
18
|
+
"""ScripMaster contains all the scipts of 5paisa client.
|
|
19
|
+
|
|
20
|
+
To get the name and symbol of any script, this class needs to be accessed. This class just filters the data from single .csv.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
def __init__(self, filepath: str = None) -> None:
|
|
24
|
+
"""Initializes the ScripMaster class.
|
|
25
|
+
|
|
26
|
+
Loads the .csv file into data attribute.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
filepath : str, optional
|
|
31
|
+
filepath to .csv file. If filepath is given then it reads the file, else it will download the file. by default None
|
|
32
|
+
"""
|
|
33
|
+
if filepath is not None:
|
|
34
|
+
self.data = pd.read_pickle(filepath)
|
|
35
|
+
else:
|
|
36
|
+
self.data = pd.read_csv("https://images.5paisa.com/website/scripmaster-csv-format.csv")
|
|
37
|
+
|
|
38
|
+
self.data["Expiry"] = pd.to_datetime(self.data["Expiry"], format="%Y-%m-%d %H:%M:%S")
|
|
39
|
+
self.data["Name"] = self.data["Name"].apply(str.upper)
|
|
40
|
+
self.data["Symbol"] = self.data["Name"]
|
|
41
|
+
|
|
42
|
+
def __repr__(self):
|
|
43
|
+
return f"scrip master data"
|
|
44
|
+
|
|
45
|
+
def __call__(self):
|
|
46
|
+
return self.data
|
|
47
|
+
|
|
48
|
+
def save(self, filepath: str) -> None:
|
|
49
|
+
"""saves the scrip master data
|
|
50
|
+
|
|
51
|
+
Parameters
|
|
52
|
+
----------
|
|
53
|
+
filepath : str
|
|
54
|
+
filepath with filename with .pkl extention
|
|
55
|
+
|
|
56
|
+
Returns
|
|
57
|
+
-------
|
|
58
|
+
_type_
|
|
59
|
+
None
|
|
60
|
+
"""
|
|
61
|
+
return self.data.to_pickle(filepath)
|
|
62
|
+
|
|
63
|
+
def get_scrip(self, stock: Stock) -> pd.DataFrame:
|
|
64
|
+
"""returns the scrips of the stock
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
stock : Stock
|
|
69
|
+
a stock object
|
|
70
|
+
|
|
71
|
+
Returns
|
|
72
|
+
-------
|
|
73
|
+
pd.DataFrame
|
|
74
|
+
Scrip data of a given stock
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
return stock.scrip
|
|
78
|
+
except:
|
|
79
|
+
pass
|
|
80
|
+
d1 = self.data
|
|
81
|
+
f1 = (d1["Exch"] == stock.exchange) & (d1["ExchType"] == stock.exchange_type) & (d1["Symbol"] == stock.symbol)
|
|
82
|
+
f2 = (d1["Series"] == "EQ") | (d1["Series"] == "XX")
|
|
83
|
+
d2 = d1[f1 & f2]
|
|
84
|
+
if d2.empty:
|
|
85
|
+
raise ValueError(f"No Scrip found for {stock.symbol} in scrip_master")
|
|
86
|
+
d2 = d2.set_index("Name")
|
|
87
|
+
### setting the scrip to stock object
|
|
88
|
+
stock.scrip = d2
|
|
89
|
+
return d2
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class Client5paisaCred(TypedDict):
|
|
93
|
+
APP_NAME: str
|
|
94
|
+
APP_SOURCE: str
|
|
95
|
+
USER_ID: str
|
|
96
|
+
PASSWORD: str
|
|
97
|
+
USER_KEY: str
|
|
98
|
+
ENCRYPTION_KEY: str
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Client5paisa(p5.FivePaisaClient):
|
|
102
|
+
def __init__(self, totp: str, mpin: str, client_code: str, cred: Client5paisaCred, scrip_master: ScripMaster = None, **kwargs):
|
|
103
|
+
super().__init__(cred=cred)
|
|
104
|
+
self.get_totp_session(client_code, f"{totp}", mpin)
|
|
105
|
+
|
|
106
|
+
print("downloading the scrip-master")
|
|
107
|
+
|
|
108
|
+
self.scrip_master = ScripMaster() if scrip_master is None else scrip_master
|
|
109
|
+
return
|
|
110
|
+
|
|
111
|
+
def download_historical_data(
|
|
112
|
+
self,
|
|
113
|
+
stock: Stock,
|
|
114
|
+
interval: INTERVAL = INTERVAL.one_day,
|
|
115
|
+
start: Union[str, dtm.datetime] = "2023-01-01",
|
|
116
|
+
end: Union[str, dtm.datetime] = "2023-03-30",
|
|
117
|
+
) -> pd.DataFrame:
|
|
118
|
+
"""Downloads the historical data and saves it to local drive or in Stock.data variable.
|
|
119
|
+
|
|
120
|
+
Parameters
|
|
121
|
+
----------
|
|
122
|
+
stock : Stock
|
|
123
|
+
Stock object
|
|
124
|
+
interval : str, optional
|
|
125
|
+
time interval of data. it should be within [1m,5m,10m,15m,30m,60m,1d], by default "1d"
|
|
126
|
+
start : Union[str, dtm.datetime], optional
|
|
127
|
+
start date of the data. The data for this date will be downloaded, by default "2023-01-01"
|
|
128
|
+
end : Union[str, dtm.datetime], optional
|
|
129
|
+
end date of the data. The data for this date will be downloaded, by default "2023-03-30"
|
|
130
|
+
|
|
131
|
+
Returns
|
|
132
|
+
----------
|
|
133
|
+
pd.DataFrame
|
|
134
|
+
A dataframe containing a historical data.
|
|
135
|
+
|
|
136
|
+
"""
|
|
137
|
+
# scrip = self.scrip_master.get_scrip(stock)
|
|
138
|
+
|
|
139
|
+
# if isinstance(start, dtm.datetime):
|
|
140
|
+
# start = start.strftime("%Y-%m-%d")
|
|
141
|
+
# if isinstance(end, dtm.datetime):
|
|
142
|
+
# end = end.strftime("%Y-%m-%d")
|
|
143
|
+
|
|
144
|
+
# df = self.historical_data(stock.exchange, stock.exchange_type, scrip.loc[stock.symbol, "Scripcode"], interval.value, start, end)
|
|
145
|
+
# df.columns = ["Datetime", "Open", "High", "Low", "Close", "Volume"]
|
|
146
|
+
# df["Datetime"] = pd.to_datetime(df["Datetime"])
|
|
147
|
+
# df = df.set_index("Datetime")
|
|
148
|
+
|
|
149
|
+
# return df
|
|
150
|
+
|
|
151
|
+
scrip = self.scrip_master.get_scrip(stock)
|
|
152
|
+
if isinstance(start, str):
|
|
153
|
+
start_dt = dtm.datetime.strptime(start, "%Y-%m-%d")
|
|
154
|
+
else:
|
|
155
|
+
start_dt = start
|
|
156
|
+
|
|
157
|
+
if isinstance(end, str):
|
|
158
|
+
end_dt = dtm.datetime.strptime(end, "%Y-%m-%d")
|
|
159
|
+
else:
|
|
160
|
+
end_dt = end
|
|
161
|
+
|
|
162
|
+
def _fetch_chunk(s: dtm.datetime, e: dtm.datetime) -> pd.DataFrame:
|
|
163
|
+
df = self.historical_data(stock.exchange, stock.exchange_type, scrip.loc[stock.symbol, "Scripcode"], interval.value, s.strftime("%Y-%m-%d"), e.strftime("%Y-%m-%d"))
|
|
164
|
+
if df is None or df.empty:
|
|
165
|
+
return pd.DataFrame()
|
|
166
|
+
df.columns = ["Datetime", "Open", "High", "Low", "Close", "Volume"]
|
|
167
|
+
df["Datetime"] = pd.to_datetime(df["Datetime"])
|
|
168
|
+
return df.set_index("Datetime")
|
|
169
|
+
|
|
170
|
+
dfs = []
|
|
171
|
+
curr_start = start_dt
|
|
172
|
+
|
|
173
|
+
while curr_start < end_dt:
|
|
174
|
+
curr_end = min(curr_start + dtm.timedelta(days=90), end_dt)
|
|
175
|
+
chunk = _fetch_chunk(curr_start, curr_end)
|
|
176
|
+
if not chunk.empty:
|
|
177
|
+
dfs.append(chunk)
|
|
178
|
+
|
|
179
|
+
curr_start = curr_end
|
|
180
|
+
|
|
181
|
+
if not dfs:
|
|
182
|
+
return pd.DataFrame()
|
|
183
|
+
|
|
184
|
+
df = pd.concat(dfs).sort_index()
|
|
185
|
+
df = df.loc[~df.index.duplicated(keep="first")]
|
|
186
|
+
|
|
187
|
+
return df
|
|
188
|
+
|
|
189
|
+
def get_market_depth(self, stockdict: Union[list[Stock], StockDict]) -> pd.DataFrame:
|
|
190
|
+
"""Gets the market depth for given list of Stocks
|
|
191
|
+
|
|
192
|
+
Parameters
|
|
193
|
+
----------
|
|
194
|
+
StockDict : list[Stock]|StockDict
|
|
195
|
+
list of Stock class instances or StockDict type object.
|
|
196
|
+
|
|
197
|
+
Returns
|
|
198
|
+
-------
|
|
199
|
+
_pd.DataFrame
|
|
200
|
+
Live Market Depth for all the Stocks in list.
|
|
201
|
+
"""
|
|
202
|
+
scrips = pd.concat(self.scrip_master.get_scrip(stock) for stock in stockdict)
|
|
203
|
+
a = scrips.rename(columns={"Exch": "Exchange", "ExchType": "ExchangeType"})[["Exchange", "ExchangeType", "Symbol"]].to_dict(orient="records")
|
|
204
|
+
d1 = pd.DataFrame(self.fetch_market_depth_by_symbol(a)["Data"])
|
|
205
|
+
|
|
206
|
+
d1.set_index("ScripCode", inplace=True)
|
|
207
|
+
d1["Datetime"] = dtm.datetime.now()
|
|
208
|
+
scrips.set_index("Scripcode", inplace=True)
|
|
209
|
+
d1["Symbol"] = scrips["Symbol"]
|
|
210
|
+
d1.set_index("Symbol", inplace=True)
|
|
211
|
+
d1.rename(columns={"Close": "PrevClose"}, inplace=True)
|
|
212
|
+
d1.rename(columns={"LastTradedPrice": "Close"}, inplace=True)
|
|
213
|
+
return d1
|
|
214
|
+
|
|
215
|
+
def update_stock_histdata0_to_ltp(self, stockdict: Union[list[Stock], StockDict]) -> None:
|
|
216
|
+
"""Updates the market depth to stock.hist_data0 attribute
|
|
217
|
+
|
|
218
|
+
Parameters
|
|
219
|
+
----------
|
|
220
|
+
StockDict : list[Stock]|StockDict
|
|
221
|
+
list of Stock class instances or StockDict type object.
|
|
222
|
+
|
|
223
|
+
Returns
|
|
224
|
+
-------
|
|
225
|
+
None
|
|
226
|
+
"""
|
|
227
|
+
d1 = self.get_market_depth(stockdict)
|
|
228
|
+
d1 = d1[["Datetime", "Open", "High", "Low", "Close", "Volume"]]
|
|
229
|
+
d1["Datetime"] = d1["Datetime"].dt.normalize()
|
|
230
|
+
### Convert datatypes of specific columns
|
|
231
|
+
float_cols = ["Open", "High", "Low", "Close"]
|
|
232
|
+
d1[float_cols] = d1[float_cols].astype(float)
|
|
233
|
+
d1["Volume"] = d1["Volume"].astype(int)
|
|
234
|
+
|
|
235
|
+
for s1 in stockdict:
|
|
236
|
+
try:
|
|
237
|
+
d = d1.loc[s1.symbol]
|
|
238
|
+
s1.hist_data0.loc[d.Datetime] = d
|
|
239
|
+
except:
|
|
240
|
+
print(f"Error in {s1.symbol}")
|
|
241
|
+
continue
|
|
242
|
+
return
|