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.
Files changed (34) hide show
  1. tradekartapi-0.1.0/PKG-INFO +46 -0
  2. tradekartapi-0.1.0/README.md +29 -0
  3. tradekartapi-0.1.0/pyproject.toml +44 -0
  4. tradekartapi-0.1.0/setup.cfg +4 -0
  5. tradekartapi-0.1.0/src/tradekartapi/__init__.py +6 -0
  6. tradekartapi-0.1.0/src/tradekartapi/adapter/__init__.py +5 -0
  7. tradekartapi-0.1.0/src/tradekartapi/adapter/groww_sdk_adapter.py +24 -0
  8. tradekartapi-0.1.0/src/tradekartapi/client.py +29 -0
  9. tradekartapi-0.1.0/src/tradekartapi/config/__init__.py +6 -0
  10. tradekartapi-0.1.0/src/tradekartapi/config/configuration.py +4 -0
  11. tradekartapi-0.1.0/src/tradekartapi/core/__init__.py +5 -0
  12. tradekartapi-0.1.0/src/tradekartapi/core/auth.py +12 -0
  13. tradekartapi-0.1.0/src/tradekartapi/core/http_client.py +0 -0
  14. tradekartapi-0.1.0/src/tradekartapi/core/interfaces.py +0 -0
  15. tradekartapi-0.1.0/src/tradekartapi/exceptions/__init__.py +19 -0
  16. tradekartapi-0.1.0/src/tradekartapi/exceptions/custom_exceptions.py +90 -0
  17. tradekartapi-0.1.0/src/tradekartapi/models.py +0 -0
  18. tradekartapi-0.1.0/src/tradekartapi/services/__init__.py +7 -0
  19. tradekartapi-0.1.0/src/tradekartapi/services/historical.py +0 -0
  20. tradekartapi-0.1.0/src/tradekartapi/services/market_data.py +0 -0
  21. tradekartapi-0.1.0/src/tradekartapi/services/orders.py +75 -0
  22. tradekartapi-0.1.0/src/tradekartapi/services/portfolio.py +1 -0
  23. tradekartapi-0.1.0/src/tradekartapi/types/__init__.py +19 -0
  24. tradekartapi-0.1.0/src/tradekartapi/types/annexures.py +31 -0
  25. tradekartapi-0.1.0/src/tradekartapi/types/order_types.py +160 -0
  26. tradekartapi-0.1.0/src/tradekartapi/types/portfolio_types.py +1 -0
  27. tradekartapi-0.1.0/src/tradekartapi/utils/__init__.py +7 -0
  28. tradekartapi-0.1.0/src/tradekartapi/utils/validators.py +13 -0
  29. tradekartapi-0.1.0/src/tradekartapi/version.py +4 -0
  30. tradekartapi-0.1.0/src/tradekartapi.egg-info/PKG-INFO +46 -0
  31. tradekartapi-0.1.0/src/tradekartapi.egg-info/SOURCES.txt +32 -0
  32. tradekartapi-0.1.0/src/tradekartapi.egg-info/dependency_links.txt +1 -0
  33. tradekartapi-0.1.0/src/tradekartapi.egg-info/requires.txt +10 -0
  34. 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,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,6 @@
1
+ from .version import __version__
2
+
3
+ __all__ = [
4
+ '__version__',
5
+
6
+ ]
@@ -0,0 +1,5 @@
1
+ from .groww_sdk_adapter import GrowwSDKAdapter
2
+
3
+ __all__ = [
4
+ 'GrowwSDKAdapter'
5
+ ]
@@ -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,6 @@
1
+ from .configuration import DEFAULT_TIMEOUT_SECONDS, PACKAGE_NAME
2
+
3
+ __all__ = [
4
+ 'DEFAULT_TIMEOUT_SECONDS',
5
+ 'PACKAGE_NAME',
6
+ ]
@@ -0,0 +1,4 @@
1
+ # for future configuration like retry, timeout, env config
2
+
3
+ DEFAULT_TIMEOUT_SECONDS = 15
4
+ PACKAGE_NAME = "tradekart-groww-api"
@@ -0,0 +1,5 @@
1
+ from .auth import AccessTokenAuth
2
+
3
+ __all__ = [
4
+ "AccessTokenAuth",
5
+ ]
@@ -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
@@ -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
@@ -0,0 +1,7 @@
1
+ from .orders import OrderService
2
+ # from .portfolio import PortfolioService
3
+
4
+ __all__ = [
5
+ 'OrderService',
6
+ # 'PortfolioService'
7
+ ]
@@ -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,7 @@
1
+ from .validators import require_non_empty, require_positive_int, validate_access_token
2
+
3
+ __all__ = [
4
+ 'require_non_empty',
5
+ 'require_positive_int',
6
+ 'validate_access_token'
7
+ ]
@@ -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,4 @@
1
+ """Version information for tradekart-groww-api."""
2
+
3
+ __version_info__ = (0, 1, 0)
4
+ __version__ = ".".join(str(v) for v in __version_info__)
@@ -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,10 @@
1
+ growwapi
2
+
3
+ [aws]
4
+ boto3>=1.34.0
5
+
6
+ [dev]
7
+ pytest>=8.3.2
8
+ ruff>=0.12.1
9
+ build>=1.2.2
10
+ twine>=5.1.1
@@ -0,0 +1 @@
1
+ tradekartapi