tickerall 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.
- tickerall-0.1.0/.gitignore +9 -0
- tickerall-0.1.0/LICENSE +21 -0
- tickerall-0.1.0/PKG-INFO +227 -0
- tickerall-0.1.0/README.md +196 -0
- tickerall-0.1.0/pyproject.toml +58 -0
- tickerall-0.1.0/tickerall/__init__.py +80 -0
- tickerall-0.1.0/tickerall/client.py +341 -0
- tickerall-0.1.0/tickerall/errors.py +138 -0
- tickerall-0.1.0/tickerall/namespaces/__init__.py +1 -0
- tickerall-0.1.0/tickerall/namespaces/accounts.py +56 -0
- tickerall-0.1.0/tickerall/namespaces/candles.py +42 -0
- tickerall-0.1.0/tickerall/namespaces/history.py +68 -0
- tickerall-0.1.0/tickerall/namespaces/orders.py +62 -0
- tickerall-0.1.0/tickerall/namespaces/positions.py +74 -0
- tickerall-0.1.0/tickerall/namespaces/sessions.py +130 -0
- tickerall-0.1.0/tickerall/namespaces/stream.py +334 -0
- tickerall-0.1.0/tickerall/py.typed +0 -0
- tickerall-0.1.0/tickerall/types.py +411 -0
tickerall-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Miguel Santos
|
|
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.
|
tickerall-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tickerall
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Official Python client for the TickerAll REST + WebSocket API — place trades, stream live market data, and manage broker sessions without an MT4/MT5 terminal in the path.
|
|
5
|
+
Project-URL: Homepage, https://tickerall.com
|
|
6
|
+
Project-URL: Documentation, https://tickerall.com/docs
|
|
7
|
+
Project-URL: Repository, https://github.com/TickerAll/tickerall
|
|
8
|
+
Author: Miguel Santos
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: algo,api,broker,forex,mt4,mt5,sdk,tickerall,trading,websocket
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: Intended Audience :: Financial and Insurance Industry
|
|
15
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Topic :: Office/Business :: Financial :: Investment
|
|
22
|
+
Classifier: Typing :: Typed
|
|
23
|
+
Requires-Python: >=3.9
|
|
24
|
+
Requires-Dist: httpx>=0.25
|
|
25
|
+
Requires-Dist: websocket-client>=1.6
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: mypy>=1.8; extra == 'dev'
|
|
28
|
+
Requires-Dist: pytest>=7.4; extra == 'dev'
|
|
29
|
+
Requires-Dist: ruff>=0.4; extra == 'dev'
|
|
30
|
+
Description-Content-Type: text/markdown
|
|
31
|
+
|
|
32
|
+
# tickerall
|
|
33
|
+
|
|
34
|
+
Official Python client for the [TickerAll](https://tickerall.com) REST + WebSocket API.
|
|
35
|
+
|
|
36
|
+
Place trades, stream live market data, and manage broker sessions programmatically — **without an MT4/MT5 terminal in the path**. No Windows VM, no Wine, no `MetaTrader5` terminal to babysit, no thread-safety workarounds.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pip install tickerall
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
Requires Python 3.9+. Depends only on [`httpx`](https://www.python-httpx.org/) and [`websocket-client`](https://github.com/websocket-client/websocket-client).
|
|
43
|
+
|
|
44
|
+
## Why
|
|
45
|
+
|
|
46
|
+
The official `MetaTrader5` Python package only runs on Windows, drives a local terminal over a single-threaded IPC channel, and falls over under concurrency. TickerAll hosts the broker connection for you and exposes it as a clean HTTP + WebSocket API, so your bot can run anywhere — Linux, macOS, a container, a Raspberry Pi — and stream ticks instead of polling.
|
|
47
|
+
|
|
48
|
+
| | `MetaTrader5` (local terminal) | `tickerall` |
|
|
49
|
+
|---|---|---|
|
|
50
|
+
| OS | Windows only | anywhere Python runs |
|
|
51
|
+
| Live ticks | poll `symbol_info_tick()` per symbol | push over WebSocket |
|
|
52
|
+
| Concurrency | single-threaded IPC, not thread-safe | stateless HTTP, thread-safe |
|
|
53
|
+
| Deploy | a terminal per account to babysit | `pip install` |
|
|
54
|
+
|
|
55
|
+
## Quickstart
|
|
56
|
+
|
|
57
|
+
```python
|
|
58
|
+
from tickerall import Tickerall
|
|
59
|
+
|
|
60
|
+
client = Tickerall(api_key="cf_live_...")
|
|
61
|
+
|
|
62
|
+
# Connect a broker account → get a TickerAll account_id
|
|
63
|
+
session = client.sessions.start(
|
|
64
|
+
broker="mt5",
|
|
65
|
+
server="Exness-MT5Trial14",
|
|
66
|
+
account=415724042,
|
|
67
|
+
password="...",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
# Place a market order
|
|
71
|
+
order = client.orders.place(
|
|
72
|
+
session.account_id,
|
|
73
|
+
type="market",
|
|
74
|
+
symbol="BTCUSDm",
|
|
75
|
+
side="BUY",
|
|
76
|
+
volume=0.10,
|
|
77
|
+
stop_loss=58000.0,
|
|
78
|
+
take_profit=72000.0,
|
|
79
|
+
)
|
|
80
|
+
print(order.ticket, order.status)
|
|
81
|
+
|
|
82
|
+
client.sessions.end(session.account_id)
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
The client is a context manager too:
|
|
86
|
+
|
|
87
|
+
```python
|
|
88
|
+
with Tickerall(api_key="cf_live_...") as client:
|
|
89
|
+
...
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
## Streaming — push, not poll
|
|
93
|
+
|
|
94
|
+
The stream runs on its own background thread. Register callbacks and go; it
|
|
95
|
+
heartbeats, reconnects with backoff, and re-subscribes automatically.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
client = Tickerall(api_key="cf_live_...")
|
|
99
|
+
session = client.sessions.start(broker="mt5", server="Exness-MT5Trial14",
|
|
100
|
+
account=415724042, password="...")
|
|
101
|
+
|
|
102
|
+
stream = client.stream.connect()
|
|
103
|
+
stream.on("tick", lambda e: print(e.symbol, e.bid, e.ask, e.timestamp))
|
|
104
|
+
stream.on("position", lambda e: print(e.event, e.position.ticket, e.position.profit))
|
|
105
|
+
stream.subscribe_ticks(session.account_id, ["BTCUSDm", "ETHUSDm"])
|
|
106
|
+
stream.subscribe_positions(session.account_id)
|
|
107
|
+
|
|
108
|
+
# ... your app runs ...
|
|
109
|
+
stream.close()
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Keep an in-memory tick cache fresh (zero polling)
|
|
113
|
+
|
|
114
|
+
A common pattern: let the WebSocket fill a dict so price reads are O(1) with no
|
|
115
|
+
network call — strictly better than polling a terminal per symbol.
|
|
116
|
+
|
|
117
|
+
```python
|
|
118
|
+
latest: dict[str, "TickEvent"] = {}
|
|
119
|
+
stream = client.stream.connect()
|
|
120
|
+
stream.on("tick", lambda e: latest.__setitem__(e.symbol, e))
|
|
121
|
+
stream.subscribe_ticks(session.account_id, ["BTCUSDm", "ETHUSDm", "XAUUSDm"])
|
|
122
|
+
|
|
123
|
+
# Anywhere in your app — instant, no IPC, no thread-safety dance:
|
|
124
|
+
tick = latest.get("BTCUSDm")
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Market data & history
|
|
128
|
+
|
|
129
|
+
```python
|
|
130
|
+
# Historical OHLC candles (coarser timeframes reach further back)
|
|
131
|
+
bars = client.candles.get(session.account_id, symbol="BTCUSDm", hours=24, timeframe="M5")
|
|
132
|
+
for c in bars:
|
|
133
|
+
print(c.timestamp, c.open, c.high, c.low, c.close)
|
|
134
|
+
|
|
135
|
+
# Closed-trade history (recent broker window)
|
|
136
|
+
trades = client.history.get(session.account_id, symbol="BTCUSDm", limit=100)
|
|
137
|
+
|
|
138
|
+
# Tradeable symbols and their volume specs
|
|
139
|
+
symbols = client.accounts.symbols(session.account_id)
|
|
140
|
+
specs = client.accounts.symbol_specs(session.account_id) # min / max / step per symbol
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Positions
|
|
144
|
+
|
|
145
|
+
```python
|
|
146
|
+
detail = client.accounts.get(session.account_id)
|
|
147
|
+
for p in detail.positions:
|
|
148
|
+
print(p.ticket, p.symbol, p.side, p.volume, p.profit)
|
|
149
|
+
|
|
150
|
+
client.positions.modify(session.account_id, ticket=p.ticket, stop_loss=60000.0)
|
|
151
|
+
client.positions.close(session.account_id, ticket=p.ticket) # full close
|
|
152
|
+
client.positions.close(session.account_id, ticket=p.ticket, volume=0.05) # partial
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Always-hot sessions & transparent re-arm
|
|
156
|
+
|
|
157
|
+
For connections that must stay up across restarts, use `keep_alive`. The
|
|
158
|
+
credentials live in this process's memory only (never persisted); if the
|
|
159
|
+
account goes cold (e.g. TickerAll restarted), the next call transparently
|
|
160
|
+
re-supplies them and retries once.
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
session = client.sessions.keep_alive(broker="mt5", server="Exness-MT5Trial14",
|
|
164
|
+
account=415724042, password="...")
|
|
165
|
+
# ... later, after an outage, this just works — the client re-arms under the hood:
|
|
166
|
+
client.accounts.get(session.account_id)
|
|
167
|
+
|
|
168
|
+
# Stop keeping it alive (drops the cached credentials):
|
|
169
|
+
client.sessions.stop_keep_alive(session.account_id)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Reliability — idempotency & queue-and-replay
|
|
173
|
+
|
|
174
|
+
State-changing calls (`sessions.start`, `orders.place`, `positions.close`,
|
|
175
|
+
`positions.modify`) carry a stable **Idempotency-Key**, so a retried call can't
|
|
176
|
+
double-execute. By default a transient connectivity failure
|
|
177
|
+
(`TickerallServiceUnavailableError`, `.transient == True`) **fails fast** so you
|
|
178
|
+
can re-decide with fresh prices:
|
|
179
|
+
|
|
180
|
+
```python
|
|
181
|
+
from tickerall import TickerallServiceUnavailableError
|
|
182
|
+
|
|
183
|
+
try:
|
|
184
|
+
client.orders.place(account_id, type="market", symbol="BTCUSDm", side="BUY", volume=0.1)
|
|
185
|
+
except TickerallServiceUnavailableError:
|
|
186
|
+
... # momentary blip — safe to retry
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
For price-insensitive orders (pending orders, SL/TP edits) you can instead
|
|
190
|
+
**queue-and-replay** until connectivity returns:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
client.orders.place(
|
|
194
|
+
account_id, type="limit", symbol="BTCUSDm", side="BUY", volume=0.1, price=60000.0,
|
|
195
|
+
queue_if_reconnecting=True, queue_max_s=60.0,
|
|
196
|
+
)
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Errors
|
|
200
|
+
|
|
201
|
+
All errors derive from `TickerallApiError` and carry `.status`, `.code`,
|
|
202
|
+
`.request_id`, `.details`, and `.transient`:
|
|
203
|
+
|
|
204
|
+
| Class | When |
|
|
205
|
+
|---|---|
|
|
206
|
+
| `TickerallAuthError` | 401 — bad/missing API key |
|
|
207
|
+
| `TickerallForbiddenError` | 403 — plan limit / reserved resource |
|
|
208
|
+
| `TickerallValidationError` | 400 / 422 — malformed request |
|
|
209
|
+
| `TickerallNotFoundError` | 404 — account / position not found |
|
|
210
|
+
| `TickerallBrokerError` | broker rejected or could not satisfy the request |
|
|
211
|
+
| `TickerallServiceUnavailableError` | transient — TickerAll momentarily unreachable (safe to retry) |
|
|
212
|
+
|
|
213
|
+
## Using it from an async app
|
|
214
|
+
|
|
215
|
+
REST methods are synchronous and thread-safe, so call them from an event loop
|
|
216
|
+
via `asyncio.to_thread`:
|
|
217
|
+
|
|
218
|
+
```python
|
|
219
|
+
detail = await asyncio.to_thread(client.accounts.get, account_id)
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
The stream is already non-blocking (its own thread) — callbacks fire as events
|
|
223
|
+
arrive.
|
|
224
|
+
|
|
225
|
+
## License
|
|
226
|
+
|
|
227
|
+
MIT © Miguel Santos
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
# tickerall
|
|
2
|
+
|
|
3
|
+
Official Python client for the [TickerAll](https://tickerall.com) REST + WebSocket API.
|
|
4
|
+
|
|
5
|
+
Place trades, stream live market data, and manage broker sessions programmatically — **without an MT4/MT5 terminal in the path**. No Windows VM, no Wine, no `MetaTrader5` terminal to babysit, no thread-safety workarounds.
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install tickerall
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Requires Python 3.9+. Depends only on [`httpx`](https://www.python-httpx.org/) and [`websocket-client`](https://github.com/websocket-client/websocket-client).
|
|
12
|
+
|
|
13
|
+
## Why
|
|
14
|
+
|
|
15
|
+
The official `MetaTrader5` Python package only runs on Windows, drives a local terminal over a single-threaded IPC channel, and falls over under concurrency. TickerAll hosts the broker connection for you and exposes it as a clean HTTP + WebSocket API, so your bot can run anywhere — Linux, macOS, a container, a Raspberry Pi — and stream ticks instead of polling.
|
|
16
|
+
|
|
17
|
+
| | `MetaTrader5` (local terminal) | `tickerall` |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| OS | Windows only | anywhere Python runs |
|
|
20
|
+
| Live ticks | poll `symbol_info_tick()` per symbol | push over WebSocket |
|
|
21
|
+
| Concurrency | single-threaded IPC, not thread-safe | stateless HTTP, thread-safe |
|
|
22
|
+
| Deploy | a terminal per account to babysit | `pip install` |
|
|
23
|
+
|
|
24
|
+
## Quickstart
|
|
25
|
+
|
|
26
|
+
```python
|
|
27
|
+
from tickerall import Tickerall
|
|
28
|
+
|
|
29
|
+
client = Tickerall(api_key="cf_live_...")
|
|
30
|
+
|
|
31
|
+
# Connect a broker account → get a TickerAll account_id
|
|
32
|
+
session = client.sessions.start(
|
|
33
|
+
broker="mt5",
|
|
34
|
+
server="Exness-MT5Trial14",
|
|
35
|
+
account=415724042,
|
|
36
|
+
password="...",
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
# Place a market order
|
|
40
|
+
order = client.orders.place(
|
|
41
|
+
session.account_id,
|
|
42
|
+
type="market",
|
|
43
|
+
symbol="BTCUSDm",
|
|
44
|
+
side="BUY",
|
|
45
|
+
volume=0.10,
|
|
46
|
+
stop_loss=58000.0,
|
|
47
|
+
take_profit=72000.0,
|
|
48
|
+
)
|
|
49
|
+
print(order.ticket, order.status)
|
|
50
|
+
|
|
51
|
+
client.sessions.end(session.account_id)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The client is a context manager too:
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
with Tickerall(api_key="cf_live_...") as client:
|
|
58
|
+
...
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
## Streaming — push, not poll
|
|
62
|
+
|
|
63
|
+
The stream runs on its own background thread. Register callbacks and go; it
|
|
64
|
+
heartbeats, reconnects with backoff, and re-subscribes automatically.
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
client = Tickerall(api_key="cf_live_...")
|
|
68
|
+
session = client.sessions.start(broker="mt5", server="Exness-MT5Trial14",
|
|
69
|
+
account=415724042, password="...")
|
|
70
|
+
|
|
71
|
+
stream = client.stream.connect()
|
|
72
|
+
stream.on("tick", lambda e: print(e.symbol, e.bid, e.ask, e.timestamp))
|
|
73
|
+
stream.on("position", lambda e: print(e.event, e.position.ticket, e.position.profit))
|
|
74
|
+
stream.subscribe_ticks(session.account_id, ["BTCUSDm", "ETHUSDm"])
|
|
75
|
+
stream.subscribe_positions(session.account_id)
|
|
76
|
+
|
|
77
|
+
# ... your app runs ...
|
|
78
|
+
stream.close()
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Keep an in-memory tick cache fresh (zero polling)
|
|
82
|
+
|
|
83
|
+
A common pattern: let the WebSocket fill a dict so price reads are O(1) with no
|
|
84
|
+
network call — strictly better than polling a terminal per symbol.
|
|
85
|
+
|
|
86
|
+
```python
|
|
87
|
+
latest: dict[str, "TickEvent"] = {}
|
|
88
|
+
stream = client.stream.connect()
|
|
89
|
+
stream.on("tick", lambda e: latest.__setitem__(e.symbol, e))
|
|
90
|
+
stream.subscribe_ticks(session.account_id, ["BTCUSDm", "ETHUSDm", "XAUUSDm"])
|
|
91
|
+
|
|
92
|
+
# Anywhere in your app — instant, no IPC, no thread-safety dance:
|
|
93
|
+
tick = latest.get("BTCUSDm")
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Market data & history
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
# Historical OHLC candles (coarser timeframes reach further back)
|
|
100
|
+
bars = client.candles.get(session.account_id, symbol="BTCUSDm", hours=24, timeframe="M5")
|
|
101
|
+
for c in bars:
|
|
102
|
+
print(c.timestamp, c.open, c.high, c.low, c.close)
|
|
103
|
+
|
|
104
|
+
# Closed-trade history (recent broker window)
|
|
105
|
+
trades = client.history.get(session.account_id, symbol="BTCUSDm", limit=100)
|
|
106
|
+
|
|
107
|
+
# Tradeable symbols and their volume specs
|
|
108
|
+
symbols = client.accounts.symbols(session.account_id)
|
|
109
|
+
specs = client.accounts.symbol_specs(session.account_id) # min / max / step per symbol
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
## Positions
|
|
113
|
+
|
|
114
|
+
```python
|
|
115
|
+
detail = client.accounts.get(session.account_id)
|
|
116
|
+
for p in detail.positions:
|
|
117
|
+
print(p.ticket, p.symbol, p.side, p.volume, p.profit)
|
|
118
|
+
|
|
119
|
+
client.positions.modify(session.account_id, ticket=p.ticket, stop_loss=60000.0)
|
|
120
|
+
client.positions.close(session.account_id, ticket=p.ticket) # full close
|
|
121
|
+
client.positions.close(session.account_id, ticket=p.ticket, volume=0.05) # partial
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Always-hot sessions & transparent re-arm
|
|
125
|
+
|
|
126
|
+
For connections that must stay up across restarts, use `keep_alive`. The
|
|
127
|
+
credentials live in this process's memory only (never persisted); if the
|
|
128
|
+
account goes cold (e.g. TickerAll restarted), the next call transparently
|
|
129
|
+
re-supplies them and retries once.
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
session = client.sessions.keep_alive(broker="mt5", server="Exness-MT5Trial14",
|
|
133
|
+
account=415724042, password="...")
|
|
134
|
+
# ... later, after an outage, this just works — the client re-arms under the hood:
|
|
135
|
+
client.accounts.get(session.account_id)
|
|
136
|
+
|
|
137
|
+
# Stop keeping it alive (drops the cached credentials):
|
|
138
|
+
client.sessions.stop_keep_alive(session.account_id)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Reliability — idempotency & queue-and-replay
|
|
142
|
+
|
|
143
|
+
State-changing calls (`sessions.start`, `orders.place`, `positions.close`,
|
|
144
|
+
`positions.modify`) carry a stable **Idempotency-Key**, so a retried call can't
|
|
145
|
+
double-execute. By default a transient connectivity failure
|
|
146
|
+
(`TickerallServiceUnavailableError`, `.transient == True`) **fails fast** so you
|
|
147
|
+
can re-decide with fresh prices:
|
|
148
|
+
|
|
149
|
+
```python
|
|
150
|
+
from tickerall import TickerallServiceUnavailableError
|
|
151
|
+
|
|
152
|
+
try:
|
|
153
|
+
client.orders.place(account_id, type="market", symbol="BTCUSDm", side="BUY", volume=0.1)
|
|
154
|
+
except TickerallServiceUnavailableError:
|
|
155
|
+
... # momentary blip — safe to retry
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
For price-insensitive orders (pending orders, SL/TP edits) you can instead
|
|
159
|
+
**queue-and-replay** until connectivity returns:
|
|
160
|
+
|
|
161
|
+
```python
|
|
162
|
+
client.orders.place(
|
|
163
|
+
account_id, type="limit", symbol="BTCUSDm", side="BUY", volume=0.1, price=60000.0,
|
|
164
|
+
queue_if_reconnecting=True, queue_max_s=60.0,
|
|
165
|
+
)
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
## Errors
|
|
169
|
+
|
|
170
|
+
All errors derive from `TickerallApiError` and carry `.status`, `.code`,
|
|
171
|
+
`.request_id`, `.details`, and `.transient`:
|
|
172
|
+
|
|
173
|
+
| Class | When |
|
|
174
|
+
|---|---|
|
|
175
|
+
| `TickerallAuthError` | 401 — bad/missing API key |
|
|
176
|
+
| `TickerallForbiddenError` | 403 — plan limit / reserved resource |
|
|
177
|
+
| `TickerallValidationError` | 400 / 422 — malformed request |
|
|
178
|
+
| `TickerallNotFoundError` | 404 — account / position not found |
|
|
179
|
+
| `TickerallBrokerError` | broker rejected or could not satisfy the request |
|
|
180
|
+
| `TickerallServiceUnavailableError` | transient — TickerAll momentarily unreachable (safe to retry) |
|
|
181
|
+
|
|
182
|
+
## Using it from an async app
|
|
183
|
+
|
|
184
|
+
REST methods are synchronous and thread-safe, so call them from an event loop
|
|
185
|
+
via `asyncio.to_thread`:
|
|
186
|
+
|
|
187
|
+
```python
|
|
188
|
+
detail = await asyncio.to_thread(client.accounts.get, account_id)
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
The stream is already non-blocking (its own thread) — callbacks fire as events
|
|
192
|
+
arrive.
|
|
193
|
+
|
|
194
|
+
## License
|
|
195
|
+
|
|
196
|
+
MIT © Miguel Santos
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling>=1.21"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "tickerall"
|
|
7
|
+
version = "0.1.0"
|
|
8
|
+
description = "Official Python client for the TickerAll REST + WebSocket API — place trades, stream live market data, and manage broker sessions without an MT4/MT5 terminal in the path."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
authors = [{ name = "Miguel Santos" }]
|
|
12
|
+
requires-python = ">=3.9"
|
|
13
|
+
keywords = ["tickerall", "mt4", "mt5", "trading", "forex", "algo", "broker", "api", "sdk", "websocket"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Development Status :: 4 - Beta",
|
|
16
|
+
"Intended Audience :: Developers",
|
|
17
|
+
"Intended Audience :: Financial and Insurance Industry",
|
|
18
|
+
"License :: OSI Approved :: MIT License",
|
|
19
|
+
"Programming Language :: Python :: 3",
|
|
20
|
+
"Programming Language :: Python :: 3.9",
|
|
21
|
+
"Programming Language :: Python :: 3.10",
|
|
22
|
+
"Programming Language :: Python :: 3.11",
|
|
23
|
+
"Programming Language :: Python :: 3.12",
|
|
24
|
+
"Topic :: Office/Business :: Financial :: Investment",
|
|
25
|
+
"Typing :: Typed",
|
|
26
|
+
]
|
|
27
|
+
dependencies = [
|
|
28
|
+
"httpx>=0.25",
|
|
29
|
+
"websocket-client>=1.6",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://tickerall.com"
|
|
34
|
+
Documentation = "https://tickerall.com/docs"
|
|
35
|
+
Repository = "https://github.com/TickerAll/tickerall"
|
|
36
|
+
|
|
37
|
+
[project.optional-dependencies]
|
|
38
|
+
dev = [
|
|
39
|
+
"pytest>=7.4",
|
|
40
|
+
"mypy>=1.8",
|
|
41
|
+
"ruff>=0.4",
|
|
42
|
+
]
|
|
43
|
+
|
|
44
|
+
[tool.hatch.build.targets.wheel]
|
|
45
|
+
packages = ["tickerall"]
|
|
46
|
+
|
|
47
|
+
[tool.hatch.build.targets.sdist]
|
|
48
|
+
include = ["tickerall", "README.md", "LICENSE"]
|
|
49
|
+
|
|
50
|
+
[tool.pytest.ini_options]
|
|
51
|
+
testpaths = ["tests"]
|
|
52
|
+
|
|
53
|
+
# The SDK supports Python 3.9+; this is only the floor the type-checker runs at
|
|
54
|
+
# (mypy requires 3.10+ for its own analysis).
|
|
55
|
+
[tool.mypy]
|
|
56
|
+
python_version = "3.10"
|
|
57
|
+
strict = true
|
|
58
|
+
warn_unused_ignores = false
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"""TickerAll — official Python client for the TickerAll REST + WebSocket API.
|
|
2
|
+
|
|
3
|
+
Place trades, stream live market data, and manage broker sessions
|
|
4
|
+
programmatically — without an MT4/MT5 terminal in the path.
|
|
5
|
+
|
|
6
|
+
from tickerall import Tickerall
|
|
7
|
+
|
|
8
|
+
client = Tickerall(api_key="cf_live_...")
|
|
9
|
+
result = client.sessions.start(
|
|
10
|
+
broker="mt5", server="Exness-MT5Trial14",
|
|
11
|
+
account=415724042, password="...",
|
|
12
|
+
)
|
|
13
|
+
stream = client.stream.connect()
|
|
14
|
+
stream.on("tick", lambda e: print(e.symbol, e.bid, e.ask))
|
|
15
|
+
stream.subscribe_ticks(result.account_id, ["BTCUSDm"])
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
from .client import Tickerall
|
|
21
|
+
from .errors import (
|
|
22
|
+
TickerallApiError,
|
|
23
|
+
TickerallAuthError,
|
|
24
|
+
TickerallBrokerError,
|
|
25
|
+
TickerallForbiddenError,
|
|
26
|
+
TickerallNotFoundError,
|
|
27
|
+
TickerallServiceUnavailableError,
|
|
28
|
+
TickerallValidationError,
|
|
29
|
+
)
|
|
30
|
+
from .namespaces.stream import TickerallStream
|
|
31
|
+
from .types import (
|
|
32
|
+
AccountDetail,
|
|
33
|
+
AccountEvent,
|
|
34
|
+
AccountInfo,
|
|
35
|
+
AccountListing,
|
|
36
|
+
AccountSnapshot,
|
|
37
|
+
Candle,
|
|
38
|
+
ClosePositionResult,
|
|
39
|
+
HistoryTrade,
|
|
40
|
+
ModifyPositionResult,
|
|
41
|
+
PendingRearmAccount,
|
|
42
|
+
PlaceOrderResult,
|
|
43
|
+
Position,
|
|
44
|
+
PositionEvent,
|
|
45
|
+
SessionStartResult,
|
|
46
|
+
SymbolSpec,
|
|
47
|
+
TickEvent,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
__version__ = "0.1.0"
|
|
51
|
+
|
|
52
|
+
__all__ = [
|
|
53
|
+
"Tickerall",
|
|
54
|
+
"TickerallStream",
|
|
55
|
+
# errors
|
|
56
|
+
"TickerallApiError",
|
|
57
|
+
"TickerallAuthError",
|
|
58
|
+
"TickerallForbiddenError",
|
|
59
|
+
"TickerallValidationError",
|
|
60
|
+
"TickerallNotFoundError",
|
|
61
|
+
"TickerallBrokerError",
|
|
62
|
+
"TickerallServiceUnavailableError",
|
|
63
|
+
# types
|
|
64
|
+
"SessionStartResult",
|
|
65
|
+
"PendingRearmAccount",
|
|
66
|
+
"AccountListing",
|
|
67
|
+
"AccountInfo",
|
|
68
|
+
"AccountDetail",
|
|
69
|
+
"Position",
|
|
70
|
+
"SymbolSpec",
|
|
71
|
+
"Candle",
|
|
72
|
+
"HistoryTrade",
|
|
73
|
+
"PlaceOrderResult",
|
|
74
|
+
"ClosePositionResult",
|
|
75
|
+
"ModifyPositionResult",
|
|
76
|
+
"TickEvent",
|
|
77
|
+
"PositionEvent",
|
|
78
|
+
"AccountEvent",
|
|
79
|
+
"AccountSnapshot",
|
|
80
|
+
]
|