ctrader-api-client 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 (93) hide show
  1. ctrader_api_client-0.1.0/.github/workflows/docs.yml +18 -0
  2. ctrader_api_client-0.1.0/.gitignore +14 -0
  3. ctrader_api_client-0.1.0/.pre-commit-config.yaml +19 -0
  4. ctrader_api_client-0.1.0/.python-version +1 -0
  5. ctrader_api_client-0.1.0/Justfile +51 -0
  6. ctrader_api_client-0.1.0/LICENSE +21 -0
  7. ctrader_api_client-0.1.0/PKG-INFO +252 -0
  8. ctrader_api_client-0.1.0/README.md +233 -0
  9. ctrader_api_client-0.1.0/docs/api/accounts.md +44 -0
  10. ctrader_api_client-0.1.0/docs/api/client.md +52 -0
  11. ctrader_api_client-0.1.0/docs/api/enums.md +102 -0
  12. ctrader_api_client-0.1.0/docs/api/events.md +143 -0
  13. ctrader_api_client-0.1.0/docs/api/market-data.md +117 -0
  14. ctrader_api_client-0.1.0/docs/api/models.md +161 -0
  15. ctrader_api_client-0.1.0/docs/api/symbols.md +91 -0
  16. ctrader_api_client-0.1.0/docs/api/trading.md +123 -0
  17. ctrader_api_client-0.1.0/docs/getting-started.md +221 -0
  18. ctrader_api_client-0.1.0/docs/index.md +77 -0
  19. ctrader_api_client-0.1.0/mkdocs.yml +67 -0
  20. ctrader_api_client-0.1.0/protos/SOURCE +1 -0
  21. ctrader_api_client-0.1.0/protos/VERSION +1 -0
  22. ctrader_api_client-0.1.0/protos/update.sh +31 -0
  23. ctrader_api_client-0.1.0/protos/vendor/.gitkeep +0 -0
  24. ctrader_api_client-0.1.0/protos/vendor/OpenApiCommonMessages.proto +31 -0
  25. ctrader_api_client-0.1.0/protos/vendor/OpenApiCommonModelMessages.proto +33 -0
  26. ctrader_api_client-0.1.0/protos/vendor/OpenApiMessages.proto +828 -0
  27. ctrader_api_client-0.1.0/protos/vendor/OpenApiModelMessages.proto +729 -0
  28. ctrader_api_client-0.1.0/pyproject.toml +75 -0
  29. ctrader_api_client-0.1.0/scripts/fix_proto_imports.py +147 -0
  30. ctrader_api_client-0.1.0/src/ctrader_api_client/__init__.py +64 -0
  31. ctrader_api_client-0.1.0/src/ctrader_api_client/_internal/__init__.py +26 -0
  32. ctrader_api_client-0.1.0/src/ctrader_api_client/_internal/messages.py +348 -0
  33. ctrader_api_client-0.1.0/src/ctrader_api_client/_internal/proto/OpenApiCommonMessages.py +42 -0
  34. ctrader_api_client-0.1.0/src/ctrader_api_client/_internal/proto/OpenApiCommonModelMessages.py +30 -0
  35. ctrader_api_client-0.1.0/src/ctrader_api_client/_internal/proto/OpenApiMessages.py +1112 -0
  36. ctrader_api_client-0.1.0/src/ctrader_api_client/_internal/proto/OpenApiModelMessages.py +802 -0
  37. ctrader_api_client-0.1.0/src/ctrader_api_client/_internal/proto/__init__.py +320 -0
  38. ctrader_api_client-0.1.0/src/ctrader_api_client/_internal/serialization.py +84 -0
  39. ctrader_api_client-0.1.0/src/ctrader_api_client/api/__init__.py +21 -0
  40. ctrader_api_client-0.1.0/src/ctrader_api_client/api/accounts.py +71 -0
  41. ctrader_api_client-0.1.0/src/ctrader_api_client/api/market_data.py +424 -0
  42. ctrader_api_client-0.1.0/src/ctrader_api_client/api/symbols.py +171 -0
  43. ctrader_api_client-0.1.0/src/ctrader_api_client/api/trading.py +506 -0
  44. ctrader_api_client-0.1.0/src/ctrader_api_client/auth/__init__.py +14 -0
  45. ctrader_api_client-0.1.0/src/ctrader_api_client/auth/credentials.py +72 -0
  46. ctrader_api_client-0.1.0/src/ctrader_api_client/auth/manager.py +511 -0
  47. ctrader_api_client-0.1.0/src/ctrader_api_client/client.py +475 -0
  48. ctrader_api_client-0.1.0/src/ctrader_api_client/config.py +56 -0
  49. ctrader_api_client-0.1.0/src/ctrader_api_client/connection/__init__.py +16 -0
  50. ctrader_api_client-0.1.0/src/ctrader_api_client/connection/heartbeat.py +120 -0
  51. ctrader_api_client-0.1.0/src/ctrader_api_client/connection/protocol.py +366 -0
  52. ctrader_api_client-0.1.0/src/ctrader_api_client/connection/transport.py +123 -0
  53. ctrader_api_client-0.1.0/src/ctrader_api_client/enums.py +138 -0
  54. ctrader_api_client-0.1.0/src/ctrader_api_client/events/__init__.py +65 -0
  55. ctrader_api_client-0.1.0/src/ctrader_api_client/events/emitter.py +254 -0
  56. ctrader_api_client-0.1.0/src/ctrader_api_client/events/router.py +400 -0
  57. ctrader_api_client-0.1.0/src/ctrader_api_client/events/types.py +340 -0
  58. ctrader_api_client-0.1.0/src/ctrader_api_client/exceptions.py +231 -0
  59. ctrader_api_client-0.1.0/src/ctrader_api_client/models/__init__.py +50 -0
  60. ctrader_api_client-0.1.0/src/ctrader_api_client/models/_base.py +19 -0
  61. ctrader_api_client-0.1.0/src/ctrader_api_client/models/account.py +177 -0
  62. ctrader_api_client-0.1.0/src/ctrader_api_client/models/deal.py +242 -0
  63. ctrader_api_client-0.1.0/src/ctrader_api_client/models/market_data.py +192 -0
  64. ctrader_api_client-0.1.0/src/ctrader_api_client/models/order.py +262 -0
  65. ctrader_api_client-0.1.0/src/ctrader_api_client/models/position.py +209 -0
  66. ctrader_api_client-0.1.0/src/ctrader_api_client/models/requests.py +299 -0
  67. ctrader_api_client-0.1.0/src/ctrader_api_client/models/symbol.py +194 -0
  68. ctrader_api_client-0.1.0/src/ctrader_api_client/py.typed +0 -0
  69. ctrader_api_client-0.1.0/tests/unit/_internal/test_messages.py +270 -0
  70. ctrader_api_client-0.1.0/tests/unit/_internal/test_serialization.py +172 -0
  71. ctrader_api_client-0.1.0/tests/unit/api/conftest.py +13 -0
  72. ctrader_api_client-0.1.0/tests/unit/api/test_accounts.py +117 -0
  73. ctrader_api_client-0.1.0/tests/unit/api/test_market_data_api.py +219 -0
  74. ctrader_api_client-0.1.0/tests/unit/api/test_symbols.py +281 -0
  75. ctrader_api_client-0.1.0/tests/unit/api/test_trading.py +350 -0
  76. ctrader_api_client-0.1.0/tests/unit/auth/test_credentials.py +177 -0
  77. ctrader_api_client-0.1.0/tests/unit/auth/test_manager.py +775 -0
  78. ctrader_api_client-0.1.0/tests/unit/connection/test_heartbeat.py +193 -0
  79. ctrader_api_client-0.1.0/tests/unit/connection/test_protocol.py +396 -0
  80. ctrader_api_client-0.1.0/tests/unit/connection/test_transport.py +244 -0
  81. ctrader_api_client-0.1.0/tests/unit/events/test_emitter.py +343 -0
  82. ctrader_api_client-0.1.0/tests/unit/events/test_router.py +608 -0
  83. ctrader_api_client-0.1.0/tests/unit/events/test_types.py +335 -0
  84. ctrader_api_client-0.1.0/tests/unit/models/test_account.py +243 -0
  85. ctrader_api_client-0.1.0/tests/unit/models/test_deal.py +381 -0
  86. ctrader_api_client-0.1.0/tests/unit/models/test_market_data.py +265 -0
  87. ctrader_api_client-0.1.0/tests/unit/models/test_order.py +438 -0
  88. ctrader_api_client-0.1.0/tests/unit/models/test_position.py +334 -0
  89. ctrader_api_client-0.1.0/tests/unit/models/test_requests.py +378 -0
  90. ctrader_api_client-0.1.0/tests/unit/models/test_symbol.py +263 -0
  91. ctrader_api_client-0.1.0/tests/unit/test_client.py +491 -0
  92. ctrader_api_client-0.1.0/tests/unit/test_config.py +149 -0
  93. ctrader_api_client-0.1.0/uv.lock +1067 -0
