primary-api
Python library for connecting to the Primary API (Matba Rofex exchange). Fork/refactor of pyRofex. Provides REST and WebSocket clients for market data, order management, and account reports.
Stack: Python 3.8+, requests, simplejson, websocket-client, pydantic
Installation
pip install primary-api
Quick Start
from primary_api import PrimaryApiClient
from primary_api.models.enums import MarketDataEntry, MarketId
from primary_api.models.market_data import MarketDataMessage, MarketDataResponse
client = PrimaryApiClient(
username="username",
password="password",
account="account123",
rest_url="https://api.remarkets.primary.com.ar/",
ws_url="wss://api.remarkets.primary.com.ar/"
)
# REST — returns typed Pydantic models
segments = client.get_segments()
print(segments.segments[0].market_segment_id)
md: MarketDataResponse = client.get_market_data("DLR/MAY26", [MarketDataEntry.LAST])
print(md.market_data.LA.price)
# WebSocket — handlers receive typed models
def on_md(msg: MarketDataMessage):
print(msg.market_data.LA.price)
sub = client.market_data_subscription(
tickers=["DLR/MAY26"],
entries=[MarketDataEntry.BIDS, MarketDataEntry.OFFERS],
handler=on_md
)
client.wait_for_interrupt()
Models
All API responses are typed Pydantic models with automatic camelCase to snake_case conversion. Every get_* method returns a typed model (not a raw dict).
segments: SegmentsResponse = client.get_segments()
print(segments.segments[0].market_segment_id)
Key models:
| Model |
Description |
SegmentsResponse |
Available market segments |
AllInstrumentsResponse |
All instruments with CFI codes |
InstrumentsByResponse |
Instruments by CFI code or segment |
DetailedInstrumentsResponse |
Full instrument details |
InstrumentDetailsResponse |
Single instrument details |
MarketDataResponse |
Bids, offers, last price, etc. |
OrderStatusResponse |
Order status / list of orders |
OrderConfirmationResponse |
Order send/cancel confirmation |
PositionsResponse |
Account positions |
DetailedPositionsResponse |
Detailed positions by contract type |
AccountReportResponse |
Account balance and report |
TradesResponse |
Trade history |
MarketDataMessage |
Parsed WS market data message (has instrument_id + market_data) |
OrderReportMessage |
Parsed WS order report message (has order_report) |
EnhancedOrderReport |
OrderReport with helper methods (is_open(), is_closed(), etc.) |
pip install primary-api-models
For docs, visit https://pypi.org/project/primary-api-models/
Configuration
Environment
The client auto-detects the environment from the URL. If remarkets is in the URL, it configures is_remarkets = True and uses ISV_PBCP as the proprietary value.
| Endpoint |
URL |
| Remarkets (testing) |
https://api.remarkets.primary.com.ar/ |
| Live production |
Provided by your broker |
Production environment
from primary_api import PrimaryApiClient
client = PrimaryApiClient(
username="matriz-username",
password="matriz-password",
account="matriz-account",
rest_url="https://api.your-broker.xoms.com.ar",
ws_url="wss://api.your-broker.xoms.com.ar"
)
REST API
Instruments
| Method |
Return Type |
Description |
get_segments() |
SegmentsResponse |
Get all available market segments |
get_all_instruments() |
AllInstrumentsResponse |
Get all instruments |
get_detailed_instruments() |
DetailedInstrumentsResponse |
Get detailed instrument list |
get_instrument_details(ticker, market) |
InstrumentDetailsResponse |
Get details for a specific instrument |
get_instruments_by_cfi(cfi_code) |
InstrumentsByResponse |
Filter instruments by CFICode |
get_instruments_by_segment(market_segment, market) |
InstrumentsByResponse |
Filter instruments by MarketSegment |
Market Data
Entries
| Símbolo |
Significado |
Descripción |
| BI |
BIDS |
Mejor oferta de compra en el Book |
| OF |
OFFERS |
Mejor oferta de venta en el Book |
| LA |
LAST |
Último precio operado en el mercado |
| OP |
OPENING PRICE |
Precio de apertura |
| CL |
CLOSING PRICE |
Precio de cierre de la rueda de negociación anterior |
| SE |
SETTLEMENT PRICE |
Precio de ajuste (solo para futuros) |
| HI |
TRADING SESSION HIGH PRICE |
Precio máximo de la rueda |
| LO |
TRADING SESSION LOW PRICE |
Precio mínimo de la rueda |
| TV |
TRADE VOLUME |
Volumen operado en contratos/nominales para ese security |
| OI |
OPEN INTEREST |
Interés abierto (solo para futuros) |
| IV |
INDEX VALUE |
Valor del índice (solo para índices) |
| EV |
TRADE EFFECTIVE VOLUME |
Volumen efectivo de negociación para ese security |
| NV |
NOMINAL VOLUME |
Volumen nominal de negociación para ese security |
| ACP |
AUCTION PRICE |
Precio de cierre del día corriente |
| Method |
Return Type |
Description |
get_market_data(ticker, entries, depth, market) |
MarketDataResponse |
Get market data (bids, offers, last price, etc.) |
Orders
| Method |
Return Type |
Description |
send_order(ticker, size, side, ...) |
OrderConfirmationResponse |
Send a new order (REST) |
send_order_via_websocket(...) |
None |
Send a new order (WebSocket) |
cancel_order(client_order_id, proprietary) |
OrderConfirmationResponse |
Cancel an existing order (REST) |
cancel_order_via_websocket(...) |
None |
Cancel an existing order (WebSocket) |
get_order_status(client_order_id, proprietary) |
OrderStatusResponse |
Check order status by client ID |
get_all_orders_by_id(client_order_id, proprietary) |
OrderStatusResponse |
All orders matching a client ID |
get_order_by_order_id(order_id, proprietary) |
OrderStatusResponse |
Order by server-assigned order ID |
get_order_by_exec_id(exec_id, proprietary) |
OrderStatusResponse |
Order by execution ID |
get_all_orders_status(account) |
OrderStatusResponse |
Get all orders for an account |
get_active_orders(account) |
OrderStatusResponse |
Get active (open) orders |
get_filled_orders(account) |
OrderStatusResponse |
Get filled orders |
Account
| Method |
Return Type |
Description |
get_account_position(account) |
PositionsResponse |
Get account position |
get_detailed_position(account) |
DetailedPositionsResponse |
Get detailed position by contract type |
get_account_report(account) |
AccountReportResponse |
Get account report (balance, margins) |
get_trade_history(ticker, date, date_from, date_to, market) |
TradesResponse |
Get trade history |
Example: Sending an Order
from primary_api.models.enums import Side, OrderType, TimeInForce, MarketId
order = client.send_order(
ticker="DLR/MAY26",
size=10,
order_type=OrderType.LIMIT,
side=Side.BUY,
market=MarketId.ROFX,
time_in_force=TimeInForce.DAY,
price=850.50,
)
print(order.order.client_id)
WebSocket Subscriptions
init_websocket_connection
Set up all handlers and establish the WebSocket connection in one call (pyRofex compatible):
from primary_api.models.market_data import MarketDataMessage
from primary_api.models.order_report import OrderReportMessage
def on_market_data(msg: MarketDataMessage):
print(msg.market_data.LA)
def on_order_report(msg: OrderReportMessage):
print(msg.order_report.cl_ord_id)
def on_error(msg: dict):
print(f"Error: {msg}")
def on_exception(e: Exception):
print(f"Exception: {e}")
client.init_websocket_connection(
market_data_handler=on_market_data,
order_report_handler=on_order_report,
error_handler=on_error,
exception_handler=on_exception,
)
# Handlers are already set — subscribe without passing handler
sub = client.market_data_subscription(tickers=["DLR/MAY26"], entries=[...])
Market Data Subscription
from primary_api.models.market_data import MarketDataMessage
def handle_market_data(msg: MarketDataMessage):
print(msg.market_data.LA.price)
sub = client.market_data_subscription(
tickers=["DLR/MAY26", "DLR/JUN26"],
entries=[
MarketDataEntry.BIDS,
MarketDataEntry.OFFERS,
MarketDataEntry.LAST,
],
depth=5,
handler=handle_market_data
)
print(sub.count()) # 2 tickers
print(sub.to_dict()) # full details
Order Report Subscription
from primary_api.models.order_report import OrderReportMessage
def handle_order_report(msg: OrderReportMessage):
print(msg.order_report.cl_ord_id)
sub = client.order_report_subscription(
handler=handle_order_report
)
client.unsubscribe_order_report()
WebSocket Lifecycle
- First
connect(): raises ApiException if the endpoint is unreachable.
- Auto-reconnect: after a successful connection, if the WebSocket drops, it reconnects automatically in a background thread. All active subscriptions are re-sent on reconnect.
- Parsed messages: incoming WS messages are parsed into typed Pydantic models before reaching handlers —
MarketDataMessage for market data, OrderReportMessage for order reports.
- Cleanup: call
client.close_websocket() to stop the reconnection loop.
- Unsubscribe: use
client.unsubscribe_market_data() / client.unsubscribe_order_report().
Enums Reference
All enums in primary_api.models.enums:
| Enum |
Values |
StatusResponse |
OK, ERROR |
MessageType |
MARKET_DATA, ORDER_REPORT |
MarketId |
ROFX |
Side |
BUY, SELL |
OrderType |
LIMIT, MARKET, STOP_LIMIT, STOP_LIMIT_MERVAL, MARKET_TO_LIMIT, PREVIOUSLY_QUOTED, STOP |
OrderStatus |
PENDING_NEW, PENDING_CANCEL, PENDING_REPLACE, NEW, FILLED, PARTIALLY_FILLED, REPLACED, CANCELED, CANCELLED, EXPIRED, REJECTED |
TimeInForce |
DAY, IOC, FOK, GTD, GTC |
Currency |
ARS, USD, EXT, USG, UYD, UYU |
CurrencyIndex |
ARS, ARS BCRA, EUR, UYU, U$S, USD MtR, USD UY, USD DB, USD G, USD R, USD C, USD D |
SettlType |
T0 (CI), T1 (24hs), T2 (48hs), T3 (72hs), T4 (96hs), T5 (120hs) |
SettlementIndex |
T0–T5 (int 0–5) |
MarketDataEntry |
BIDS, OFFERS, LAST, OPENING_PRICE, CLOSING_PRICE, SETTLEMENT_PRICE, HIGH_PRICE, LOW_PRICE, TRADE_VOLUME, OPEN_INTEREST, INDEX_VALUE, TRADE_EFFECTIVE_VOLUME, NOMINAL_VOLUME, ACP, TRADE_COUNT |
CFICode |
STOCK, BOND, FUTURE, PUT_STOCK, CALL_STOCK, PUT_FUTURE, CALL_FUTURE, CEDEAR, REPO, etc. |
MarketSegment |
MERV, MFCI, DDF, DDA, DUAL, MAE, MATBA, TIVA, U-DDF, U-DDA, U-DUAL, U-DDF-S, U-FIN, U-COMM, U-STOCK, MVR, MVC, MVM, AVS, TEST |
ContractType |
BOND, CEDEAR, FUTURE, NEGOTIABLE_OBLIGATION, OPTION_PUT, OPTION_CALL, STOCK, LETTER_NOTE, DEFAULT, REPURCHASE |
Tests development
Environment variables (for testing)
Create a .env file:
CL_USERNAME=your_user
CL_PASSWORD=your_pass
CL_ACCOUNT=your_account
CL_API_HTTPS=https://api.remarkets.primary.com.ar/
CL_API_WSS=wss://api.remarkets.primary.com.ar/