A connector connects ATAS to a source of market data (a built-in connector, also to a broker). The built-in connectors (Rithmic, CQG, dxFeed, the crypto exchanges and others) are written with the same SDK that is available to you: you can write a connector to your own data feed — a vendor API, a recorded data set, a simulator — and use it in ATAS like any built-in connection. Charts, the SmartDOM, the heatmap, indicators and strategies receive its data exactly as from any other connection.
This article describes a market data connector. Trading through third-party connectors is not supported: the base class also carries the trading API (portfolios, positions, orders) of the built-in connectors, but a connector of your own must be a market data feed only.
Connectors work the same way in the classic ATAS and in ATAS X (see ATAS X and macOS).
A class library (net10.0) that references, from the platform folder and with <Private>false</Private>:
ATAS.DataFeedsCore.dll — the connector SDK (namespace ATAS.DataFeedsCore; the settings base class is in OFT.Core);Utils.Common.dll — the compiler asks for it because the SDK base classes use it. Depending on the SDK members you use, it may also ask for OFT.Attributes.dll and OFT.Localization.dll; reference them the same way.The library contains two public classes:
| Class | Base class | Role |
|---|---|---|
| connection settings | BaseConnectorSettings<TConnector, TSelf> | the connector's name in the «Add connection» dialog, the properties the user edits, saved with the connection |
| connector | BaseConnector<TMessage, TPortfolioKey, TSecurityKey> | connects, finds instruments, publishes trades, best prices and the order book |
The platform finds the settings class (a public non-abstract class with a public parameterless constructor), creates the connector through it and drives the connector through its base class.
| Member | Mandatory | Meaning |
|---|---|---|
[DisplayName] | yes | the connector name in the dialog. It must be unique — see Name rules |
[Description], [Category] | no | the text in the dialog and the dialog group. The category does not affect the connection limits of the tariff (see Tariff restriction) |
Features | yes | ConnectorFeatures.MarketData. Trading through third-party connectors is not supported, so do not add Executions, StopOrders or OcoOrders |
OnApplySettings(connector) | yes | copies the settings to the connector. Called after the connector is created and after every change of the settings |
CompareCredentials(other) | yes | true if both connections use the same account: the platform does not connect two such connections at once. Return false when there are no credentials |
| public properties | no | shown in the connection settings (use [Display], [Range], [Required] from System.ComponentModel.DataAnnotations) and saved with the connection. Use SecureString for passwords |
CheckSupported(out message) | no | return false with a message if the connector cannot run on this machine (for example, a Windows-only vendor library on macOS) |
Login/password connectors implement ILoginPasswordConnectorSettings; the platform then shows the standard login fields.
The settings are saved as JSON with the type name of your class. Keep the saved form compatible between versions of your DLL: do not rename the class or its namespace, and do not change the type of a saved property. A saved connection that cannot be restored — the DLL is missing, the constructor or a property setter throws, a saved value no longer fits the property — is not deleted: it is kept unchanged in the connections list, marked as unavailable and is never connected, so restoring the DLL brings it back.
The type parameters of BaseConnector<TMessage, TPortfolioKey, TSecurityKey> are your queue message type (see Threading) and the keys you use for accounts and instruments (usually string).
| Member | Mandatory | Meaning |
|---|---|---|
IsSupportedAmericanFutures, IsSupportedAmericanStocks, IsSupportedRussianMarket, IsSupportedCrypto, IsSupportedCfd | yes (the first three are abstract) | the instrument kinds your feed serves: the platform asks your connector only for these |
IsSupportedTradingFunctions, IsSupportedStopOrders, IsSupportedServerOCO | yes | false for a data feed |
OnConnect() | yes | opens the connection; call OnConnected() when it is established (by default within 60 seconds, the ConnectTimeout property; otherwise the attempt is aborted). An exception fails the attempt: the connection shows the error and stays disconnected — the platform does not retry a first connect by itself (see «Connection loss» below) |
OnDisconnect() | yes | closes the connection; call OnDisconnected() |
OnSearchSecurities(filter) | yes | finds the instrument the user opened, see Instruments |
OnSubscribeMarketData(security, types), OnUnsubscribeMarketData(security, types) | yes | start and stop the data of an instrument. The base class counts subscribers: you are called once per change of the combined subscription |
OnProcess(message) | yes | handles the messages you put into the connector queue |
OnSearchSecurities(securityKey), OnRegisterOrder, OnCancelOrder | yes (abstract) | may be empty / report an error in a data feed |
OnSendHeartbeat(), HeartbeatTimeout | no | periodic check of a live connection |
You publish data with the protected methods of the base class:
| Method | Data |
|---|---|
ProcessSecurity(key, securityId, message, update) | creates or updates the Security of an instrument |
ProcessTick(security, id, time, direction, price, volume) | a trade |
ProcessBestBidAsk(security, time, MarketDataType.Bid/Ask, price, volume) | the best bid or ask |
ProcessMarketDepths(security, levels) | changed order book levels (MarketDepth: side, price, volume; volume 0 removes the level) |
ProcessSummary(key, update) | the daily summary (open, high, low, volume) |
The Process… methods drop data of instruments that nobody subscribed to, so you do not need to track the subscriptions yourself. Times are UTC.
Connection loss. When your source drops a connection that was established, call OnConnectionError(exception) with the reason and then OnDisconnected(). The platform then reconnects by itself: it calls OnConnect again and, after OnConnected, searches the instruments that are still open again (OnSearchSecurities) and subscribes them again (OnSubscribeMarketData). Report a refusal that a retry cannot fix (for example, rejected credentials) as a ConnectionRefusedException (namespace ATAS.DataFeedsCore.Exceptions): the platform then stops reconnecting and shows the reason. The same two calls during OnConnect, before OnConnected, fail the connect attempt like an exception.
OnConnect and OnDisconnect run on a thread-pool thread. OnSearchSecurities and the subscription methods run on the caller's thread, which may be the UI thread: do not block in them.Enqueue(message) hands a message to OnProcess on that thread, Enqueue(action) runs an action there. Call ProcessSecurity, ProcessTick, ProcessBestBidAsk and ProcessMarketDepths from the queue thread: the state of an instrument is not synchronized otherwise. The usual pattern is that the network callbacks of your API only enqueue, and all processing happens in the queue.OnProcess or in an enqueued action is logged and the queue continues.AsyncConnector<TPortfolioKey, TSecurityKey> is a variant of the base class for async code: await SwitchToConnectorThreadAsync() continues on the queue thread.The user opens instruments from the ATAS instrument catalogue. When a chart or a SmartDOM opens one, the platform asks every connected connector that supports the instrument kind with a SecurityFilter: Code (the contract code, for example ESZ6), Exchange, Type, ContractCode (the root, ES) and Expiration. If several connections serve the instrument, the one higher in the connections list wins.
Answer with exactly one security through RaiseSearchSecuritiesResult(filter, null, [security]), or with an error (RaiseSearchSecuritiesResult(filter, exception, [])) when you do not serve the instrument. Before that, create the security with ProcessSecurity and fill at least Code, Exchange, Type and TickSize; securityId is your unique id in the form code@exchange.
A connector cannot add instruments that are not in the ATAS catalogue.
The connector API covers live data only. Historical bars are not part of it: a connector cannot supply, replace or extend the history of an instrument, and which bars a chart shows before the live part does not depend on the connection that delivers the live data. Make sure the prices of your live data match the catalogue instrument, otherwise the live part will not continue the bars the chart already shows.
A simulated data feed: for a few CME futures it answers the instrument search and produces a random walk of trades, best prices and a synthetic order book. It does not trade.
Put the DLL into the custom connectors folder of the platform settings: AppData%\ATAS\CustomConnectors for the classic ATAS, AppData%\ATAS X\CustomConnectors for ATAS X (create the folder if it does not exist). It is not the Connectors folder next to it: that one holds the data of the connections.
Newtonsoft.Json) is not loaded again — the platform's copy is used. Native libraries of a vendor API can be placed in the same folder..pdb next to the DLL.OnApplySettings is written to the log and shown on that connection; the platform and the other connections keep working. Changed settings whose OnApplySettings throws are not saved.Your code runs inside the platform process, and part of it runs while the platform starts: the DLL is loaded (including any module initializer), saved connections are restored (settings constructors and property setters), and their connectors are created and configured (connector constructor, OnApplySettings, IConnectorSettingsSupportInitialization.Initialize). The platform cannot interrupt code that hangs there, and a hang there stops the platform start. So:
OnApplySettings fast and free of I/O — never open a network connection or wait for anything there;OnConnect (a thread-pool thread, bounded by the connection timeout) or in the connector queue.The [DisplayName] of the settings class identifies the connector: the connections are saved with it, and the data folder of the connector is named after it. A custom connector whose name equals the name of a built-in connector (ignoring case), or whose settings class has the full name of a built-in one, is not loaded — built-in connectors always win. Between two custom connectors with the same name, the one from the DLL that comes first in file name order is loaded. Choose a name that includes your product or company name.
net10.0, not net10.0-windows, and do not use WPF or Windows Forms: the connection settings are edited in the standard property grid of both platforms, so a connector does not need them. ATAS X does not load a DLL that references WPF or Windows Forms (the reason is written to the log).false from CheckSupported on other systems, so the connection shows the reason instead of failing on connect.arm64 on Apple silicon Macs).Custom connectors can be restricted by the tariff (see «Developer API and tariffs»). In that case your connector is still listed in the «Add connection» dialog, but choosing it shows the tariff banner; connections added earlier stay in the connections list, show «Custom connectors are not available on your current plan» and are not connected — automatically or manually. The tariff is checked every time a connection is added or connected, so the connections work again as soon as the restriction is lifted (for example, after the user logs on with another account). The other connection limits of the tariff (adding connections, the number of active connections) apply to custom connectors as well. A custom connection always counts as a crypto connection if its connector reports IsSupportedCrypto, and as a classic (US and EU markets) connection otherwise — whatever [Category] the settings class declares.
this.LogInfo(...) works in a connector as well).