Skip to content

Commit 5ffb052

Browse files
Iniziale implementazione API Directa con supporto simulazione
0 parents  commit 5ffb052

19 files changed

Lines changed: 3215 additions & 0 deletions

.github/workflows/python-tests.yml

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
name: Python Tests
2+
3+
on:
4+
push:
5+
branches: [ main, master ]
6+
pull_request:
7+
branches: [ main, master ]
8+
9+
jobs:
10+
test:
11+
runs-on: ubuntu-latest
12+
strategy:
13+
matrix:
14+
python-version: [3.7, 3.8, 3.9, '3.10']
15+
16+
steps:
17+
- uses: actions/checkout@v2
18+
- name: Set up Python ${{ matrix.python-version }}
19+
uses: actions/setup-python@v2
20+
with:
21+
python-version: ${{ matrix.python-version }}
22+
- name: Install dependencies
23+
run: |
24+
python -m pip install --upgrade pip
25+
pip install pytest pytest-cov
26+
pip install -e .
27+
- name: Test with pytest
28+
run: |
29+
pytest tests/ --cov=directa_api

.gitignore

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Python
2+
__pycache__/
3+
*.py[cod]
4+
*$py.class
5+
*.so
6+
.Python
7+
env/
8+
build/
9+
develop-eggs/
10+
dist/
11+
downloads/
12+
eggs/
13+
.eggs/
14+
lib/
15+
lib64/
16+
parts/
17+
sdist/
18+
var/
19+
*.egg-info/
20+
.installed.cfg
21+
*.egg
22+
.env
23+
.venv
24+
venv/
25+
ENV/
26+
.coverage
27+
.pytest_cache/
28+
29+
# IDE and editor files
30+
.idea/
31+
.vscode/
32+
*.swp
33+
*.swo
34+
*~
35+
.DS_Store
36+
37+
# Local configs
38+
.env.local
39+
.env.development.local
40+
.env.test.local
41+
.env.production.local
42+
43+
# Logs
44+
logs/
45+
*.log
46+
npm-debug.log*
47+
yarn-debug.log*
48+
yarn-error.log*
49+
50+
# Directa specific
51+
*.session
52+
credentials.json

.python-version

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
3.10.4

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2025 Niccolò Salvini
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.

