tradekartapi 0.1.0__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.
- tradekartapi-0.1.0/PKG-INFO +46 -0
- tradekartapi-0.1.0/README.md +29 -0
- tradekartapi-0.1.0/pyproject.toml +44 -0
- tradekartapi-0.1.0/setup.cfg +4 -0
- tradekartapi-0.1.0/src/tradekartapi/__init__.py +6 -0
- tradekartapi-0.1.0/src/tradekartapi/adapter/__init__.py +5 -0
- tradekartapi-0.1.0/src/tradekartapi/adapter/groww_sdk_adapter.py +24 -0
- tradekartapi-0.1.0/src/tradekartapi/client.py +29 -0
- tradekartapi-0.1.0/src/tradekartapi/config/__init__.py +6 -0
- tradekartapi-0.1.0/src/tradekartapi/config/configuration.py +4 -0
- tradekartapi-0.1.0/src/tradekartapi/core/__init__.py +5 -0
- tradekartapi-0.1.0/src/tradekartapi/core/auth.py +12 -0
- tradekartapi-0.1.0/src/tradekartapi/core/http_client.py +0 -0
- tradekartapi-0.1.0/src/tradekartapi/core/interfaces.py +0 -0
- tradekartapi-0.1.0/src/tradekartapi/exceptions/__init__.py +19 -0
- tradekartapi-0.1.0/src/tradekartapi/exceptions/custom_exceptions.py +90 -0
- tradekartapi-0.1.0/src/tradekartapi/models.py +0 -0
- tradekartapi-0.1.0/src/tradekartapi/services/__init__.py +7 -0
- tradekartapi-0.1.0/src/tradekartapi/services/historical.py +0 -0
- tradekartapi-0.1.0/src/tradekartapi/services/market_data.py +0 -0
- tradekartapi-0.1.0/src/tradekartapi/services/orders.py +75 -0
- tradekartapi-0.1.0/src/tradekartapi/services/portfolio.py +1 -0
- tradekartapi-0.1.0/src/tradekartapi/types/__init__.py +19 -0
- tradekartapi-0.1.0/src/tradekartapi/types/annexures.py +31 -0
- tradekartapi-0.1.0/src/tradekartapi/types/order_types.py +160 -0
- tradekartapi-0.1.0/src/tradekartapi/types/portfolio_types.py +1 -0
- tradekartapi-0.1.0/src/tradekartapi/utils/__init__.py +7 -0
- tradekartapi-0.1.0/src/tradekartapi/utils/validators.py +13 -0
- tradekartapi-0.1.0/src/tradekartapi/version.py +4 -0
- tradekartapi-0.1.0/src/tradekartapi.egg-info/PKG-INFO +46 -0
- tradekartapi-0.1.0/src/tradekartapi.egg-info/SOURCES.txt +32 -0
- tradekartapi-0.1.0/src/tradekartapi.egg-info/dependency_links.txt +1 -0
- tradekartapi-0.1.0/src/tradekartapi.egg-info/requires.txt +10 -0
- tradekartapi-0.1.0/src/tradekartapi.egg-info/top_level.txt +1 -0
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tradekartapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tradekart wrapper SDK for Groww trading APIs
|
|
5
|
+
Author-email: Vikalp <tradekart@gmail.com>, Siddhant <tradekart@gmail.com>, Lakshay <tradekart@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: growwapi
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=8.3.2; extra == "dev"
|
|
12
|
+
Requires-Dist: ruff>=0.12.1; extra == "dev"
|
|
13
|
+
Requires-Dist: build>=1.2.2; extra == "dev"
|
|
14
|
+
Requires-Dist: twine>=5.1.1; extra == "dev"
|
|
15
|
+
Provides-Extra: aws
|
|
16
|
+
Requires-Dist: boto3>=1.34.0; extra == "aws"
|
|
17
|
+
|
|
18
|
+
# README #
|
|
19
|
+
|
|
20
|
+
This README would normally document whatever steps are necessary to get your application up and running.
|
|
21
|
+
|
|
22
|
+
### What is this repository for? ###
|
|
23
|
+
|
|
24
|
+
* Quick summary
|
|
25
|
+
* Version
|
|
26
|
+
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
|
27
|
+
|
|
28
|
+
### How do I get set up? ###
|
|
29
|
+
|
|
30
|
+
* Summary of set up
|
|
31
|
+
* Configuration
|
|
32
|
+
* Dependencies
|
|
33
|
+
* Database configuration
|
|
34
|
+
* How to run tests
|
|
35
|
+
* Deployment instructions
|
|
36
|
+
|
|
37
|
+
### Contribution guidelines ###
|
|
38
|
+
|
|
39
|
+
* Writing tests
|
|
40
|
+
* Code review
|
|
41
|
+
* Other guidelines
|
|
42
|
+
|
|
43
|
+
### Who do I talk to? ###
|
|
44
|
+
|
|
45
|
+
* Repo owner or admin
|
|
46
|
+
* Other community or team contact
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# README #
|
|
2
|
+
|
|
3
|
+
This README would normally document whatever steps are necessary to get your application up and running.
|
|
4
|
+
|
|
5
|
+
### What is this repository for? ###
|
|
6
|
+
|
|
7
|
+
* Quick summary
|
|
8
|
+
* Version
|
|
9
|
+
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
|
10
|
+
|
|
11
|
+
### How do I get set up? ###
|
|
12
|
+
|
|
13
|
+
* Summary of set up
|
|
14
|
+
* Configuration
|
|
15
|
+
* Dependencies
|
|
16
|
+
* Database configuration
|
|
17
|
+
* How to run tests
|
|
18
|
+
* Deployment instructions
|
|
19
|
+
|
|
20
|
+
### Contribution guidelines ###
|
|
21
|
+
|
|
22
|
+
* Writing tests
|
|
23
|
+
* Code review
|
|
24
|
+
* Other guidelines
|
|
25
|
+
|
|
26
|
+
### Who do I talk to? ###
|
|
27
|
+
|
|
28
|
+
* Repo owner or admin
|
|
29
|
+
* Other community or team contact
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=69", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tradekartapi"
|
|
7
|
+
dynamic = ["version"]
|
|
8
|
+
description = "Tradekart wrapper SDK for Groww trading APIs"
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
requires-python = ">=3.10"
|
|
11
|
+
license = { text = "MIT" }
|
|
12
|
+
authors = [
|
|
13
|
+
{ name = "Vikalp", email = "tradekart@gmail.com" },
|
|
14
|
+
{ name = "Siddhant", email = "tradekart@gmail.com" },
|
|
15
|
+
{ name = "Lakshay", email = "tradekart@gmail.com" }
|
|
16
|
+
]
|
|
17
|
+
dependencies = [
|
|
18
|
+
"growwapi",
|
|
19
|
+
]
|
|
20
|
+
|
|
21
|
+
[project.optional-dependencies]
|
|
22
|
+
dev = [
|
|
23
|
+
"pytest>=8.3.2",
|
|
24
|
+
"ruff>=0.12.1",
|
|
25
|
+
"build>=1.2.2",
|
|
26
|
+
"twine>=5.1.1",
|
|
27
|
+
]
|
|
28
|
+
aws = [
|
|
29
|
+
"boto3>=1.34.0",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[tool.setuptools]
|
|
33
|
+
package-dir = { "" = "src" }
|
|
34
|
+
include-package-data = true
|
|
35
|
+
|
|
36
|
+
[tool.setuptools.packages.find]
|
|
37
|
+
where = ["src"]
|
|
38
|
+
include = ["tradekartapi*"]
|
|
39
|
+
|
|
40
|
+
[tool.setuptools.dynamic]
|
|
41
|
+
version = { attr = "tradekartapi.version.__version__" }
|
|
42
|
+
|
|
43
|
+
[tool.pytest.ini_options]
|
|
44
|
+
testpaths = ["tests"]
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from ..utils import validate_access_token
|
|
4
|
+
|
|
5
|
+
class GrowwSDKAdapter:
|
|
6
|
+
"""
|
|
7
|
+
Thin adapter over official GrowwAPI SDK object.
|
|
8
|
+
This keeps our services decoupled from direct SDK construction.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
def __init__(self, sdk_client: Any) -> None:
|
|
12
|
+
self._sdk_client = sdk_client
|
|
13
|
+
|
|
14
|
+
@classmethod
|
|
15
|
+
def from_access_token(cls, access_token: str, groww_sdk_client: Any) -> 'GrowwSDKAdapter':
|
|
16
|
+
#validate access token
|
|
17
|
+
validate_access_token(access_token)
|
|
18
|
+
#initialize sdk client
|
|
19
|
+
sdk_client = groww_sdk_client(access_token)
|
|
20
|
+
return cls(sdk_client=sdk_client)
|
|
21
|
+
|
|
22
|
+
@property
|
|
23
|
+
def client(self) -> Any:
|
|
24
|
+
return self._sdk_client
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from .adapter import GrowwSDKAdapter
|
|
4
|
+
from .services import OrderService
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class TradeKartClient:
|
|
8
|
+
"""
|
|
9
|
+
Main facade for app developers.
|
|
10
|
+
|
|
11
|
+
Example:
|
|
12
|
+
from growwapi import GrowwAPI
|
|
13
|
+
from tradekartapi.client import TradeKartClient
|
|
14
|
+
client = TradeKartClient.from_access_token("token", GrowwAPI)
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def __init__(self, adapter: GrowwSDKAdapter):
|
|
18
|
+
self._adapter = adapter
|
|
19
|
+
self.order_service = OrderService(adapter)
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_access_token(cls, access_token: str, groww_sdk_client: Any) -> 'GrowwClient':
|
|
23
|
+
adapter = GrowwSDKAdapter.from_access_token(access_token, groww_sdk_client)
|
|
24
|
+
return cls(adapter=adapter)
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def raw_sdk(self) -> Any:
|
|
28
|
+
return self._adapter.client
|
|
29
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
from ..exceptions import AuthenticationError
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class AccessTokenAuth:
|
|
5
|
+
"""Authentication using access token."""
|
|
6
|
+
|
|
7
|
+
@staticmethod
|
|
8
|
+
def validate(access_token: str) -> str:
|
|
9
|
+
token = access_token.strip() if access_token else ""
|
|
10
|
+
if not token:
|
|
11
|
+
raise AuthenticationError("Access token is required")
|
|
12
|
+
return token
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .custom_exceptions import (
|
|
2
|
+
GrowwApiWrapperError,
|
|
3
|
+
AuthenticationError,
|
|
4
|
+
ValidationError,
|
|
5
|
+
ServiceError,
|
|
6
|
+
HttpError,
|
|
7
|
+
RateLimitError,
|
|
8
|
+
NotFoundError
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'GrowwApiWrapperError',
|
|
13
|
+
'AuthenticationError',
|
|
14
|
+
'ValidationError',
|
|
15
|
+
'ServiceError',
|
|
16
|
+
'HttpError',
|
|
17
|
+
'RateLimitError',
|
|
18
|
+
'NotFoundError'
|
|
19
|
+
]
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class GrowwApiWrapperError(Exception):
|
|
5
|
+
"""Base exception for the Tradekart Groww wrapper.
|
|
6
|
+
All package-specific exceptions should inherit from this class so app code
|
|
7
|
+
can catch one common parent when needed.
|
|
8
|
+
"""
|
|
9
|
+
def __init__(
|
|
10
|
+
self,
|
|
11
|
+
message: str,
|
|
12
|
+
*,
|
|
13
|
+
status_code : int | None = None,
|
|
14
|
+
details: dict[str, Any] | None = None,
|
|
15
|
+
) -> None:
|
|
16
|
+
super().__init__(message)
|
|
17
|
+
self.message = message
|
|
18
|
+
self.status_code = status_code
|
|
19
|
+
self.details = details
|
|
20
|
+
|
|
21
|
+
def __str__(self) -> str:
|
|
22
|
+
parts = [self.message]
|
|
23
|
+
|
|
24
|
+
if self.status_code is not None:
|
|
25
|
+
parts.append(f'status_code={self.status_code}')
|
|
26
|
+
|
|
27
|
+
if self.details is not None:
|
|
28
|
+
parts.append(f'details={self.details}')
|
|
29
|
+
|
|
30
|
+
return '|'.join(parts)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class AuthenticationError(GrowwApiWrapperError):
|
|
34
|
+
"""Raised when access token is invalid or missing."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ValidationError(GrowwApiWrapperError):
|
|
38
|
+
"""Raised when input validation fails."""
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class ServiceError(GrowwApiWrapperError):
|
|
42
|
+
"""Raised when an SDK service operation fails."""
|
|
43
|
+
|
|
44
|
+
def __init__(
|
|
45
|
+
self,
|
|
46
|
+
message : str,
|
|
47
|
+
*,
|
|
48
|
+
status_code: int | None = None,
|
|
49
|
+
operation: str | None = None,
|
|
50
|
+
details : dict[str, Any] | None = None
|
|
51
|
+
) -> None:
|
|
52
|
+
merged_details = dict(details or {})
|
|
53
|
+
if operation is not None:
|
|
54
|
+
merged_details['operation'] = operation
|
|
55
|
+
|
|
56
|
+
super().__init__(
|
|
57
|
+
message,
|
|
58
|
+
status_code=status_code,
|
|
59
|
+
details=merged_details
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class HttpError(GrowwApiWrapperError):
|
|
64
|
+
"""Raised when an HTTP/API layer request fails."""
|
|
65
|
+
|
|
66
|
+
def __init__(
|
|
67
|
+
self,
|
|
68
|
+
message: str,
|
|
69
|
+
*,
|
|
70
|
+
status_code: int | None = None,
|
|
71
|
+
details: dict[str, Any] | None = None,
|
|
72
|
+
response_body: Any | None = None,
|
|
73
|
+
) -> None:
|
|
74
|
+
merged_details = dict(details or {})
|
|
75
|
+
|
|
76
|
+
if response_body is not None:
|
|
77
|
+
merged_details['response_body'] = response_body
|
|
78
|
+
|
|
79
|
+
super().__init__(
|
|
80
|
+
message=message,
|
|
81
|
+
status_code=status_code,
|
|
82
|
+
details=merged_details,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
class RateLimitError(HttpError):
|
|
86
|
+
"""Raised when API rate limit is hit."""
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class NotFoundError(HttpError):
|
|
90
|
+
"""Raised when requested resource is not found."""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from ..types import CancelOrderRequest, GetOrderList, ModifyOrderRequest, OrderDetail, OrderStatus, PlaceOrderRequest
|
|
4
|
+
from ..exceptions import ServiceError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class OrderService:
|
|
8
|
+
def __init__(self, sdk : Any) -> None:
|
|
9
|
+
self._sdk = sdk.client
|
|
10
|
+
|
|
11
|
+
def place_order(self, request: PlaceOrderRequest) -> dict[str, Any]:
|
|
12
|
+
try:
|
|
13
|
+
return self._sdk.place_order(**request.to_payload())
|
|
14
|
+
except Exception as exc:
|
|
15
|
+
raise ServiceError(
|
|
16
|
+
'Error in placing order',
|
|
17
|
+
status_code=500,
|
|
18
|
+
operation='PLACING_ORDER',
|
|
19
|
+
details={'reason': 'Error in placing method'},
|
|
20
|
+
) from exc
|
|
21
|
+
|
|
22
|
+
def modify_order(self, request: ModifyOrderRequest) -> dict[str, Any]:
|
|
23
|
+
try:
|
|
24
|
+
return self._sdk.modify_order(**request.to_payload())
|
|
25
|
+
except Exception as exc:
|
|
26
|
+
raise ServiceError(
|
|
27
|
+
'Error in modifying order',
|
|
28
|
+
status_code=500,
|
|
29
|
+
operation='MODIFYING_ORDER',
|
|
30
|
+
details={'reason': 'Error in modify method'},
|
|
31
|
+
) from exc
|
|
32
|
+
|
|
33
|
+
def cancel_order(self, request: CancelOrderRequest) -> dict[str, Any]:
|
|
34
|
+
try:
|
|
35
|
+
return self._sdk.cancel_order(**request.to_payload())
|
|
36
|
+
except Exception as exc:
|
|
37
|
+
raise ServiceError(
|
|
38
|
+
'Error in canceling order',
|
|
39
|
+
status_code=500,
|
|
40
|
+
operation='CANCELING_ORDER',
|
|
41
|
+
details={'reason': 'Error in cancel method'},
|
|
42
|
+
) from exc
|
|
43
|
+
|
|
44
|
+
def get_order_status(self, request: OrderStatus) -> dict[str, Any]:
|
|
45
|
+
try:
|
|
46
|
+
return self._sdk.get_order_status(**request.to_payload())
|
|
47
|
+
except Exception as exc:
|
|
48
|
+
raise ServiceError(
|
|
49
|
+
'Error in getting order status',
|
|
50
|
+
status_code=500,
|
|
51
|
+
operation='GET_ORDER_STATUS',
|
|
52
|
+
details={'reason': 'Error in get order status method'},
|
|
53
|
+
) from exc
|
|
54
|
+
|
|
55
|
+
def get_order_list(self, request: GetOrderList) -> dict[str, Any]:
|
|
56
|
+
try:
|
|
57
|
+
return self._sdk.get_order_list(**request.to_payload())
|
|
58
|
+
except Exception as exc:
|
|
59
|
+
raise ServiceError(
|
|
60
|
+
'Error in getting order list',
|
|
61
|
+
status_code=500,
|
|
62
|
+
operation='GET_ORDER_LIST',
|
|
63
|
+
details={'reason': 'Error in get order list method'},
|
|
64
|
+
) from exc
|
|
65
|
+
|
|
66
|
+
def get_order_detail(self, request: OrderDetail) -> dict[str, Any]:
|
|
67
|
+
try:
|
|
68
|
+
return self._sdk.get_order_detail(**request.to_payload())
|
|
69
|
+
except Exception as exc:
|
|
70
|
+
raise ServiceError(
|
|
71
|
+
'Error in getting order detail',
|
|
72
|
+
status_code=500,
|
|
73
|
+
operation='GET_ORDER_DETAIL',
|
|
74
|
+
details={'reason': 'Error in get order detail method'},
|
|
75
|
+
) from exc
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#Todo Add Portfolio Services
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from .order_types import (
|
|
2
|
+
PlaceOrderRequest,
|
|
3
|
+
ModifyOrderRequest,
|
|
4
|
+
CancelOrderRequest,
|
|
5
|
+
OrderStatus,
|
|
6
|
+
OrderStatsByRefId,
|
|
7
|
+
GetOrderList,
|
|
8
|
+
OrderDetail,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
'PlaceOrderRequest',
|
|
13
|
+
'ModifyOrderRequest',
|
|
14
|
+
'CancelOrderRequest',
|
|
15
|
+
'OrderStatus',
|
|
16
|
+
'OrderStatsByRefId',
|
|
17
|
+
'GetOrderList',
|
|
18
|
+
'OrderDetail'
|
|
19
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
class Validity(str, Enum):
|
|
6
|
+
DAY = 'DAY'
|
|
7
|
+
|
|
8
|
+
class Exchange(str, Enum):
|
|
9
|
+
NSE = 'NSE'
|
|
10
|
+
BSE = 'BSE'
|
|
11
|
+
MCX = 'MCX'
|
|
12
|
+
|
|
13
|
+
class Segment(str, Enum):
|
|
14
|
+
CASH = 'CASH'
|
|
15
|
+
FNO = 'FNO'
|
|
16
|
+
COMMODITY = 'COMMODITY'
|
|
17
|
+
|
|
18
|
+
class Product(str, Enum):
|
|
19
|
+
CNC = 'CNC'
|
|
20
|
+
MIS = 'MIS'
|
|
21
|
+
NRML = 'NRML'
|
|
22
|
+
|
|
23
|
+
class OrderType(str, Enum):
|
|
24
|
+
MARKET = 'MARKET'
|
|
25
|
+
LIMIT = 'LIMIT'
|
|
26
|
+
STOP_LOSS = 'SL'
|
|
27
|
+
STOP_LIMIT = 'SL_M'
|
|
28
|
+
|
|
29
|
+
class TransactionType(str, Enum):
|
|
30
|
+
BUY = 'BUY'
|
|
31
|
+
SELL = 'SELL'
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass
|
|
4
|
+
from typing import Any
|
|
5
|
+
from ..utils.validators import require_non_empty, require_positive_int
|
|
6
|
+
from .annexures import Exchange, Segment, Product, OrderType, TransactionType, Validity
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass(frozen=True, slots=True)
|
|
10
|
+
class PlaceOrderRequest:
|
|
11
|
+
trading_symbol: str
|
|
12
|
+
quantity : int
|
|
13
|
+
validity : Validity
|
|
14
|
+
exchange: Exchange
|
|
15
|
+
segment : Segment
|
|
16
|
+
product : Product
|
|
17
|
+
order_type : OrderType
|
|
18
|
+
transaction_type : TransactionType
|
|
19
|
+
price: float | None = None
|
|
20
|
+
trigger_price: float | None = None
|
|
21
|
+
order_reference_id: str | None = None
|
|
22
|
+
|
|
23
|
+
def __post_init__(self) -> None:
|
|
24
|
+
require_non_empty(self.trading_symbol, 'trading_symbol')
|
|
25
|
+
require_positive_int(self.quantity, 'quantity')
|
|
26
|
+
#Todo Add Valodation for price and trigger_price
|
|
27
|
+
|
|
28
|
+
def to_payload(self) -> dict[str, Any]:
|
|
29
|
+
payload = {
|
|
30
|
+
'trading_symbol': self.trading_symbol,
|
|
31
|
+
'quantity': self.quantity,
|
|
32
|
+
'validity': self.validity.value,
|
|
33
|
+
'exchange': self.exchange.value,
|
|
34
|
+
'segment': self.segment.value,
|
|
35
|
+
'product': self.product.value,
|
|
36
|
+
'order_type': self.order_type.value,
|
|
37
|
+
'transaction_type': self.transaction_type.value,
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if self.price is not None:
|
|
41
|
+
payload['price'] = self.price
|
|
42
|
+
if self.trigger_price is not None:
|
|
43
|
+
payload['trigger_price'] = self.trigger_price
|
|
44
|
+
if self.order_reference_id is not None:
|
|
45
|
+
payload['order_reference_id'] = self.order_reference_id
|
|
46
|
+
return payload
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@dataclass(frozen=True, slots=True)
|
|
50
|
+
class ModifyOrderRequest:
|
|
51
|
+
quantity : int
|
|
52
|
+
order_type : OrderType
|
|
53
|
+
segment : Segment
|
|
54
|
+
groww_order_id : str
|
|
55
|
+
price : float | None = None
|
|
56
|
+
trigger_price: float | None = None
|
|
57
|
+
|
|
58
|
+
def __post_init__(self) -> None:
|
|
59
|
+
require_positive_int(self.quantity, 'quantity')
|
|
60
|
+
require_non_empty(self.groww_order_id, 'groww_order_id')
|
|
61
|
+
#Todo Add Valodation for price and trigger_price
|
|
62
|
+
|
|
63
|
+
def to_payload(self) -> dict[str, Any]:
|
|
64
|
+
payload = {
|
|
65
|
+
'quantity': self.quantity,
|
|
66
|
+
'order_type': self.order_type.value,
|
|
67
|
+
'segment': self.segment.value,
|
|
68
|
+
'groww_order_id': self.groww_order_id
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if self.price is not None:
|
|
72
|
+
payload['price'] = self.price
|
|
73
|
+
|
|
74
|
+
if self.trigger_price is not None:
|
|
75
|
+
payload['trigger_price'] = self.trigger_price
|
|
76
|
+
|
|
77
|
+
return payload
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@dataclass(frozen=True, slots=True)
|
|
81
|
+
class CancelOrderRequest:
|
|
82
|
+
segment : Segment
|
|
83
|
+
groww_order_id : str
|
|
84
|
+
|
|
85
|
+
def __post_init__(self) -> None:
|
|
86
|
+
require_non_empty(self.groww_order_id, 'groww_order_id')
|
|
87
|
+
|
|
88
|
+
def to_payload(self) -> dict[str, Any]:
|
|
89
|
+
return {
|
|
90
|
+
'segment': self.segment.value,
|
|
91
|
+
'groww_order_id': self.groww_order_id
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@dataclass(frozen=True, slots=True)
|
|
96
|
+
class OrderStatus:
|
|
97
|
+
segment : Segment
|
|
98
|
+
groww_order_id : str
|
|
99
|
+
|
|
100
|
+
def __post_init__(self) -> None:
|
|
101
|
+
require_non_empty(self.groww_order_id, 'groww_order_id')
|
|
102
|
+
|
|
103
|
+
def to_payload(self) -> dict[str, Any]:
|
|
104
|
+
return {
|
|
105
|
+
'segment': self.segment.value,
|
|
106
|
+
'groww_order_id': self.groww_order_id
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
@dataclass(frozen=True, slots=True)
|
|
111
|
+
class OrderStatsByRefId:
|
|
112
|
+
order_reference_id : str
|
|
113
|
+
segment : Segment
|
|
114
|
+
|
|
115
|
+
def __post_init__(self) -> None:
|
|
116
|
+
require_non_empty(self.order_reference_id, 'order_reference_id')
|
|
117
|
+
|
|
118
|
+
def to_payload(self) -> dict[str, Any]:
|
|
119
|
+
return {
|
|
120
|
+
'order_reference_id': self.order_reference_id,
|
|
121
|
+
'segment': self.segment.value
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
@dataclass(frozen=True, slots=True)
|
|
126
|
+
class GetOrderList:
|
|
127
|
+
segment : Segment | None = None
|
|
128
|
+
page : int | None = None
|
|
129
|
+
page_size : int | None = None
|
|
130
|
+
|
|
131
|
+
def __post_init__(self) -> None:
|
|
132
|
+
if self.page is not None:
|
|
133
|
+
require_positive_int(self.page, 'page')
|
|
134
|
+
if self.page_size is not None:
|
|
135
|
+
require_positive_int(self.page_size, 'page_size')
|
|
136
|
+
|
|
137
|
+
def to_payload(self) -> dict[str, Any]:
|
|
138
|
+
payload = {}
|
|
139
|
+
if self.segment is not None:
|
|
140
|
+
payload['segment'] = self.segment.value
|
|
141
|
+
if self.page is not None:
|
|
142
|
+
payload['page'] = self.page
|
|
143
|
+
if self.page_size is not None:
|
|
144
|
+
payload['page_size'] = self.page_size
|
|
145
|
+
return payload
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
@dataclass(frozen=True, slots=True)
|
|
149
|
+
class OrderDetail:
|
|
150
|
+
segment : Segment
|
|
151
|
+
groww_order_id : str
|
|
152
|
+
|
|
153
|
+
def __post_init__(self) -> None:
|
|
154
|
+
require_non_empty(self.groww_order_id, 'groww_order_id')
|
|
155
|
+
|
|
156
|
+
def to_payload(self) -> dict[str, Any]:
|
|
157
|
+
return {
|
|
158
|
+
'segment': self.segment.value,
|
|
159
|
+
'groww_order_id': self.groww_order_id
|
|
160
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#Todo Add Portfolio Types
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from ..exceptions import ValidationError
|
|
2
|
+
|
|
3
|
+
def require_non_empty(value: str, field_name: str) -> None:
|
|
4
|
+
if not value or not value.strip():
|
|
5
|
+
raise ValidationError(f"{field_name} is required and cannot be empty.")
|
|
6
|
+
|
|
7
|
+
def require_positive_int(value: int, field_name: str) -> None:
|
|
8
|
+
if value <= 0:
|
|
9
|
+
raise ValidationError(f"{field_name} must be greater than 0.")
|
|
10
|
+
|
|
11
|
+
def validate_access_token(access_token: str) -> None:
|
|
12
|
+
if not access_token or not access_token.strip():
|
|
13
|
+
raise ValidationError("Access token is required and cannot be empty.")
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tradekartapi
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Tradekart wrapper SDK for Groww trading APIs
|
|
5
|
+
Author-email: Vikalp <tradekart@gmail.com>, Siddhant <tradekart@gmail.com>, Lakshay <tradekart@gmail.com>
|
|
6
|
+
License: MIT
|
|
7
|
+
Requires-Python: >=3.10
|
|
8
|
+
Description-Content-Type: text/markdown
|
|
9
|
+
Requires-Dist: growwapi
|
|
10
|
+
Provides-Extra: dev
|
|
11
|
+
Requires-Dist: pytest>=8.3.2; extra == "dev"
|
|
12
|
+
Requires-Dist: ruff>=0.12.1; extra == "dev"
|
|
13
|
+
Requires-Dist: build>=1.2.2; extra == "dev"
|
|
14
|
+
Requires-Dist: twine>=5.1.1; extra == "dev"
|
|
15
|
+
Provides-Extra: aws
|
|
16
|
+
Requires-Dist: boto3>=1.34.0; extra == "aws"
|
|
17
|
+
|
|
18
|
+
# README #
|
|
19
|
+
|
|
20
|
+
This README would normally document whatever steps are necessary to get your application up and running.
|
|
21
|
+
|
|
22
|
+
### What is this repository for? ###
|
|
23
|
+
|
|
24
|
+
* Quick summary
|
|
25
|
+
* Version
|
|
26
|
+
* [Learn Markdown](https://bitbucket.org/tutorials/markdowndemo)
|
|
27
|
+
|
|
28
|
+
### How do I get set up? ###
|
|
29
|
+
|
|
30
|
+
* Summary of set up
|
|
31
|
+
* Configuration
|
|
32
|
+
* Dependencies
|
|
33
|
+
* Database configuration
|
|
34
|
+
* How to run tests
|
|
35
|
+
* Deployment instructions
|
|
36
|
+
|
|
37
|
+
### Contribution guidelines ###
|
|
38
|
+
|
|
39
|
+
* Writing tests
|
|
40
|
+
* Code review
|
|
41
|
+
* Other guidelines
|
|
42
|
+
|
|
43
|
+
### Who do I talk to? ###
|
|
44
|
+
|
|
45
|
+
* Repo owner or admin
|
|
46
|
+
* Other community or team contact
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/tradekartapi/__init__.py
|
|
4
|
+
src/tradekartapi/client.py
|
|
5
|
+
src/tradekartapi/models.py
|
|
6
|
+
src/tradekartapi/version.py
|
|
7
|
+
src/tradekartapi.egg-info/PKG-INFO
|
|
8
|
+
src/tradekartapi.egg-info/SOURCES.txt
|
|
9
|
+
src/tradekartapi.egg-info/dependency_links.txt
|
|
10
|
+
src/tradekartapi.egg-info/requires.txt
|
|
11
|
+
src/tradekartapi.egg-info/top_level.txt
|
|
12
|
+
src/tradekartapi/adapter/__init__.py
|
|
13
|
+
src/tradekartapi/adapter/groww_sdk_adapter.py
|
|
14
|
+
src/tradekartapi/config/__init__.py
|
|
15
|
+
src/tradekartapi/config/configuration.py
|
|
16
|
+
src/tradekartapi/core/__init__.py
|
|
17
|
+
src/tradekartapi/core/auth.py
|
|
18
|
+
src/tradekartapi/core/http_client.py
|
|
19
|
+
src/tradekartapi/core/interfaces.py
|
|
20
|
+
src/tradekartapi/exceptions/__init__.py
|
|
21
|
+
src/tradekartapi/exceptions/custom_exceptions.py
|
|
22
|
+
src/tradekartapi/services/__init__.py
|
|
23
|
+
src/tradekartapi/services/historical.py
|
|
24
|
+
src/tradekartapi/services/market_data.py
|
|
25
|
+
src/tradekartapi/services/orders.py
|
|
26
|
+
src/tradekartapi/services/portfolio.py
|
|
27
|
+
src/tradekartapi/types/__init__.py
|
|
28
|
+
src/tradekartapi/types/annexures.py
|
|
29
|
+
src/tradekartapi/types/order_types.py
|
|
30
|
+
src/tradekartapi/types/portfolio_types.py
|
|
31
|
+
src/tradekartapi/utils/__init__.py
|
|
32
|
+
src/tradekartapi/utils/validators.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tradekartapi
|