@@ -0,0 +1,18 @@
1
+ name: Deploy Documentation
2
+
3
+ on:
4
+ push:
5
+ branches: [master]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: write
10
+
11
+ jobs:
12
+ deploy:
13
+ runs-on: ubuntu-latest
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: astral-sh/setup-uv@v5
17
+ - run: uv sync --group dev
18
+ - run: uv run mkdocs gh-deploy --force
@@ -0,0 +1,14 @@
1
+ # Python-generated files
2
+ __pycache__/
3
+ *.py[oc]
4
+ build/
5
+ dist/
6
+ wheels/
7
+ *.egg-info
8
+
9
+ # Virtual environments
10
+ .venv
11
+
12
+ # IDE-specific files
13
+ .idea/
14
+ .vscode/
@@ -0,0 +1,19 @@
1
+ repos:
2
+ # Ruff for linting & formatting
3
+ - repo: https://github.com/astral-sh/ruff-pre-commit
4
+ rev: v0.12.9
5
+ hooks:
6
+ - id: ruff-check
7
+ args: [--fix]
8
+ language_version: python3
9
+ - id: ruff-format
10
+ language_version: python3
11
+
12
+ # Basic pre-commit housekeeping hooks
13
+ - repo: https://github.com/pre-commit/pre-commit-hooks
14
+ rev: v6.0.0
15
+ hooks:
16
+ - id: end-of-file-fixer
17
+ - id: trailing-whitespace
18
+ - id: check-added-large-files
19
+ - id: check-shebang-scripts-are-executable
@@ -0,0 +1 @@
1
+ 3.14
@@ -0,0 +1,51 @@
1
+ #!/usr/bin/env just --justfile
2
+
3
+ SEPARATOR := `printf '%0.s-' {1..60}`
4
+ PROTO_PATH := "protos/vendor"
5
+ PYTHON_OUT_PATH := "src/ctrader_api_client/_internal/proto"
6
+
7
+ default: help
8
+
9
+ # Print this help message
10
+ help:
11
+ just --list
12
+
13
+ # Run all CI steps: linting, formatting, type checking
14
+ ci directory='':
15
+ @just lint {{directory}}
16
+ @just fmt {{directory}}
17
+ @just type-check {{directory}}
18
+
19
+ # Lint the codebase using ruff, optionally specifying a directory to lint.
20
+ lint directory='':
21
+ uv run ruff check --fix {{directory}}
22
+
23
+ # Format the codebase using ruff, optionally specifying a directory to format.
24
+ fmt directory='':
25
+ uv run ruff format {{directory}}
26
+
27
+ # Run type checking using ty, optionally specifying a directory to check.
28
+ type-check directory='':
29
+ uv run ty check {{directory}}
30
+
31
+ # Run tests using pytest, optionally specifying a directory to test.
32
+ test directory='':
33
+ uv run pytest {{directory}}
34
+
35
+ # Update .proto files to a specific version. Defaults to 'main' if no version is provided.
36
+ update-proto version='':
37
+ ./protos/update.sh {{version}}
38
+
39
+ # Generate Python code from .proto files. This should be run after updating the .proto files.
40
+ @compile-proto:
41
+ echo {{SEPARATOR}}
42
+ echo "Compiling .proto files from {{PROTO_PATH}}/ to Python code under {{PYTHON_OUT_PATH}}/"
43
+ uv run protoc -I={{PROTO_PATH}} \
44
+ --python_betterproto_out={{PYTHON_OUT_PATH}} \
45
+ {{PROTO_PATH}}/*.proto
46
+ echo {{SEPARATOR}}
47
+ echo "Fixing cross-module imports..."
48
+ uv run python scripts/fix_proto_imports.py
49
+ echo "Formatting generated code with ruff..."
50
+ @uv run ruff check {{PYTHON_OUT_PATH}} --fix
51
+ @uv run ruff format {{PYTHON_OUT_PATH}}
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Elio Anthony Chukri
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,252 @@
1
+ Metadata-Version: 2.4
2
+ Name: ctrader-api-client
3
+ Version: 0.1.0
4
+ Summary: API Client to interact with the cTrader Open API spec
5
+ Author-email: Elio <elioachukri@pm.me>
6
+ License-File: LICENSE
7
+ Classifier: License :: OSI Approved :: MIT License
8
+ Classifier: Natural Language :: English
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: Programming Language :: Python :: 3.12
11
+ Classifier: Programming Language :: Python :: 3.13
12
+ Classifier: Programming Language :: Python :: 3.14
13
+ Requires-Python: >=3.12
14
+ Requires-Dist: anyio>=4.13.0
15
+ Requires-Dist: betterproto[compiler]>=1.2.5
16
+ Requires-Dist: pydantic>=2.12.5
17
+ Requires-Dist: tenacity>=9.1.4
18
+ Description-Content-Type: text/markdown
19
+
20
+ # cTrader API Client
21
+
22
+ A Python client for the cTrader Open API. Provides a high-level async interface for trading operations, market data subscriptions, and account management.
23
+
24
+ Documentation:
25
+ - [Library Docs](https://elioachukri.github.io/ctrader-api-client/)
26
+ - [cTrader Open API Docs](https://help.ctrader.com/open-api/)
27
+
28
+ > Note that this library is in early development. The API may change, and some features may be incomplete. Contributions and feedback are welcome!
29
+
30
+ ## Requirements
31
+
32
+ - Python 3.12+
33
+ - An activated cTrader Open API application with client ID and secret
34
+ - OAuth tokens for account authentication (see below)
35
+
36
+ ## Installation
37
+
38
+ **Using uv (recommended):**
39
+
40
+ ```bash
41
+ uv add ctrader-api-client
42
+ ```
43
+
44
+ **Using pip:**
45
+
46
+ ```bash
47
+ pip install ctrader-api-client
48
+ ```
49
+
50
+ ## Quick Start
51
+
52
+ ```python
53
+ import asyncio
54
+ from ctrader_api_client import CTraderClient, ClientConfig
55
+ from ctrader_api_client.events import ReadyEvent, SpotEvent
56
+
57
+ config = ClientConfig(
58
+ client_id="your_client_id",
59
+ client_secret="your_client_secret",
60
+ )
61
+
62
+ client = CTraderClient(config)
63
+
64
+
65
+ @client.on(SpotEvent, symbol_id=270) # US500.cash
66
+ async def on_price(event: SpotEvent):
67
+ print(f"Price update: {event.bid}/{event.ask}")
68
+
69
+
70
+ @client.on(ReadyEvent)
71
+ async def on_ready(event: ReadyEvent):
72
+ """Called when account is authenticated and ready."""
73
+ await client.market_data.subscribe_spots(event.account_id, [270])
74
+
75
+
76
+ async def main():
77
+ async with client:
78
+ await client.auth.authenticate_app()
79
+ await client.auth.authenticate_by_trader_login(
80
+ trader_login=12345678,
81
+ access_token="your_access_token",
82
+ refresh_token="your_refresh_token",
83
+ expires_at=1778617423,
84
+ )
85
+
86
+ # Keep running to receive events
87
+ await asyncio.Event().wait()
88
+
89
+
90
+ if __name__ == "__main__":
91
+ asyncio.run(main())
92
+ ```
93
+
94
+ ## OAuth Token Generation
95
+
96
+ This library requires OAuth tokens from cTrader. For simple use cases, you can use [ctrader-oauth-fetcher](https://github.com/ElioaChukri/ctrader-oauth-fetcher) to generate tokens:
97
+
98
+ ```bash
99
+ uvx ctrader-oauth-fetcher --client-id [ID] --client-secret [SECRET]
100
+ ```
101
+
102
+ This opens a browser for authorization and returns your access token, refresh token, and expiry time.
103
+
104
+ For production applications, implement the OAuth flow according to the [cTrader Open API documentation](https://help.ctrader.com/open-api/).
105
+
106
+ ## Features
107
+
108
+ ### Authentication
109
+
110
+ ```python
111
+ # Authenticate the application
112
+ await client.auth.authenticate_app()
113
+
114
+ # Authenticate a trading account
115
+ creds = await client.auth.authenticate_by_trader_login(
116
+ trader_login=12345678,
117
+ access_token="...",
118
+ refresh_token="...",
119
+ expires_at=1778617423,
120
+ )
121
+
122
+ # Tokens are automatically refreshed before expiry
123
+ ```
124
+
125
+ ### Market Data
126
+
127
+ ```python
128
+ # Subscribe to spot prices
129
+ await client.market_data.subscribe_spots(account_id, [symbol_id])
130
+
131
+ # Subscribe to candles
132
+ await client.market_data.subscribe_trendbars(account_id, symbol_id, TrendbarPeriod.M1)
133
+
134
+ # Get historical data
135
+ bars = await client.market_data.get_trendbars(
136
+ account_id, symbol_id, TrendbarPeriod.H1, from_ts, to_ts
137
+ )
138
+ ```
139
+
140
+ ### Trading
141
+
142
+ ```python
143
+ from ctrader_api_client.models import NewOrderRequest, ClosePositionRequest
144
+ from ctrader_api_client.enums import OrderType, OrderSide
145
+
146
+ # Place a market order
147
+ request = NewOrderRequest(
148
+ symbol_id=symbol_id,
149
+ order_type=OrderType.MARKET,
150
+ side=OrderSide.BUY,
151
+ volume=100000, # 1 lot in cents
152
+ )
153
+ result = await client.trading.create_order(account_id, request)
154
+
155
+ # Get open positions
156
+ positions = await client.trading.get_positions(account_id)
157
+
158
+ # Close a position
159
+ close_position = ClosePositionRequest(
160
+ position_id=position_id,
161
+ volume=100000, # Close full volume
162
+ )
163
+ await client.trading.close_position(account_id, close_position)
164
+ ```
165
+
166
+ ### Event Handling
167
+
168
+ ```python
169
+ from ctrader_api_client.events import (
170
+ SpotEvent,
171
+ ExecutionEvent,
172
+ ReadyEvent,
173
+ ReconnectedEvent,
174
+ )
175
+
176
+ # Price updates
177
+ @client.on(SpotEvent, symbol_id=270)
178
+ async def on_spot(event: SpotEvent):
179
+ print(f"{event.bid}/{event.ask}")
180
+
181
+ # Order executions
182
+ @client.on(ExecutionEvent, account_id=account_id)
183
+ async def on_execution(event: ExecutionEvent):
184
+ print(f"Order {event.order_id}: {event.execution_type}")
185
+
186
+ # Account ready (fires on initial auth and after reconnection)
187
+ @client.on(ReadyEvent)
188
+ async def on_ready(event: ReadyEvent):
189
+ # Set up subscriptions here
190
+ await client.market_data.subscribe_spots(event.account_id, symbols)
191
+
192
+ # Connection restored
193
+ @client.on(ReconnectedEvent)
194
+ async def on_reconnected(event: ReconnectedEvent):
195
+ print(f"Reconnected, restored accounts: {event.restored_accounts}")
196
+ ```
197
+
198
+ ### Symbols
199
+
200
+ ```python
201
+ # List all symbols
202
+ symbols = await client.symbols.list_all(account_id)
203
+
204
+ # Search by name
205
+ results = await client.symbols.search(account_id, "EUR")
206
+
207
+ # Get specific symbol
208
+ symbol = await client.symbols.get_by_id(account_id, symbol_id)
209
+ ```
210
+
211
+ ### Account Information
212
+
213
+ ```python
214
+ # Get account details
215
+ account = await client.accounts.get_trader(account_id)
216
+ print(f"Balance: {account.balance}")
217
+ ```
218
+
219
+ ## Automatic Reconnection
220
+
221
+ The client automatically handles connection drops:
222
+
223
+ 1. Reconnects with exponential backoff
224
+ 2. Re-authenticates the app and all accounts
225
+ 3. Emits `ReadyEvent` for each restored account (for resubscribing to market data)
226
+ 4. Emits `ReconnectedEvent` with summary of restored/failed accounts
227
+
228
+ Use `ReadyEvent` to set up subscriptions that persist across reconnections.
229
+
230
+ ## Configuration
231
+
232
+ ```python
233
+ config = ClientConfig(
234
+ client_id="your_client_id",
235
+ client_secret="your_client_secret",
236
+
237
+ # Connection settings
238
+ host="live.ctraderapi.com", # or "demo.ctraderapi.com"
239
+ port=5035,
240
+ use_ssl=True,
241
+
242
+ # Timeouts
243
+ heartbeat_interval=10.0,
244
+ heartbeat_timeout=30.0, # Or 0 to disable server heartbeat checks
245
+ request_timeout=30.0,
246
+
247
+ # Reconnection
248
+ reconnect_attempts=5,
249
+ reconnect_min_wait=1.0,
250
+ reconnect_max_wait=60.0,
251
+ )
252
+ ```
@@ -0,0 +1,233 @@
1
+ # cTrader API Client
2
+
3
+ A Python client for the cTrader Open API. Provides a high-level async interface for trading operations, market data subscriptions, and account management.
4
+
5
+ Documentation:
6
+ - [Library Docs](https://elioachukri.github.io/ctrader-api-client/)
7
+ - [cTrader Open API Docs](https://help.ctrader.com/open-api/)
8
+
9
+ > Note that this library is in early development. The API may change, and some features may be incomplete. Contributions and feedback are welcome!
10
+
11
+ ## Requirements
12
+
13
+ - Python 3.12+
14
+ - An activated cTrader Open API application with client ID and secret
15
+ - OAuth tokens for account authentication (see below)
16
+
17
+ ## Installation
18
+
19
+ **Using uv (recommended):**
20
+
21
+ ```bash
22
+ uv add ctrader-api-client
23
+ ```
24
+
25
+ **Using pip:**
26
+
27
+ ```bash
28
+ pip install ctrader-api-client
29
+ ```
30
+
31
+ ## Quick Start
32
+
33
+ ```python
34
+ import asyncio
35
+ from ctrader_api_client import CTraderClient, ClientConfig
36
+ from ctrader_api_client.events import ReadyEvent, SpotEvent
37
+
38
+ config = ClientConfig(
39
+ client_id="your_client_id",
40
+ client_secret="your_client_secret",
41
+ )
42
+
43
+ client = CTraderClient(config)
44
+
45
+
46
+ @client.on(SpotEvent, symbol_id=270) # US500.cash
47
+ async def on_price(event: SpotEvent):
48
+ print(f"Price update: {event.bid}/{event.ask}")
49
+
50
+
51
+ @client.on(ReadyEvent)
52
+ async def on_ready(event: ReadyEvent):
53
+ """Called when account is authenticated and ready."""
54
+ await client.market_data.subscribe_spots(event.account_id, [270])
55
+
56
+
57
+ async def main():
58
+ async with client:
59
+ await client.auth.authenticate_app()
60
+ await client.auth.authenticate_by_trader_login(
61
+ trader_login=12345678,
62
+ access_token="your_access_token",
63
+ refresh_token="your_refresh_token",
64
+ expires_at=1778617423,
65
+ )
66
+
67
+ # Keep running to receive events
68
+ await asyncio.Event().wait()
69
+
70
+
71
+ if __name__ == "__main__":
72
+ asyncio.run(main())
73
+ ```
74
+
75
+ ## OAuth Token Generation
76
+
77
+ This library requires OAuth tokens from cTrader. For simple use cases, you can use [ctrader-oauth-fetcher](https://github.com/ElioaChukri/ctrader-oauth-fetcher) to generate tokens:
78
+
79
+ ```bash
80
+ uvx ctrader-oauth-fetcher --client-id [ID] --client-secret [SECRET]
81
+ ```
82
+
83
+ This opens a browser for authorization and returns your access token, refresh token, and expiry time.
84
+
85
+ For production applications, implement the OAuth flow according to the [cTrader Open API documentation](https://help.ctrader.com/open-api/).
86
+
87
+ ## Features
88
+
89
+ ### Authentication
90
+
91
+ ```python
92
+ # Authenticate the application
93
+ await client.auth.authenticate_app()
94
+
95
+ # Authenticate a trading account
96
+ creds = await client.auth.authenticate_by_trader_login(
97
+ trader_login=12345678,
98
+ access_token="...",
99
+ refresh_token="...",
100
+ expires_at=1778617423,
101
+ )
102
+
103
+ # Tokens are automatically refreshed before expiry
104
+ ```
105
+
106
+ ### Market Data
107
+
108
+ ```python
109
+ # Subscribe to spot prices
110
+ await client.market_data.subscribe_spots(account_id, [symbol_id])
111
+
112
+ # Subscribe to candles
113
+ await client.market_data.subscribe_trendbars(account_id, symbol_id, TrendbarPeriod.M1)
114
+
115
+ # Get historical data
116
+ bars = await client.market_data.get_trendbars(
117
+ account_id, symbol_id, TrendbarPeriod.H1, from_ts, to_ts
118
+ )
119
+ ```
120
+
121
+ ### Trading
122
+
123
+ ```python
124
+ from ctrader_api_client.models import NewOrderRequest, ClosePositionRequest
125
+ from ctrader_api_client.enums import OrderType, OrderSide
126
+
127
+ # Place a market order
128
+ request = NewOrderRequest(
129
+ symbol_id=symbol_id,
130
+ order_type=OrderType.MARKET,
131
+ side=OrderSide.BUY,
132
+ volume=100000, # 1 lot in cents
133
+ )
134
+ result = await client.trading.create_order(account_id, request)
135
+
136
+ # Get open positions
137
+ positions = await client.trading.get_positions(account_id)
138
+
139
+ # Close a position
140
+ close_position = ClosePositionRequest(
141
+ position_id=position_id,
142
+ volume=100000, # Close full volume
143
+ )
144
+ await client.trading.close_position(account_id, close_position)
145
+ ```
146
+
147
+ ### Event Handling
148
+
149
+ ```python
150
+ from ctrader_api_client.events import (
151
+ SpotEvent,
152
+ ExecutionEvent,
153
+ ReadyEvent,
154
+ ReconnectedEvent,
155
+ )
156
+
157
+ # Price updates
158
+ @client.on(SpotEvent, symbol_id=270)
159
+ async def on_spot(event: SpotEvent):
160
+ print(f"{event.bid}/{event.ask}")
161
+
162
+ # Order executions
163
+ @client.on(ExecutionEvent, account_id=account_id)
164
+ async def on_execution(event: ExecutionEvent):
165
+ print(f"Order {event.order_id}: {event.execution_type}")
166
+
167
+ # Account ready (fires on initial auth and after reconnection)
168
+ @client.on(ReadyEvent)
169
+ async def on_ready(event: ReadyEvent):
170
+ # Set up subscriptions here
171
+ await client.market_data.subscribe_spots(event.account_id, symbols)
172
+
173
+ # Connection restored
174
+ @client.on(ReconnectedEvent)
175
+ async def on_reconnected(event: ReconnectedEvent):
176
+ print(f"Reconnected, restored accounts: {event.restored_accounts}")
177
+ ```
178
+
179
+ ### Symbols
180
+
181
+ ```python
182
+ # List all symbols
183
+ symbols = await client.symbols.list_all(account_id)
184
+
185
+ # Search by name
186
+ results = await client.symbols.search(account_id, "EUR")
187
+
188
+ # Get specific symbol
189
+ symbol = await client.symbols.get_by_id(account_id, symbol_id)
190
+ ```
191
+
192
+ ### Account Information
193
+
194
+ ```python
195
+ # Get account details
196
+ account = await client.accounts.get_trader(account_id)
197
+ print(f"Balance: {account.balance}")
198
+ ```
199
+
200
+ ## Automatic Reconnection
201
+
202
+ The client automatically handles connection drops:
203
+
204
+ 1. Reconnects with exponential backoff
205
+ 2. Re-authenticates the app and all accounts
206
+ 3. Emits `ReadyEvent` for each restored account (for resubscribing to market data)
207
+ 4. Emits `ReconnectedEvent` with summary of restored/failed accounts
208
+
209
+ Use `ReadyEvent` to set up subscriptions that persist across reconnections.
210
+
211
+ ## Configuration
212
+
213
+ ```python
214
+ config = ClientConfig(
215
+ client_id="your_client_id",
216
+ client_secret="your_client_secret",
217
+
218
+ # Connection settings
219
+ host="live.ctraderapi.com", # or "demo.ctraderapi.com"
220
+ port=5035,
221
+ use_ssl=True,
222
+
223
+ # Timeouts
224
+ heartbeat_interval=10.0,
225
+ heartbeat_timeout=30.0, # Or 0 to disable server heartbeat checks
226
+ request_timeout=30.0,
227
+
228
+ # Reconnection
229
+ reconnect_attempts=5,
230
+ reconnect_min_wait=1.0,
231
+ reconnect_max_wait=60.0,
232
+ )
233
+ ```
@@ -0,0 +1,44 @@
1
+ # Accounts API
2
+
3
+ Account information retrieval operations.
4
+
5
+ Access via `client.accounts`.
6
+
7
+ ## AccountsAPI
8
+
9
+ ::: ctrader_api_client.api.AccountsAPI
10
+ options:
11
+ show_source: false
12
+ members:
13
+ - get_trader
14
+
15
+ ## Usage Examples
16
+
17
+ ### Get Account Details
18
+
19
+ ```python
20
+ account = await client.accounts.get_trader(account_id)
21
+
22
+ print(f"Balance: {account.get_balance()}")
23
+ print(f"Leverage: {account.get_leverage()}")
24
+ print(f"Account type: {account.account_type}")
25
+ print(f"Broker name: {account.broker_name}")
26
+ ```
27
+
28
+ ## Account Discovery
29
+
30
+ To discover available accounts for an access token, use the auth manager:
31
+
32
+ ```python
33
+ # Get all accounts associated with a token
34
+ accounts = await client.auth.get_accounts(access_token)
35
+
36
+ for acc in accounts:
37
+ print(f"Login: {acc.trader_login}, Account ID: {acc.account_id}")
38
+ print(f" Live: {acc.is_live}, Broker: {acc.broker_name}")
39
+ ```
40
+
41
+ ## Related
42
+
43
+ - [Authentication](client.md#authentication) - Authenticating accounts
44
+ - [Models - Account](models.md#account) - Account model reference
@@ -0,0 +1,52 @@
1
+ # Client
2
+
3
+ The main entry point for interacting with the cTrader API.
4
+
5
+ ## CTraderClient
6
+
7
+ ::: ctrader_api_client.CTraderClient
8
+ options:
9
+ show_source: false
10
+ members:
11
+ - __init__
12
+ - connect
13
+ - close
14
+ - "on"
15
+ - "off"
16
+ - auth
17
+ - accounts
18
+ - symbols
19
+ - trading
20
+ - market_data
21
+ - is_connected
22
+ - protocol
23
+
24
+ ## ClientConfig
25
+
26
+ ::: ctrader_api_client.ClientConfig
27
+ options:
28
+ show_source: false
29
+
30
+ ## Authentication
31
+
32
+ The `client.auth` property provides access to authentication operations.
33
+
34
+ ::: ctrader_api_client.auth.AuthManager
35
+ options:
36
+ show_source: false
37
+ members:
38
+ - authenticate_app
39
+ - authenticate_account
40
+ - authenticate_by_trader_login
41
+ - get_accounts
42
+ - resolve_account_id
43
+ - get_credentials
44
+ - remove_account
45
+ - is_app_authenticated
46
+ - authenticated_accounts
47
+
48
+ ## AccountCredentials
49
+
50
+ ::: ctrader_api_client.auth.AccountCredentials
51
+ options:
52
+ show_source: false