README.md

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
# Directa Trading API
2+
3+
Un wrapper Python per interagire con l'API Trading di Directa SIM tramite la piattaforma Darwin.
4+
5+
## Caratteristiche
6+
7+
- Connessione all'API Trading di Directa SIM (porta 10002)
8+
- Gestione automatica della connessione e monitoraggio dello stato
9+
- Supporto per le operazioni di base (acquisto/vendita, cancellazione ordini)
10+
- Supporto per query di portfolio, disponibilità e informazioni account
11+
- Modalità simulazione per testare operazioni senza utilizzare denaro reale
12+
- Parser dettagliati per le risposte dell'API
13+
14+
## Installazione
15+
16+
```bash
17+
git clone https://github.com/TUOUSERNAME/directa-api-python.git
18+
cd directa-api-python
19+
pip install -e .
20+
```
21+
22+
## Requisiti
23+
24+
- Python 3.7+
25+
- Piattaforma di trading Darwin in esecuzione
26+
27+
## Utilizzo base
28+
29+
```python
30+
from directa_api import DirectaTrading
31+
32+
# Connessione all'API
33+
api = DirectaTrading()
34+
if api.connect():
35+
# Verifica dello stato
36+
status = api.get_darwin_status()
37+
print(f"Stato connessione: {status['data']['connection_status']}")
38+
39+
# Ottieni informazioni account
40+
account = api.get_account_info()
41+
print(f"Codice account: {account['data']['account_code']}")
42+
print(f"Liquidità: {account['data']['liquidity']}")
43+
44+
# Ottieni portfolio
45+
portfolio = api.get_portfolio()
46+
if portfolio['success']:
47+
for position in portfolio['data']:
48+
print(f"{position['symbol']}: {position['quantity_portfolio']} azioni")
49+
50+
# Chiusura connessione
51+
api.disconnect()
52+
```
53+
54+
## Modalità Simulazione
55+
56+
È possibile utilizzare la modalità simulazione per testare operazioni senza utilizzare denaro reale:
57+
58+
```python
59+
# Crea un'istanza in modalità simulazione
60+
api = DirectaTrading(simulation_mode=True)
61+
api.connect()
62+
63+
# Simula acquisto di azioni
64+
order = api.place_order("INTC", "BUY", 100, 50.25)
65+
order_id = order["data"]["order_id"]
66+
67+
# Simula esecuzione dell'ordine
68+
api.simulate_order_execution(order_id, executed_price=50.00)
69+
70+
# Verifica portfolio simulato
71+
portfolio = api.get_portfolio()
72+
print(portfolio)
73+
74+
# Chiusura
75+
api.disconnect()
76+
```
77+
78+
Vedere `examples/simulation_example.py` per un esempio completo.
79+
80+
## Esempi
81+
82+
Nella directory `examples` sono presenti diversi script di esempio:
83+
84+
- `trading_example.py`: Esempio base di utilizzo dell'API
85+
- `simulation_example.py`: Esempio di utilizzo della modalità simulazione
86+
- `raw_socket_test.py`: Test di connessione socket semplice per diagnostica
87+
88+
## Note sull'API Directa
89+
90+
- L'API Trading di Directa è accessibile solo quando la piattaforma Darwin è in esecuzione
91+
- L'API utilizza la porta 10002 per le operazioni di trading
92+
- È necessario avere un account Directa attivo per utilizzare l'API in modalità reale
93+
94+
## Licenza
95+
96+
Questo progetto è distribuito con licenza MIT. Vedere il file `LICENSE` per i dettagli.
97+
98+
## Disclaimer
99+
100+
Questo software è fornito "così com'è", senza garanzie di alcun tipo. L'autore e i contributori non sono responsabili per eventuali perdite finanziarie derivanti dall'uso di questo software. Utilizzare a proprio rischio e pericolo. Non è affiliato ufficialmente a Directa SIM.

directa_api/__init__.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
from directa_api.trading import DirectaTrading
2+
from directa_api.errors import ERROR_CODES, ORDER_STATUS_CODES, get_error_message, get_order_status
3+
4+
__all__ = [
5+
'DirectaTrading',
6+
'ERROR_CODES',
7+
'ORDER_STATUS_CODES',
8+
'get_error_message',
9+
'get_order_status'
10+
]

