ctrader-api-client 0.8.0__tar.gz → 0.8.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.
Potentially problematic release.
This version of ctrader-api-client might be problematic. Click here for more details.
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/PKG-INFO +14 -2
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/README.md +11 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/models.md +20 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/symbols.md +58 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/pyproject.toml +4 -1
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/__init__.py +10 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/exceptions.py +22 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/__init__.py +5 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/symbol.py +7 -0
- ctrader_api_client-0.8.1/src/ctrader_api_client/models/trading_hours.py +506 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/unit/test_symbol_conversion.py +19 -1
- ctrader_api_client-0.8.1/tests/unit/test_trading_hours.py +387 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/uv.lock +12 -1
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/.claude/settings.local.json +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/.github/workflows/docs.yml +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/.gitignore +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/.pre-commit-config.yaml +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/.python-version +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/Justfile +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/LICENSE +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/accounts.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/client.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/enums.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/events.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/exceptions.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/market-data.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/api/trading.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/getting-started.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/docs/index.md +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/mkdocs.yml +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/protos/SOURCE +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/protos/VERSION +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/protos/update.sh +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/protos/vendor/OpenApiCommonMessages.proto +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/protos/vendor/OpenApiCommonModelMessages.proto +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/protos/vendor/OpenApiMessages.proto +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/protos/vendor/OpenApiModelMessages.proto +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/scripts/fix_proto_imports.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/clock.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/conversions.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/messages.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/proto/OpenApiCommonMessages.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/proto/OpenApiCommonModelMessages.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/proto/OpenApiMessages.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/proto/OpenApiModelMessages.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/proto/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/_internal/serialization.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/api/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/api/_base.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/api/accounts.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/api/market_data.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/api/symbols.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/api/trading.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/auth/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/auth/_recovery.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/auth/_refresh.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/auth/_session.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/auth/credentials.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/auth/manager.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/auth/policy.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/auth/store.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/client.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/composition.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/config.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/connection/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/connection/heartbeat.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/connection/listener.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/connection/protocol.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/connection/supervisor.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/connection/transport.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/enums.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/events/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/events/_execution.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/events/emitter.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/events/publisher.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/events/router.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/events/types.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/_base.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/account.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/deal.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/market_data.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/order.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/position.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/requests.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/py.typed +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/api/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/api/conftest.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/api/test_market_data.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/api/test_reference_data.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/api/test_subscription_restore.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/api/test_trading.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/auth/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/auth/conftest.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/auth/test_authentication.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/auth/test_token_lifecycle.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/connection/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/connection/conftest.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/connection/test_event_dispatch.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/connection/test_framing.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/connection/test_heartbeat.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/connection/test_reconnection.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/connection/test_request_response.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/events/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/events/conftest.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/events/test_routing.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/events/test_subscriptions.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/behavior/test_client.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/conftest.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/harness/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/harness/clock.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/harness/factories.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/harness/recorder.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/harness/server.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/harness/signals.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/harness/stub_protocol.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/harness/wire.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/unit/__init__.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/unit/test_api_errors.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/unit/test_market_data_conversion.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/unit/test_request_conversion.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/unit/test_trading_records.py +0 -0
- {ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/tests/unit/test_wire_format.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
2
|
Name: ctrader-api-client
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: API Client to interact with the cTrader Open API spec
|
|
5
5
|
Author-email: Elio <elioachukri@pm.me>
|
|
6
6
|
License-File: LICENSE
|
|
@@ -15,6 +15,7 @@ Requires-Dist: anyio>=4.13.0
|
|
|
15
15
|
Requires-Dist: betterproto[compiler]>=1.2.5
|
|
16
16
|
Requires-Dist: pydantic>=2.12.5
|
|
17
17
|
Requires-Dist: tenacity>=9.1.4
|
|
18
|
+
Requires-Dist: tzdata>=2025.2
|
|
18
19
|
Description-Content-Type: text/markdown
|
|
19
20
|
|
|
20
21
|
# cTrader API Client
|
|
@@ -301,6 +302,17 @@ results = await client.symbols.search(account_id, "EUR")
|
|
|
301
302
|
|
|
302
303
|
# Get specific symbol
|
|
303
304
|
symbol = await client.symbols.get_by_id(account_id, symbol_id)
|
|
305
|
+
|
|
306
|
+
# Trading hours arrive with the symbol, so this costs no extra request
|
|
307
|
+
hours = symbol.trading_hours
|
|
308
|
+
if hours.is_open():
|
|
309
|
+
print(f"Open, closes at {hours.next_close()}")
|
|
310
|
+
else:
|
|
311
|
+
print(f"Closed, opens at {hours.next_open()}")
|
|
312
|
+
|
|
313
|
+
# Sessions for a given day, with broker holidays already applied
|
|
314
|
+
for session in hours.sessions_on(date(2026, 6, 19)):
|
|
315
|
+
print(session.start, "->", session.end)
|
|
304
316
|
```
|
|
305
317
|
|
|
306
318
|
### Account Information
|
|
@@ -282,6 +282,17 @@ results = await client.symbols.search(account_id, "EUR")
|
|
|
282
282
|
|
|
283
283
|
# Get specific symbol
|
|
284
284
|
symbol = await client.symbols.get_by_id(account_id, symbol_id)
|
|
285
|
+
|
|
286
|
+
# Trading hours arrive with the symbol, so this costs no extra request
|
|
287
|
+
hours = symbol.trading_hours
|
|
288
|
+
if hours.is_open():
|
|
289
|
+
print(f"Open, closes at {hours.next_close()}")
|
|
290
|
+
else:
|
|
291
|
+
print(f"Closed, opens at {hours.next_open()}")
|
|
292
|
+
|
|
293
|
+
# Sessions for a given day, with broker holidays already applied
|
|
294
|
+
for session in hours.sessions_on(date(2026, 6, 19)):
|
|
295
|
+
print(session.start, "->", session.end)
|
|
285
296
|
```
|
|
286
297
|
|
|
287
298
|
### Account Information
|
|
@@ -130,6 +130,26 @@ partial_close = ClosePositionRequest(
|
|
|
130
130
|
show_source: false
|
|
131
131
|
|
|
132
132
|
|
|
133
|
+
::: ctrader_api_client.models.TradingHours
|
|
134
|
+
options:
|
|
135
|
+
show_source: false
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
::: ctrader_api_client.models.TradingSession
|
|
139
|
+
options:
|
|
140
|
+
show_source: false
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
::: ctrader_api_client.models.ScheduleInterval
|
|
144
|
+
options:
|
|
145
|
+
show_source: false
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
::: ctrader_api_client.models.Holiday
|
|
149
|
+
options:
|
|
150
|
+
show_source: false
|
|
151
|
+
|
|
152
|
+
|
|
133
153
|
::: ctrader_api_client.models.Account
|
|
134
154
|
options:
|
|
135
155
|
show_source: false
|
|
@@ -58,6 +58,64 @@ for sym in eur_pairs:
|
|
|
58
58
|
print(sym.name)
|
|
59
59
|
```
|
|
60
60
|
|
|
61
|
+
## Trading Hours
|
|
62
|
+
|
|
63
|
+
Every `Symbol` arrives with its trading calendar attached, so asking when a symbol is open
|
|
64
|
+
costs no extra request:
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from datetime import date
|
|
68
|
+
|
|
69
|
+
symbol = await client.symbols.get_by_id(account_id, 270)
|
|
70
|
+
hours = symbol.trading_hours
|
|
71
|
+
|
|
72
|
+
if hours.is_open():
|
|
73
|
+
print("open now, closes at", hours.next_close())
|
|
74
|
+
else:
|
|
75
|
+
print("closed, opens at", hours.next_open())
|
|
76
|
+
|
|
77
|
+
for session in hours.sessions_on(date(2026, 6, 19)):
|
|
78
|
+
print(session.start, "->", session.end, f"({session.duration})")
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Sessions are reported in the symbol's own `schedule_timezone` and are never merged, so a
|
|
82
|
+
schedule with a nightly maintenance break reports one session either side of it.
|
|
83
|
+
|
|
84
|
+
### Holidays
|
|
85
|
+
|
|
86
|
+
Holidays are closed windows that cut into the weekly schedule rather than replacing a whole
|
|
87
|
+
day. A broker publishing an "early close" sends a window covering the rest of the day, which
|
|
88
|
+
brings the close forward:
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# Regular Friday: 01:05 - 23:50. With a 20:00 early close holiday:
|
|
92
|
+
hours.sessions_on(date(2026, 6, 19)) # -> one session, 01:05 - 20:00
|
|
93
|
+
|
|
94
|
+
hours.holidays_on(date(2026, 6, 19)) # -> the holiday responsible
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
A holiday carries **its own** timezone, which is not always the symbol's. A closure defined in
|
|
98
|
+
a timezone an hour behind the symbol's straddles two of the symbol's calendar days, and both
|
|
99
|
+
are shortened accordingly.
|
|
100
|
+
|
|
101
|
+
A holiday whose window is empty (`start_second >= end_second`, which brokers send as `0`/`0`)
|
|
102
|
+
closes the entire day.
|
|
103
|
+
|
|
104
|
+
### Trading mode is separate
|
|
105
|
+
|
|
106
|
+
`is_open()` answers the calendar question only. A symbol can be closed to trading by its
|
|
107
|
+
`trading_mode` while the schedule says it is open, so check both:
|
|
108
|
+
|
|
109
|
+
```python
|
|
110
|
+
tradable = hours.is_open() and symbol.trading_mode == TradingMode.ENABLED
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Staleness
|
|
114
|
+
|
|
115
|
+
Schedules arrive only with a symbol fetch, so a long-lived `Symbol` can drift. Subscribe to
|
|
116
|
+
`SymbolChangedEvent` and re-fetch when the server reports a change. The API exposes no
|
|
117
|
+
historical schedules — what you receive is the current configuration.
|
|
118
|
+
|
|
61
119
|
## Volume Conversion
|
|
62
120
|
|
|
63
121
|
The cTrader API uses volume in "cents" (smallest volume units). The relationship between lots and volume depends on the symbol's `lot_size`:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "ctrader-api-client"
|
|
3
|
-
version = "0.8.
|
|
3
|
+
version = "0.8.1"
|
|
4
4
|
description = "API Client to interact with the cTrader Open API spec"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
authors = [
|
|
@@ -20,6 +20,9 @@ dependencies = [
|
|
|
20
20
|
"betterproto[compiler]>=1.2.5",
|
|
21
21
|
"pydantic>=2.12.5",
|
|
22
22
|
"tenacity>=9.1.4",
|
|
23
|
+
# Windows ships no system IANA database, which ZoneInfo needs to resolve the
|
|
24
|
+
# broker-supplied timezones that trading schedules are expressed in.
|
|
25
|
+
"tzdata>=2025.2",
|
|
23
26
|
]
|
|
24
27
|
|
|
25
28
|
[build-system]
|
|
@@ -99,6 +99,7 @@ from .exceptions import (
|
|
|
99
99
|
TokenExpiredError,
|
|
100
100
|
TokenRefreshError,
|
|
101
101
|
UnknownPayloadTypeError,
|
|
102
|
+
UnknownTimezoneError,
|
|
102
103
|
)
|
|
103
104
|
from .models import (
|
|
104
105
|
Account,
|
|
@@ -108,13 +109,17 @@ from .models import (
|
|
|
108
109
|
CloseDetail,
|
|
109
110
|
ClosePositionRequest,
|
|
110
111
|
Deal,
|
|
112
|
+
Holiday,
|
|
111
113
|
NewOrderRequest,
|
|
112
114
|
Order,
|
|
113
115
|
Position,
|
|
114
116
|
PositionUnrealizedPnL,
|
|
117
|
+
ScheduleInterval,
|
|
115
118
|
Symbol,
|
|
116
119
|
SymbolInfo,
|
|
117
120
|
TickData,
|
|
121
|
+
TradingHours,
|
|
122
|
+
TradingSession,
|
|
118
123
|
Trendbar,
|
|
119
124
|
)
|
|
120
125
|
|
|
@@ -154,6 +159,7 @@ __all__ = [
|
|
|
154
159
|
"ExecutionEvent",
|
|
155
160
|
"ExecutionType",
|
|
156
161
|
"FramingError",
|
|
162
|
+
"Holiday",
|
|
157
163
|
"MarginCallTriggerEvent",
|
|
158
164
|
"MarginChangeEvent",
|
|
159
165
|
"NewOrderRequest",
|
|
@@ -170,6 +176,7 @@ __all__ = [
|
|
|
170
176
|
"ReauthPolicy",
|
|
171
177
|
"ReconnectedEvent",
|
|
172
178
|
"RefreshPolicy",
|
|
179
|
+
"ScheduleInterval",
|
|
173
180
|
"SpotEvent",
|
|
174
181
|
"StopTriggerMethod",
|
|
175
182
|
"SubscriptionRestoreFailedEvent",
|
|
@@ -184,9 +191,12 @@ __all__ = [
|
|
|
184
191
|
"TokenRefreshFailedEvent",
|
|
185
192
|
"TokenStore",
|
|
186
193
|
"TraderUpdateEvent",
|
|
194
|
+
"TradingHours",
|
|
187
195
|
"TradingMode",
|
|
196
|
+
"TradingSession",
|
|
188
197
|
"TrailingStopChangedEvent",
|
|
189
198
|
"Trendbar",
|
|
190
199
|
"TrendbarPeriod",
|
|
191
200
|
"UnknownPayloadTypeError",
|
|
201
|
+
"UnknownTimezoneError",
|
|
192
202
|
]
|
|
@@ -255,6 +255,28 @@ class APIError(CTraderError):
|
|
|
255
255
|
)
|
|
256
256
|
|
|
257
257
|
|
|
258
|
+
# =============================================================================
|
|
259
|
+
# Data Errors
|
|
260
|
+
# =============================================================================
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
class UnknownTimezoneError(CTraderError):
|
|
264
|
+
"""A timezone named by the server is not in the IANA database.
|
|
265
|
+
|
|
266
|
+
Trading schedules are broker-supplied wall clock time, so a timezone that
|
|
267
|
+
cannot be resolved leaves every session boundary meaningless. This should
|
|
268
|
+
never happen: if it does, the broker has published a symbol whose schedule
|
|
269
|
+
timezone is misconfigured, and the schedule cannot be interpreted at all.
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
def __init__(self, name: str) -> None:
|
|
273
|
+
self.name = name
|
|
274
|
+
super().__init__(
|
|
275
|
+
f"Unknown schedule timezone {name!r}. This should never happen; it means the broker "
|
|
276
|
+
f"published a symbol whose schedule timezone is not a valid IANA name."
|
|
277
|
+
)
|
|
278
|
+
|
|
279
|
+
|
|
258
280
|
# =============================================================================
|
|
259
281
|
# Protocol Errors
|
|
260
282
|
# =============================================================================
|
{ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/__init__.py
RENAMED
|
@@ -30,6 +30,7 @@ from .order import Order
|
|
|
30
30
|
from .position import Position, PositionUnrealizedPnL
|
|
31
31
|
from .requests import AmendOrderRequest, AmendPositionRequest, ClosePositionRequest, NewOrderRequest
|
|
32
32
|
from .symbol import Symbol, SymbolInfo
|
|
33
|
+
from .trading_hours import Holiday, ScheduleInterval, TradingHours, TradingSession
|
|
33
34
|
|
|
34
35
|
|
|
35
36
|
__all__ = [
|
|
@@ -40,12 +41,16 @@ __all__ = [
|
|
|
40
41
|
"CloseDetail",
|
|
41
42
|
"ClosePositionRequest",
|
|
42
43
|
"Deal",
|
|
44
|
+
"Holiday",
|
|
43
45
|
"NewOrderRequest",
|
|
44
46
|
"Order",
|
|
45
47
|
"Position",
|
|
46
48
|
"PositionUnrealizedPnL",
|
|
49
|
+
"ScheduleInterval",
|
|
47
50
|
"Symbol",
|
|
48
51
|
"SymbolInfo",
|
|
49
52
|
"TickData",
|
|
53
|
+
"TradingHours",
|
|
54
|
+
"TradingSession",
|
|
50
55
|
"Trendbar",
|
|
51
56
|
]
|
{ctrader_api_client-0.8.0 → ctrader_api_client-0.8.1}/src/ctrader_api_client/models/symbol.py
RENAMED
|
@@ -3,9 +3,12 @@ from __future__ import annotations
|
|
|
3
3
|
from decimal import Decimal
|
|
4
4
|
from typing import TYPE_CHECKING
|
|
5
5
|
|
|
6
|
+
from pydantic import Field
|
|
7
|
+
|
|
6
8
|
from .._internal.proto import ProtoOATradingMode
|
|
7
9
|
from ..enums import TradingMode
|
|
8
10
|
from ._base import FrozenModel
|
|
11
|
+
from .trading_hours import TradingHours
|
|
9
12
|
|
|
10
13
|
|
|
11
14
|
if TYPE_CHECKING:
|
|
@@ -91,6 +94,8 @@ class Symbol(FrozenModel):
|
|
|
91
94
|
tp_distance: Minimum take profit distance.
|
|
92
95
|
schedule_timezone: Timezone for trading schedule.
|
|
93
96
|
measurement_units: Measurement units (e.g., "oz" for gold).
|
|
97
|
+
trading_hours: Weekly schedule and holidays describing when the symbol is
|
|
98
|
+
open. Empty for a symbol the broker publishes without a schedule.
|
|
94
99
|
"""
|
|
95
100
|
|
|
96
101
|
symbol_id: int
|
|
@@ -114,6 +119,7 @@ class Symbol(FrozenModel):
|
|
|
114
119
|
tp_distance: int = 0
|
|
115
120
|
schedule_timezone: str = ""
|
|
116
121
|
measurement_units: str = ""
|
|
122
|
+
trading_hours: TradingHours = Field(default_factory=TradingHours)
|
|
117
123
|
|
|
118
124
|
def volume_to_lots(self, volume_cents: int) -> Decimal:
|
|
119
125
|
"""Convert volume in cents to lots.
|
|
@@ -179,4 +185,5 @@ class Symbol(FrozenModel):
|
|
|
179
185
|
tp_distance=proto.tp_distance if proto.tp_distance else 0,
|
|
180
186
|
schedule_timezone=proto.schedule_time_zone or "",
|
|
181
187
|
measurement_units=proto.measurement_units or "",
|
|
188
|
+
trading_hours=TradingHours.from_proto(proto),
|
|
182
189
|
)
|