dogabot-sdk 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.
@@ -0,0 +1,6 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.egg-info/
4
+ .pytest_cache/
5
+ dist/
6
+ .build/
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) dogabot
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,49 @@
1
+ Metadata-Version: 2.5
2
+ Name: dogabot-sdk
3
+ Version: 0.1.0
4
+ Summary: Official dogabot REST API SDK for Python
5
+ Author: dogabot
6
+ License-Expression: MIT
7
+ License-File: LICENSE
8
+ Keywords: api,dogabot,sdk,trading
9
+ Requires-Python: >=3.11
10
+ Requires-Dist: httpx>=0.27
11
+ Requires-Dist: pydantic>=2.0
12
+ Provides-Extra: dev
13
+ Requires-Dist: pytest>=8.0; extra == 'dev'
14
+ Requires-Dist: respx>=0.21; extra == 'dev'
15
+ Description-Content-Type: text/markdown
16
+
17
+ # dogabot-sdk (Python)
18
+
19
+ Official REST client for the [dogabot public API](https://docs.dogabot.com/).
20
+
21
+ ```bash
22
+ pip install dogabot-sdk
23
+ # or: uv add dogabot-sdk
24
+ # from source: pip install "dogabot-sdk @ git+https://github.com/dogabot/dogabot-sdk-python.git"
25
+ ```
26
+
27
+ ```python
28
+ from dogabot_sdk import Client
29
+
30
+ with Client(api_key="dbk_live_...") as client: # or DOGABOT_API_KEY
31
+ me = client.get_me()
32
+ print(me)
33
+ ```
34
+
35
+ Writes require an idempotency key:
36
+
37
+ ```python
38
+ client.resources.post_terminal_placeorder(
39
+ body={"exchange": "hyperliquid", "symbol": "BTC", "side": "buy", "quantity": 0.001,
40
+ "order_type": "market", "trading_mode": "paper", "broadcast_mode": "personal"},
41
+ options={"idempotency_key": "place-paper-btc-1"},
42
+ )
43
+ ```
44
+
45
+ **Backend use only** for API keys — do not embed `dbk_live_…` in browser bundles.
46
+
47
+ Runnable samples: [`examples/`](./examples/).
48
+
49
+ Docs: https://docs.dogabot.com/sdk/ · MCP: https://docs.dogabot.com/mcp/
@@ -0,0 +1,33 @@
1
+ # dogabot-sdk (Python)
2
+
3
+ Official REST client for the [dogabot public API](https://docs.dogabot.com/).
4
+
5
+ ```bash
6
+ pip install dogabot-sdk
7
+ # or: uv add dogabot-sdk
8
+ # from source: pip install "dogabot-sdk @ git+https://github.com/dogabot/dogabot-sdk-python.git"
9
+ ```
10
+
11
+ ```python
12
+ from dogabot_sdk import Client
13
+
14
+ with Client(api_key="dbk_live_...") as client: # or DOGABOT_API_KEY
15
+ me = client.get_me()
16
+ print(me)
17
+ ```
18
+
19
+ Writes require an idempotency key:
20
+
21
+ ```python
22
+ client.resources.post_terminal_placeorder(
23
+ body={"exchange": "hyperliquid", "symbol": "BTC", "side": "buy", "quantity": 0.001,
24
+ "order_type": "market", "trading_mode": "paper", "broadcast_mode": "personal"},
25
+ options={"idempotency_key": "place-paper-btc-1"},
26
+ )
27
+ ```
28
+
29
+ **Backend use only** for API keys — do not embed `dbk_live_…` in browser bundles.
30
+
31
+ Runnable samples: [`examples/`](./examples/).
32
+
33
+ Docs: https://docs.dogabot.com/sdk/ · MCP: https://docs.dogabot.com/mcp/
@@ -0,0 +1,15 @@
1
+ # dogabot-sdk examples (Python)
2
+
3
+ Set a live API key, then run from this directory (or any cwd):
4
+
5
+ ```bash
6
+ export DOGABOT_API_KEY=dbk_live_...
7
+ pip install -e .. # from sdk/python, once
8
+ python get_me.py
9
+ python get_ticker.py
10
+ python list_markets.py
11
+ # paper place only — refuses unless you opt in:
12
+ CONFIRM_PLACE=1 python place_paper_order.py
13
+ ```
14
+
15
+ Docs: https://docs.dogabot.com/sdk/
@@ -0,0 +1,21 @@
1
+ """Print the authenticated account (GET /api/v1/me)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import sys
8
+
9
+ from dogabot_sdk import Client
10
+
11
+
12
+ def main() -> None:
13
+ if not os.environ.get("DOGABOT_API_KEY"):
14
+ print("Set DOGABOT_API_KEY=dbk_live_...", file=sys.stderr)
15
+ sys.exit(1)
16
+ with Client() as client:
17
+ print(json.dumps(client.get_me(), indent=2, default=str))
18
+
19
+
20
+ if __name__ == "__main__":
21
+ main()
@@ -0,0 +1,26 @@
1
+ """Fetch a ticker with query params (GET /api/v1/ticker)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import sys
8
+
9
+ from dogabot_sdk import Client
10
+
11
+
12
+ def main() -> None:
13
+ if not os.environ.get("DOGABOT_API_KEY"):
14
+ print("Set DOGABOT_API_KEY=dbk_live_...", file=sys.stderr)
15
+ sys.exit(1)
16
+ exchange = os.environ.get("DOGABOT_EXCHANGE", "hyperliquid")
17
+ symbol = os.environ.get("DOGABOT_SYMBOL", "BTC")
18
+ with Client() as client:
19
+ ticker = client.resources.get_ticker(
20
+ options={"query": {"exchange": exchange, "symbol": symbol}},
21
+ )
22
+ print(json.dumps(ticker, indent=2, default=str))
23
+
24
+
25
+ if __name__ == "__main__":
26
+ main()
@@ -0,0 +1,24 @@
1
+ """List markets with optional filters (GET /api/v1/markets)."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import json
6
+ import os
7
+ import sys
8
+
9
+ from dogabot_sdk import Client
10
+
11
+
12
+ def main() -> None:
13
+ if not os.environ.get("DOGABOT_API_KEY"):
14
+ print("Set DOGABOT_API_KEY=dbk_live_...", file=sys.stderr)
15
+ sys.exit(1)
16
+ with Client() as client:
17
+ markets = client.resources.get_markets(
18
+ options={"query": {"limit": 5, "is_tradable": "true"}},
19
+ )
20
+ print(json.dumps(markets, indent=2, default=str))
21
+
22
+
23
+ if __name__ == "__main__":
24
+ main()
@@ -0,0 +1,48 @@
1
+ """Place a tiny paper market order (requires Idempotency-Key).
2
+
3
+ Safety: set CONFIRM_PLACE=1 to actually call the API. Default is dry-run print.
4
+ trading_mode stays paper — do not flip to live unless you intend to.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import json
10
+ import os
11
+ import sys
12
+ import uuid
13
+
14
+ from dogabot_sdk import Client
15
+
16
+
17
+ def main() -> None:
18
+ if not os.environ.get("DOGABOT_API_KEY"):
19
+ print("Set DOGABOT_API_KEY=dbk_live_...", file=sys.stderr)
20
+ sys.exit(1)
21
+
22
+ body = {
23
+ "exchange": os.environ.get("DOGABOT_EXCHANGE", "hyperliquid"),
24
+ "symbol": os.environ.get("DOGABOT_SYMBOL", "BTC"),
25
+ "side": "buy",
26
+ "quantity": 0.001,
27
+ "order_type": "market",
28
+ "trading_mode": "paper",
29
+ "broadcast_mode": "personal",
30
+ }
31
+ idem = os.environ.get("DOGABOT_IDEMPOTENCY_KEY") or f"example-paper-{uuid.uuid4()}"
32
+
33
+ if os.environ.get("CONFIRM_PLACE") != "1":
34
+ print("Dry-run only. Would POST placeorder with:")
35
+ print(json.dumps({"body": body, "idempotency_key": idem}, indent=2))
36
+ print("Re-run with CONFIRM_PLACE=1 to submit (still paper).")
37
+ return
38
+
39
+ with Client() as client:
40
+ out = client.resources.post_terminal_placeorder(
41
+ body=body,
42
+ options={"idempotency_key": idem},
43
+ )
44
+ print(json.dumps(out, indent=2, default=str))
45
+
46
+
47
+ if __name__ == "__main__":
48
+ main()
@@ -0,0 +1,24 @@
1
+ [build-system]
2
+ requires = ["hatchling>=1.25"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "dogabot-sdk"
7
+ version = "0.1.0"
8
+ description = "Official dogabot REST API SDK for Python"
9
+ readme = "README.md"
10
+ requires-python = ">=3.11"
11
+ license = "MIT"
12
+ authors = [{ name = "dogabot" }]
13
+ keywords = ["dogabot", "trading", "sdk", "api"]
14
+ dependencies = [
15
+ "httpx>=0.27",
16
+ "pydantic>=2.0",
17
+ ]
18
+
19
+ [project.optional-dependencies]
20
+ dev = ["pytest>=8.0", "respx>=0.21"]
21
+
22
+ [tool.pytest.ini_options]
23
+ testpaths = ["tests"]
24
+ pythonpath = ["src"]
@@ -0,0 +1,7 @@
1
+ """Official dogabot REST API SDK for Python."""
2
+
3
+ from dogabot_sdk._client import Client
4
+ from dogabot_sdk._errors import APIError
5
+ from dogabot_sdk.resources_generated import OPERATION_IDS, Resources
6
+
7
+ __all__ = ["Client", "APIError", "Resources", "OPERATION_IDS"]
@@ -0,0 +1,129 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ import time
5
+ from importlib.metadata import PackageNotFoundError, version
6
+ from typing import Any
7
+
8
+ import httpx
9
+
10
+ from dogabot_sdk._errors import APIError
11
+ from dogabot_sdk._types import JsonValue, RequestOptions
12
+ from dogabot_sdk.resources_generated import Resources
13
+
14
+ DEFAULT_BASE = "https://api.dogabot.com"
15
+
16
+
17
+ def _pkg_version() -> str:
18
+ try:
19
+ return version("dogabot-sdk")
20
+ except PackageNotFoundError:
21
+ return "0.1.0"
22
+
23
+
24
+ class Client:
25
+ def __init__(
26
+ self,
27
+ *,
28
+ api_key: str | None = None,
29
+ base_url: str = DEFAULT_BASE,
30
+ timeout: float = 30.0,
31
+ max_retries: int = 2,
32
+ http_client: httpx.Client | None = None,
33
+ user_agent: str | None = None,
34
+ ) -> None:
35
+ key = api_key or os.environ.get("DOGABOT_API_KEY")
36
+ if not key:
37
+ raise ValueError("api_key is required (pass api_key= or set DOGABOT_API_KEY)")
38
+ self.api_key = key
39
+ self.base_url = base_url.rstrip("/")
40
+ self.timeout = timeout
41
+ self.max_retries = max_retries
42
+ self.user_agent = user_agent or f"dogabot-sdk-python/{_pkg_version()}"
43
+ self._owns_client = http_client is None
44
+ self._http = http_client or httpx.Client(timeout=timeout)
45
+ self.resources = Resources(self)
46
+
47
+ def close(self) -> None:
48
+ if self._owns_client:
49
+ self._http.close()
50
+
51
+ def __enter__(self) -> Client:
52
+ return self
53
+
54
+ def __exit__(self, *args: object) -> None:
55
+ self.close()
56
+
57
+ def get_me(self, *, options: RequestOptions | None = None) -> JsonValue:
58
+ return self.resources.get_me(options=options)
59
+
60
+ def request(
61
+ self,
62
+ *,
63
+ method: str,
64
+ path: str,
65
+ body: JsonValue | None = None,
66
+ write: bool = False,
67
+ options: RequestOptions | None = None,
68
+ ) -> JsonValue:
69
+ opts = options or {}
70
+ idem = opts.get("idempotency_key")
71
+ if write and not idem:
72
+ raise ValueError(f"idempotency_key is required for write operation {method} {path}")
73
+
74
+ url = path if path.startswith("http") else f"{self.base_url}{path}"
75
+ headers = {
76
+ "Authorization": f"Bearer {self.api_key}",
77
+ "Accept": "application/json",
78
+ "User-Agent": self.user_agent,
79
+ }
80
+ extra = opts.get("headers")
81
+ if extra:
82
+ headers.update(extra)
83
+ if idem:
84
+ headers["Idempotency-Key"] = idem
85
+
86
+ params = opts.get("query")
87
+ timeout = opts.get("timeout", self.timeout)
88
+
89
+ attempt = 0
90
+ while True:
91
+ resp = self._http.request(
92
+ method,
93
+ url,
94
+ params=params,
95
+ json=body if body is not None else None,
96
+ headers=headers,
97
+ timeout=timeout,
98
+ )
99
+ if resp.is_success:
100
+ if not resp.content:
101
+ return None
102
+ try:
103
+ return resp.json()
104
+ except Exception:
105
+ return resp.text
106
+
107
+ retryable = resp.status_code == 429 or resp.status_code >= 500
108
+ if retryable and attempt < self.max_retries:
109
+ attempt += 1
110
+ retry_after = resp.headers.get("Retry-After")
111
+ try:
112
+ wait = float(retry_after) if retry_after else min(2**attempt, 8)
113
+ except ValueError:
114
+ wait = min(2**attempt, 8)
115
+ time.sleep(wait)
116
+ continue
117
+
118
+ body_json: dict[str, Any] | None
119
+ try:
120
+ parsed = resp.json()
121
+ body_json = parsed if isinstance(parsed, dict) else None
122
+ except Exception:
123
+ body_json = None
124
+ msg = (
125
+ body_json.get("error")
126
+ if body_json and isinstance(body_json.get("error"), str)
127
+ else f"HTTP {resp.status_code} {method} {path}"
128
+ )
129
+ raise APIError(msg, status_code=resp.status_code, body=body_json, headers=resp.headers)
@@ -0,0 +1,20 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Mapping
4
+
5
+ import httpx
6
+
7
+
8
+ class APIError(Exception):
9
+ def __init__(
10
+ self,
11
+ message: str,
12
+ *,
13
+ status_code: int,
14
+ body: Mapping[str, Any] | None,
15
+ headers: httpx.Headers,
16
+ ) -> None:
17
+ super().__init__(message)
18
+ self.status_code = status_code
19
+ self.body = body
20
+ self.headers = headers
@@ -0,0 +1,12 @@
1
+ from __future__ import annotations
2
+
3
+ from typing import Any, Mapping, MutableMapping, TypedDict
4
+
5
+ JsonValue = Any
6
+
7
+
8
+ class RequestOptions(TypedDict, total=False):
9
+ query: Mapping[str, Any]
10
+ headers: MutableMapping[str, str]
11
+ idempotency_key: str
12
+ timeout: float
@@ -0,0 +1 @@
1
+ # Marker for PEP 561 typed package.