directa_api/errors.py

Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
"""
2+
Error codes and messages for Directa Trading API
3+
"""
4+
5+
ERROR_CODES = {
6+
"0": "ERR_UNKNOWN - Errore generico",
7+
"1000": "ERR_MAX_SUBSCRIPTION_OVERFLOW - Limite massimo di titoli sottoscritti raggiunto",
8+
"1001": "ERR_ALREADY_SUBSCRIBED - Titolo richiesto già sottoscritto",
9+
"1002": "ERR_EMPTY_LIST - Nessun titolo inviato nel comando",
10+
"1003": "ERR_UNKNOWN_COMMAND - Comando sconosciuto",
11+
"1004": "ERR_COMMAND_NOT_EXECUTED - Comando non eseguito",
12+
"1005": "ERR_NOT_SUBSCRIBED - Errore sottoscrizione",
13+
"1006": "ERR_DARWIN_STOP - Chiusura Darwin in corso",
14+
"1007": "ERR_BAD_SUBSCRIPTION - Errore titolo inesistente",
15+
"1008": "ERR_DATA_UNAVAILABLE - Flusso richiesto non disponibile",
16+
"1009": "ERR_TRADING_CMD_INCOMPLETE - Comando trading non completo",
17+
"1010": "ERR_TRADING_CMD_ERROR - Comando trading errato",
18+
"1011": "ERR_TRADING_UNAVAILABLE - Trading non abilitato",
19+
"1012": "ERR_TRADING_REQUEST_ERROR - Errore immissione ordine",
20+
"1013": "ERR_HISTORYCALL_PARAMS - Errore numero paramentri nel comando",
21+
"1015": "ERR_HISTORYCALL_RANGE_INTRADAY - Errore range per chiamate intraday",
22+
"1016": "ERR_HISTORYCALL_DAY_OR_RANGE - Errore nei giorni o nel range date nel comando inviato",
23+
"1018": "ERR_EMPTY_STOCKLIST - Nessuno strumento nel portafoglio",
24+
"1019": "ERR_EMPTY_ORDERLIST - Nessun ordine presente",
25+
"1020": "ERR_DUPLICATED_ID - ID Ordine duplicato",
26+
"1021": "ERR_INVALID_ORDER_STATE - Stato ordine incongruente con l'operazione richiesta",
27+
"1024": "ERR_TRADING_PUSH_DISCONNECTED - Segnala la disconnessione del trading",
28+
"1025": "ERR_TRADING_PUSH_RECONNECTION_OK - Segnale di riconnessione",
29+
"1026": "ERR_TRADING_PUSH_RELOAD - Segnala il reload del trading",
30+
"1027": "ERR_DATAFEED_DISCONNECTED - Segnala la disconessione del datafeed",
31+
"1028": "ERR_DATAFEED_RELOAD - Segnala il reload del datafeed",
32+
"1030": "ERR_MARKET_UNAVAILABLE - Mercato non abilitato per il ticker richiesto",
33+
"1031": "CONTATTO_NON_ATTIVO - Contatto verso il nostro server di trading scaduto, necessario riavviare l'applicazione",
34+
"1032": "DATAFEED NON ABILITATO - Quotazioni non abilitate"
35+
}
36+
37+
ORDER_STATUS_CODES = {
38+
"2000": "In negoziazione",
39+
"2001": "Errore immissione",
40+
"2002": "In negoziazione dopo conferma ricevuta",
41+
"2003": "Eseguito",
42+
"2004": "Revocato",
43+
"2005": "In attesa di conferma",
44+
"2006": "Modificato"
45+
}
46+
47+
def get_error_message(error_code: str) -> str:
48+
"""
49+
Get the error message for an error code
50+
51+
Args:
52+
error_code: The error code
53+
54+
Returns:
55+
The error message or a generic message if not found
56+
"""
57+
if error_code in ERROR_CODES:
58+
return ERROR_CODES[error_code]
59+
return f"Codice errore sconosciuto: {error_code}"
60+
61+
def get_order_status(status_code: str) -> str:
62+
"""
63+
Get the order status description for a status code
64+
65+
Args:
66+
status_code: The status code
67+
68+
Returns:
69+
The status description or a generic message if not found
70+
"""
71+
if status_code in ORDER_STATUS_CODES:
72+
return ORDER_STATUS_CODES[status_code]
73+
return f"Stato ordine sconosciuto: {status_code}"
74+
75+
def is_error_response(response: str) -> bool:
76+
"""
77+
Check if a response is an error response
78+
79+
Args:
80+
response: The API response
81+
82+
Returns:
83+
True if it's an error response, False otherwise
84+
"""
85+
return response.strip().startswith("ERR;") or response.strip()[0:4].isdigit() and response.strip()[4] == ":"
86+
87+
def parse_error_response(response: str) -> dict:
88+
"""
89+
Parse an error response into a JSON-compatible dictionary
90+
91+
Args:
92+
response: The error response string
93+
94+
Returns:
95+
A dictionary with error information in JSON format
96+
"""
97+
# Handle the format ERR;CMD;ERROR_CODE
98+
if response.strip().startswith("ERR;"):
99+
parts = response.strip().split(';')
100+
if len(parts) >= 3:
101+
error_code = parts[2]
102+
return {
103+
"success": False,
104+
"error_code": error_code,
105+
"error_message": get_error_message(error_code),
106+
"raw_response": response
107+
}
108+
109+
# Handle the format ERROR_CODE:ERROR_MESSAGE
110+
elif response.strip()[0:4].isdigit() and response.strip()[4] == ":":
111+
error_code = response.strip()[0:4]
112+
return {
113+
"success": False,
114+
"error_code": error_code,
115+
"error_message": get_error_message(error_code),
116+
"raw_response": response
117+
}
118+
119+
# Generic error format
120+
return {
121+
"success": False,
122+
"error_code": "unknown",
123+
"error_message": "Formato di errore sconosciuto",
124+
"raw_response": response
125+
}

0 commit comments

Comments
 (0)