Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/fix8mt/ufeed_bindings_csharp
C# language binding to UFEed.
https://github.com/fix8mt/ufeed_bindings_csharp
csharp fix fix-engine fix8 fix8-market fix8pro fixengine fixprotocol google-protocol-buffers high-performance low-latency middle middleware zeromq
Last synced: 20 days ago
JSON representation
C# language binding to UFEed.
- Host: GitHub
- URL: https://github.com/fix8mt/ufeed_bindings_csharp
- Owner: fix8mt
- License: lgpl-2.1
- Created: 2021-11-06T11:38:50.000Z (about 3 years ago)
- Default Branch: main
- Last Pushed: 2021-11-16T06:39:32.000Z (about 3 years ago)
- Last Synced: 2024-01-04T02:16:09.112Z (12 months ago)
- Topics: csharp, fix, fix-engine, fix8, fix8-market, fix8pro, fixengine, fixprotocol, google-protocol-buffers, high-performance, low-latency, middle, middleware, zeromq
- Language: C#
- Homepage:
- Size: 212 KB
- Stars: 1
- Watchers: 2
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# UFEed C# Binding
- [Introduction](#introduction)
- [Getting started](#getting-started)
- [Interface](#interface)
- [UFEMessage and UFEMessage Builder](#ufemessage-and-ufemessage-builder)
- [UFEedClient](#ufeedclient)
- [Constants](#constants)
- [FIX variants constants](#fix-variants-constants)
- [Building](#building)------------------------------------------------------------------------
# Introduction
The UFEed C# Adapter (`UFEed_C#`) provides a low level native C# interface to
the UFEGW. Interactions with the UFEGW are based around a `UFEedClient`
object from `UFE.UFEedClient` namespace which can be used to send and
receive Messages to and from the UFEGW.Use the following [Universal FIX engine documentaion](https://fix8mt.atlassian.net/wiki/spaces/FMT/pages/634438/Universal+FIX+Engine+Home) for a reference.
Features of `UFEedClient`:
- System API support ([see 4. Implementation Guide - Section
1.3](https://fix8mt.atlassian.net/wiki/spaces/FIX8PRO/pages/628308/4.+Implementation+Guide))- Business API support (eg. NewOrderSingle and standard FIX messages)
- Provides a 4-way communications API in order to make requests,
publish messages, receive responses and subscribe to broadcast
messages- User defined implementation of callback interface to handle these
PUB, SUB, REQ and REP message events- Dynamic configuration of PUB, SUB, REQ, REP addressing and topics
- Internal session management
Features of a `UFEMessage` and `UFEMessage.Builder`:
- A generic container of fields, i.e. tag/typed value pairs with
possible nested messages called groups- Smart field creation and access, rendering field value to ival, sval
or fval depending on context- Named Message getters/setters (name, long_name, seq, service_id,
subservice_id)# Getting started
The `UFEed_C#` is provided as a .NetCore nuget project for Linux and Windows. 3rd-party
dependencies are included to nuget package and will be downloaded during
`nuget restore` command. ZeroMQ C# wrapper
() has native build for .NetCore,
it's included in distribution as `ZeroMQ.1.0.0.nupkg`. Within the
packed or target build `UFEed_C#` archive we find the following directory structure
(example is Linux):```
ufeed_bindings_csharp
├── packages
│ ├── UFE.UFEedClient..nupkg
│ └── ZeroMQ.1.0.0.nupkg
├── Program.cs
└── UFE.UFEedClient.Sample.netcore.csproj
```# Interface
The main `UFEed_C#` interfaces/classes are `UFEMessage` and `UFEedClient`.
Most of `UFEMessage` field setters and `UFEedClient` setters follow
'builder' pattern to simplify C# language constructs (i.e. setters
return the reference to an object it was called from):```c#
// logon
var login = _uc.CreateMessage()
.SetLongName("login")
.SetType(StSystem)
.SetServiceId(UFE_CMD_LOGIN)
.AddField(UFE_CMD, UFE_CMD_LOGIN)
.AddField(UFE_LOGIN_ID, "webuser")
.AddField(UFE_LOGIN_PW, "5e884898da28047151d0e56f8dc");
```## UFEMessage and UFEMessage Builder
The `UFEMessage` class provides read-only access to underlying `WireMessage` object and mapped message fields and groups. `UFEMessage.BuildeR` on the other hand gives write-access to underlying
`WireMessage`. Messages are objects with which requests are made from the `UFEed_C#` to the UFEGW. `UFEMessage` ctors are public but in most cases `UFEedClient::CreateMessage()` factory method shall be used to extend `UFEMessage` class to other underlying protocols and middleware plugins in the future. `UFEMessage.Builder` class follows "builder" pattern to build a message.```c#
public class Builder
{
///
/// Ctors
///
public Builder(WireMessage wm);///
/// Message properties
///
public string Name { get; set; }
public string LongName { get; set; }
public WireMessage.Types.Type Type { get; set; }
public int ServiceId { get; set; }
public int SubServiceId { get; set; }
public int Seq { get; set; }///
/// Message properties setters for builder pattern
///
public Builder SetName(string name);
public Builder SetLongName(string longName);
public Builder SetType(WireMessage.Types.Type type);
public Builder SetServiceId(int serviceId);
public Builder SetSubServiceId(int subServiceId);
public Builder SetSeq(int seq);///
/// Underlying WireMessage readonly property
///
public WireMessage WireMessage { get; }///
/// Adds field with typed value
///
/// Field tag
/// Field typed value
/// Field location
/// self
public Builder AddField(uint tag, long val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, ByteString val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, string val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, char val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, double val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, bool val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, DateTime val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, Guid val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, Message.Status val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddField(uint tag, object val, UFEFieldLocation loc = UFEFieldLocation.FlBody);
public Builder AddFields(IEnumerable fields);
public Builder AddGroup(uint tag, out UFEField group, Action tr, UFEFieldLocation loc = UFEFieldLocation.FlBody)
public Builder AddGroupItem(UFEField group);///
/// Creates UFEMessage from builder
///
/// built UFEMessage
public UFEMessage Build();
}
````UFEMessage`:
```c#
public class UFEMessage
{
///
/// Ctors
///
/// Optional WireMessage to construct from
public UFEMessage(WireMessage wm);///
/// Message properties
///
public string Name { get; }
public string LongName { get; }
public WireMessage.Types.Type Type { get; }
public int ServiceId { get; }
public int SubServiceId { get; }
public int Seq { get; }///
/// Underlying WireMessage readonly property
///
public WireMessage WireMessage { get; }///
/// Mapped fields property
///
public Dictionary Fields { get; };///
/// Mapped groups property
///
public Dictionary> Groups { get; };///
/// Finds field by tag
///
/// field tag to find
/// Found field or null if not found
public UFEField FindField(uint tag);///
/// Finds field value by tag
///
/// field tag to find
/// Found field value or null if not found
public object FindFieldValue(uint tag);///
/// Finds group by tag
///
/// group tag to find
/// Found group or null if not found
public List FindGroup(uint tag);///
/// Creates a new builder
///
/// message builder from this message
public Builder NewBuilder();
}
````UFEMessage` and `UFEMessage.Builder` usage sample:
```c#
// logon
var login = _uc.CreateMessage()
.SetLongName("login")
.SetType(StSystem)
.SetServiceId(UFE_CMD_LOGIN)
.AddField(UFE_CMD, UFE_CMD_LOGIN)
.AddField(UFE_LOGIN_ID, "webuser")
.AddField(UFE_LOGIN_PW, "5e884898da28047151d0e56f8dc");
var response = _uc.Request(login);
if (1 != _receivedResMsgs.Count)
throw new UFEedClientException("Received message count mismatch");
if (_receivedResMsgs[0].FindField(UFE_SESSION_TOKEN) == null)
throw new UFEedClientException("Could not find session token");
var sessToken = _receivedResMsgs[0].FindFieldValue(UFE_SESSION_TOKEN);
if (sessToken == null || !(sessToken is Guid) || sessToken.ToString().Length==0)
throw new UFEedClientException($"Invalid session token {sessToken}");
if (response.FindFieldValue(UFE_SESSION_TOKEN).ToString() != sessToken.ToString())
throw new UFEedClientException($"Session token mimatch {response.FindFieldValue(UFE_SESSION_TOKEN)} != {sessToken}");
````UFEMessage` and `UFEMessage.Builder` create NewOrderSingle with groups:
```c#
UFEField g1 = null;
var nos = _uc.CreateMessage()
.SetLongName("NewOrderSingle")
.SetType(StFixmsg)
.SetServiceId(1)
.SetName(MsgType.NEWORDERSINGLE)
.AddField(ClOrdID.tag, "123")
.AddField(TransactTime.tag, DateTime.Now)
.AddField(ExecInst.tag, ExecInst.ALL_OR_NONE)
.AddField(OrdType.tag, OrdType.LIMIT)
.AddField(Side.tag, Side.BUY)
.AddGroup(NoAllocs.tag, out g1, (builder, group) =>
{
builder.AddGroupItem(group)
.SetLongName("NoAlloc")
.SetType(StFixmsg)
.SetSeq(1)
.AddField(AllocAccount.tag, "ABC", FlBody)
.AddField(AllocQty.tag, 2);
builder.AddGroupItem(group)
.SetLongName("NoAlloc")
.SetType(StFixmsg)
.SetSeq(2)
.AddField(AllocAccount.tag, "CDE", FlBody)
.AddField(AllocQty.tag, 4);
});
```## UFEedClient
The `UFEedClient` class is used as the interface to make both System and
Business API calls to the UFEGW. Sessions between `UFEedClient` and the
UFEGW are made up of ZeroMQ PUB/SUB and REQ/REP sockets. The network
addresses and message topics inherent to these sockets are configurable
via `UFEedClient`. In addition, the `UFEedClient` manages these UFEGW
sessions on behalf of the user (after the user has successfully logged
in).`UFEedClient` provides a event callback interface that is a part of
`UFEedClient` that must be subscribed by `UFEedClient` consumer:```c#
class UFEedClient
{
...
///
/// Event for receiving subscription message. Will be called in a different thread in most cases.
///
public event EventHandler SubscriptionMessageReceived;///
/// Event for receiving responder message. Will be called in a different thread in most cases.
///
public event EventHandler ResponderMessageReceived;///
/// Event for receiving response message for a sent request.
///
public event EventHandler ResponseMessageReceived;///
/// Event for authentication request. Will be called in a different thread in most cases.
/// Consumer must set UFEAuthEventArgs.Accept to false in case of wrong login/password.
///
public event EventHandler AuthenticateRequested;///
/// Event for ZeroMQ errors.
/// Consumer must set UFEZeroMQErrorEventArgs.Continue to false in case of unrecoverable errors
///
// ReSharper disable once InconsistentNaming
public event EventHandler ZeroMQErrorHappened;///
/// Event for UFEedClintErrors
/// Consumer must set UFEErrorEventArgs.Continue to false in case of unrecoverable errors
///
public event EventHandler ErrorHappened;
}
````UFEedClient` is configured with `UFEconfiguration` class:
```c#
public class UFEedConfiguration
{
///
/// Subscriber endpoint, defaults to "tcp://127.0.0.1:55745"
///
public string Subscriber { get; set; };///
/// Requester endpoint, defaults to "tcp://127.0.0.1:55746"
///
public string Requester { get; set; };///
/// Publisher endpoint, defaults to "tcp://*:55747"
///
public string Publisher { get; set; };///
/// Responder endpoint, defaults to "tcp://*:55748"
///
public string Responder { get; set; };///
/// Subscriber topic, defaults to "ufegw-publisher"
///
public string SubscriberTopic { get; set; };///
/// Requester topic, defaults to "ufegw-responder"
///
public string RequesterTopic { get; set; };///
/// Publisher topic, defaults to "ufeedclient-publisher"
///
public string PublisherTopic { get; set; };///
/// Responder topic, defaults to "ufeedclient-responder"
///
public string ResponderTopic { get; set; };///
/// Max IO threads for ZMQ background work, defaults to 1
///
public int MaxIoThreads { get; set; ;///
/// Poll interval in milliseconds, defaults to 10
///
public uint PollIntervalMs { get; set; };
}
````UFEedClient` interface:
```c#
public class UFEedClient : IDisposable
{
///
/// Constructs UFEedClient
///
/// configuration to setup UFEedClient object
public UFEedClient(UFEedConfiguration configuration);///
/// Disposes UFEedClient resources, implements IDisposable.Dispose()
///
public void Dispose();///
/// Returns UFEedClient configuration
///
public UFEedConfiguration Configuration { get; }///
/// Starts UFEedClient. When started in synchronous mode (wait = true)
/// it does not return until Stop() is called from a different thread.
///
/// true for synchronous call, false for asynchronous
public void Start(bool wait = false);///
/// Stops UFEedClient
///
public void Stop();///
/// Creates UFEMEssage from WireMessage
///
/// WireMessage to create from
/// new UFEMessage
public UFEMessage CreateMessage(WireMessage wm = null);///
/// Synchronously sends request to UFE and waits for UFE response, REQ/REP pattern
///
/// Request to send
/// Response received
/// Can throw UFEedClientException
public UFEMessage Request(UFEMessage,Builder request);///
/// Send message to responder channel. Back channel pattern.
/// IMPORTANT: Should be called as much as possible from within ResponderMessageReceived callback thread
///
/// Message to send
public void Respond(UFEMessage.Builder msg);///
/// Events
///
public event EventHandler SubscriptionMessageReceived;
public event EventHandler ResponderMessageReceived;
public event EventHandler ResponseMessageReceived;
public event EventHandler AuthenticateRequested;
public event EventHandler ZeroMQErrorHappened;
public event EventHandler ErrorHappened;
}
````UFEedClient` usage sample:
```c#
using (var uc = new UFEedClient(new UFEedConfiguration{Subscriber = SUBSCRIBER_DEFAULT}))
{
uc.SubscriptionMessageReceived += (sender, args) => { ... };
uc.ResponderMessageReceived += (sender, args) => { ... };
uc.ZeroMQErrorHappened += (sender, args) => { ... };
uc.ErrorHappened += (sender, args) => { ... };
uc.Start();
var login = uc.CreateMessage()
.SetLongName("login")
.SetType(StSystem)
.SetServiceId(UFE_CMD_LOGIN)
.AddField(UFE_CMD, UFE_CMD_LOGIN)
.AddField(UFE_LOGIN_ID, "abcdef")
.AddField(UFE_LOGIN_PW, "1e884898da28047151d0e56f8df");
var response = uc.Request(login);
...
}
```# Constants
The `UFEed_C#` maintains a list of constant values that translate to integer
codes in the UFEGW. These integer codes are used to identify System API
services as well as general FIX functionality. A full list of these
constants is available at `UFE/UFE.UFEedClient/UFEConsts.cs` file.
Constants could be regenerated using `UFE.UFEedClient.GenConsts` project.## FIX variants constants
The `UFEed_C#` provides constants for all stock FIX variants:
```c#
using UFE.UFEedClient.FIX50SP2.Field;
...
// NOS creation
var nos = _uc.CreateMessage()
.SetLongName("NewOrderSingle")
.SetType(StFixmsg)
.SetServiceId(1)
.SetName(MsgType.NEWORDERSINGLE)
.AddField(ClOrdID.tag, "123")
.AddField(TransactTime.tag, DateTime.Now)
.AddField(ExecInst.tag, ExecInst.ALL_OR_NONE)
.AddField(OrdType.tag, OrdType.LIMIT)
.AddField(Side.tag, Side.BUY);
```# Building
The `UFEed_C#` build follows standard .NetCore build pattern. Solution file `UFE.netcore.sln` contains all the projects to be build.
The `UFEed_C#` provides a sample to use as a starting point for UFEed C#
development. To build the sample, you have to open
`UFE.UFEedClient.Sample.netcore.csproj` provided in the installation
package and reference `UFE.UFEedClient` project.