librelist archives

« back to archive

[RFC/PATCH] Primitive financial exchange

[RFC/PATCH] Primitive financial exchange

From:
Marat Stanichenko
Date:
2014-01-20 @ 10:09
Dear colleagues,

I would like to introduce a primitive version of financial exchange. This is by
no means intended to be a full-featured exchange (although it may be extended to!)
Therefore, current implementation should not be considered as an 
individual project
but solely as a part of the library. I can think of several reasons why 
this should
be included into the project:

* A proof of concept. It may show that libtrading suits for various projects.
* A kind of template. Users may rely on existing implementation once running
  their own projects.
* A testing area. The more library is used, the greater chance we realize what
  feauters are to be implemented and what bugs are to be fixed.
* Performance measurment. Once we have a ready to use exchange simulator we can
  simulate real life activity to measure particular performance statistics.

Current implementation is very basic. It consists of primitive matching engine
which supports limit orders only. Once an order is matched the corresponding
trade details are printed out.

For the sake of simplicity all orders are stored in a linear array. On the one
hand it speeds up particular operations such a order cancellation on the other
hand it causes obvious problems such as maximum number of orders accepted. Active
orders are stored in bidirectional lists. A single list per order book level.

Not so much you can do at the moment but in order to have a try just run a market
and see whether limit orders are processed correctly e.g.

$ ./tools/sim/market -p 7070
Market is running on port 7070...
Buyer 0 Seller 1, price 10, size 50
Buyer 0 Seller 2, price 10, size 50

$ ./tools/sim/trader -h localhost -p 7070
Trader Logged on
Buy/Sell/Quit (1/2/0): 1
Price: 10
Quantity: 100

$ ./tools/sim/trader -h localhost -p 7070
Trader Logged on
Buy/Sell/Quit (1/2/0): 2
Price: 9
Quantity: 50

$ ./tools/sim/trader -h localhost -p 7070
Trader Logged on
Buy/Sell/Quit (1/2/0): 2
Price: 11
Quantity: 100
Buy/Sell/Quit (1/2/0): 2
Price: 8
Quantity: 100

Please, let me know whether you would like this to be merged and whether any
further activity should be performed in this direction going forward.

Feel free to share you comments and ideas.

Signed-off-by: Marat Stanichenko <mstanichenko@gmail.com>
---
 Makefile           |    8 ++
 tools/sim/engine.c |  179 +++++++++++++++++++++++++++++++++
 tools/sim/engine.h |   30 ++++++
 tools/sim/market.c |  277 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/sim/market.h |   96 ++++++++++++++++++
 tools/sim/trader.c |  188 +++++++++++++++++++++++++++++++++++
 6 files changed, 778 insertions(+)
 create mode 100644 tools/sim/engine.c
 create mode 100644 tools/sim/engine.h
 create mode 100644 tools/sim/market.c
 create mode 100644 tools/sim/market.h
 create mode 100644 tools/sim/trader.c

diff --git a/Makefile b/Makefile
index e3b76df..aee3836 100644
--- a/Makefile
+++ b/Makefile
@@ -81,6 +81,8 @@ PROGRAMS += tools/fast/fast_server
 PROGRAMS += tools/fix/fix_client
 PROGRAMS += tools/fix/fix_server
 PROGRAMS += tools/fix/fix_perf
+PROGRAMS += tools/sim/market
+PROGRAMS += tools/sim/trader
 PROGRAMS += tools/tape/tape
 
 DEFINES =
@@ -105,6 +107,12 @@ ifeq ($(uname_S),Darwin)
 	COMPAT_OBJS += lib/compat/clock_gettime.o
 endif
 
+market_EXTRA_DEPS += lib/die.o
+market_EXTRA_DEPS += tools/sim/engine.o
+market_EXTRA_LIBS += -lm
+
+trader_EXTRA_DEPS += lib/die.o
+
 fix_client_EXTRA_DEPS += lib/die.o
 fix_client_EXTRA_DEPS += tools/fix/test.o
 fix_client_EXTRA_LIBS += -lm
diff --git a/tools/sim/engine.c b/tools/sim/engine.c
new file mode 100644
index 0000000..db1ea1d
--- /dev/null
+++ b/tools/sim/engine.c
@@ -0,0 +1,179 @@
+#include <string.h>
+#include <stdio.h>
+
+#include "engine.h"
+
+void order_book_init(struct order_book *book)
+{
+	memset(book, 0, sizeof(struct order_book));
+
+	book->best_ask = MAX_LEVEL + 1;
+	book->best_bid = 0;
+}
+
+static void order_insert(struct level *level, struct order *order)
+{
+	if (level->head != NULL) {
+		level->tail->next = order;
+		order->prev = level->tail;
+	} else {
+		level->head = order;
+		order->prev = NULL;
+	}
+
+	level->tail = order;
+	order->next = NULL;
+
+	return;
+}
+
+static void order_remove(struct level *level, struct order *order)
+{
+	struct order *prev = order->prev;
+	struct order *next = order->next;
+
+	if (prev == NULL)
+		level->head = next;
+	else
+		prev->next = next;
+
+	if (next == NULL)
+		level->tail = prev;
+	else
+		next->prev = prev;
+
+	return;
+}
+
+static int do_trade(unsigned long buyer, unsigned long seller, unsigned 
long price, size_t size)
+{
+	fprintf(stdout, "Buyer %lu Seller %lu, price %lu, size %zu\n", buyer, 
seller, price, size);
+
+	return 0;
+}
+
+static int match(struct order_book *book, struct order *order)
+{
+	struct level *level;
+	struct order *head;
+
+	if (order->side == 0) {
+		level = book->levels + order->level;
+
+		while (order->level >= book->best_ask) {
+			level = book->levels + book->best_ask;
+			head = level->head;
+
+			while (head != NULL) {
+				if (head->size < order->size) {
+					do_trade(order->trader, head->trader, book->best_ask, head->size);
+
+					order->size -= head->size;
+					head->size = 0;
+				} else {
+					do_trade(order->trader, head->trader, book->best_ask, order->size);
+
+					head->size -= order->size;
+					order->size = 0;
+				}
+
+				if (!head->size) {
+					order_remove(level, head);
+					head = level->head;
+				}
+
+				if (!order->size)
+					goto done;
+			}
+
+			book->best_ask++;
+		}
+
+		if (order->level > book->best_bid)
+			book->best_bid = order->level;
+	} else {
+		level = book->levels + order->level;
+
+		while (order->level <= book->best_bid) {
+			level = book->levels + book->best_bid;
+			head = level->head;
+
+			while (head != NULL) {
+				if (head->size < order->size) {
+					do_trade(head->trader, order->trader, book->best_bid, head->size);
+
+					order->size -= head->size;
+					head->size = 0;
+				} else {
+					do_trade(head->trader, order->trader, book->best_bid, order->size);
+
+					head->size -= order->size;
+					order->size = 0;
+				}
+
+				if (!head->size) {
+					order_remove(level, head);
+					head = level->head;
+				}
+
+				if (!order->size)
+					goto done;
+			}
+
+			book->best_bid--;
+		}
+
+		if (order->level < book->best_ask)
+			book->best_ask = order->level;
+	}
+
+	order_insert(level, order);
+
+done:
+	return 0;
+}
+
+int do_limit(struct order_book *book, struct order *limit)
+{
+	struct order *order;
+
+	if (book->orders_num >= MAX_ORDERS)
+		goto reject;
+
+	if (limit->level > MAX_LEVEL || limit->level < 1)
+		goto reject;
+
+	if (!limit->size)
+		goto reject;
+
+	order = book->orders + book->orders_num;
+	memcpy(order, limit, sizeof(struct order));
+	book->orders_num++;
+
+	return match(book, order);
+
+reject:
+	return -1;
+}
+
+int do_cancel(struct order_book *book, unsigned long id)
+{
+	struct order *order = book->orders + id;
+	struct level *level;
+
+	if (!book->orders_num || id >= book->orders_num)
+		goto reject;
+
+	if (!order->size)
+		goto reject;
+
+	level = book->levels + order->level;
+	order_remove(level, order);
+
+	order->size = 0;
+
+	return 0;
+
+reject:
+	return -1;
+}
diff --git a/tools/sim/engine.h b/tools/sim/engine.h
new file mode 100644
index 0000000..dbac751
--- /dev/null
+++ b/tools/sim/engine.h
@@ -0,0 +1,30 @@
+#define	MAX_ORDERS	10000
+#define	MAX_LEVEL	100
+
+struct order {
+	unsigned long trader;
+	unsigned long level;
+	struct order *next;
+	struct order *prev;
+	unsigned long side;
+	size_t size;
+};
+
+struct level {
+	struct order *head;
+	struct order *tail;
+};
+
+struct order_book {
+	struct level levels[MAX_LEVEL + 2];
+	struct order orders[MAX_ORDERS];
+	unsigned long orders_num;
+	unsigned long best_ask;
+	unsigned long best_bid;
+	unsigned long book_id;
+	char code[32];
+};
+
+int do_cancel(struct order_book *book, unsigned long order_id);
+int do_limit(struct order_book *book, struct order *order);
+void order_book_init(struct order_book *book);
diff --git a/tools/sim/market.c b/tools/sim/market.c
new file mode 100644
index 0000000..3f56306
--- /dev/null
+++ b/tools/sim/market.c
@@ -0,0 +1,277 @@
+#include "libtrading/proto/fix_message.h"
+#include "libtrading/proto/fix_session.h"
+#include "libtrading/die.h"
+#include "market.h"
+
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#define	EPOLL_MAXEVENTS	100
+
+static const char *program;
+
+static int socket_setopt(int sockfd, int level, int optname, int optval)
+{
+	return setsockopt(sockfd, level, optname, (void *) &optval, sizeof(optval));
+}
+
+static void usage(void)
+{
+	fprintf(stderr, "\n usage: %s -p port\n\n", program);
+
+	exit(EXIT_FAILURE);
+}
+
+static void market_init(struct market *market)
+{
+	memset(market, 0, sizeof(struct market));
+
+	order_book_init(&market->book);
+
+	return;
+}
+
+static inline struct fix_field *fix_field_by_tag(struct fix_message *msg,
enum fix_tag tag)
+{
+	struct fix_field *field = NULL;
+	int i;
+
+	for (i = 0; i < msg->nr_fields; i++) {
+		field = msg->fields + i;
+
+		if (field->tag == tag)
+			return field;
+	}
+
+	return NULL;
+}
+
+static int do_income(struct market *market, struct fix_session_cfg *cfg)
+{
+	struct fix_message *recv_msg;
+	struct fix_message send_msg;
+	struct fix_field *field;
+	struct trader *trader;
+	struct order order;
+
+	trader = trader_by_sock(market, cfg->sockfd);
+	if (!trader) {
+		trader = trader_new(market);
+		if (!trader)
+			goto fail;
+
+		trader->session = fix_session_new(cfg);
+		if (!trader->session)
+			goto fail;
+	}
+
+	recv_msg = fix_session_recv(trader->session, 0);
+	if (!recv_msg)
+		goto done;
+
+	if (!trader->active) {
+		if (!fix_message_type_is(recv_msg, FIX_MSG_TYPE_LOGON))
+			goto logout;
+
+		field = fix_field_by_tag(recv_msg, SenderCompID);
+		if (!field)
+			goto logout;
+
+		strncpy(trader->name, field->string_value, 32);
+
+		trader->active = true;
+
+		send_msg.type = FIX_MSG_TYPE_LOGON;
+		fix_session_send(trader->session, &send_msg, 0);
+
+		goto done;
+	}
+
+	if (fix_session_admin(trader->session, recv_msg))
+		goto done;
+
+	if (fix_message_type_is(recv_msg, FIX_MSG_TYPE_LOGOUT)) {
+		goto logout;
+	} else if (fix_message_type_is(recv_msg, FIX_MSG_TYPE_NEW_ORDER_SINGLE)) {
+		order.trader = trader->id;
+
+		field = fix_field_by_tag(recv_msg, Side);
+		if (!field)
+			goto done;
+
+		if (field->string_value[0] == '1')
+			order.side = 0;
+		else if (field->string_value[0] == '2')
+			order.side = 1;
+		else
+			goto done;
+
+		field = fix_field_by_tag(recv_msg, Price);
+		if (!field)
+			goto done;
+
+		order.level = round(field->float_value);
+
+		field = fix_field_by_tag(recv_msg, OrderQty);
+		if (!field)
+			goto done;
+
+		order.size = round(field->float_value);
+
+		if (do_limit(&market->book, &order))
+			goto done;
+	}
+
+done:
+	return 0;
+
+fail:
+	return -1;
+
+logout:
+	send_msg.type = FIX_MSG_TYPE_LOGOUT;
+	fix_session_send(trader->session, &send_msg, 0);
+
+	trader_free(trader);
+	close(cfg->sockfd);
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct epoll_event *events = NULL;
+	struct market *market = NULL;
+	struct fix_session_cfg cfg;
+	struct epoll_event event;
+	struct sockaddr_in sa;
+	int sockfd = -1;
+	int port = 0;
+	int efd = -1;
+	int ret = -1;
+	int num;
+	int opt;
+	int i;
+
+	program = basename(argv[0]);
+
+	while ((opt = getopt(argc, argv, "p:")) != -1) {
+		switch (opt) {
+		case 'p':
+			port = atoi(optarg);
+			break;
+		default:
+			usage();
+		}
+	}
+
+	if (!port)
+		usage();
+
+	market = calloc(1, sizeof(struct market));
+	if (!market)
+		die("couldn't create a market");
+
+	market_init(market);
+	sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (sockfd < 0)
+		die("cannot create a socket");
+
+	if (socket_setopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0)
+		die("cannot set a socket option: TCP_NODELAY");
+
+	if (socket_setopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 1) < 0)
+		die("cannot set a socket option: SO_REUSEADDR");
+
+	sa = (struct sockaddr_in) {
+		.sin_family		= AF_INET,
+		.sin_port		= htons(port),
+		.sin_addr		= (struct in_addr) {
+			.s_addr			= INADDR_ANY,
+		},
+	};
+
+	if (bind(sockfd, (const struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0)
+		die("bind failed");
+
+	fprintf(stdout, "Market is running on port %d...\n", port);
+
+	if (listen(sockfd, 10) < 0)
+		die("listen failed");
+
+	efd = epoll_create1(0);
+	if (efd < 0)
+		die("epoll_create1 failed");
+
+	events = calloc(EPOLL_MAXEVENTS, sizeof(struct epoll_event));
+	if (!events)
+		die("calloc failed");
+
+	event.data.fd = sockfd;
+	event.events = EPOLLIN | EPOLLRDHUP;
+	if (epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &event)) {
+		fprintf(stderr, "epoll_ctl failed\n");
+		goto exit;
+	}
+
+	cfg.dialect = &fix_dialects[FIX_4_4];
+
+	while (true) {
+		num = epoll_wait(efd, events, EPOLL_MAXEVENTS, -1);
+		if (num < 0) {
+			fprintf(stderr, "epoll_wait failed\n");
+			goto exit;
+		}
+
+		for (i = 0; i < num; i++) {
+			if (events[i].data.fd == sockfd) {
+				if (events[i].events & EPOLLERR ||
+					events[i].events & EPOLLHUP ||
+						events[i].events & EPOLLRDHUP) {
+					fprintf(stderr, "listening socket error\n");
+					goto exit;
+				}
+
+				cfg.sockfd = accept(sockfd, NULL, NULL);
+				if (cfg.sockfd < 0) {
+					fprintf(stderr, "accept failed\n");
+					goto exit;
+				}
+
+				event.data.fd = cfg.sockfd;
+				event.events = EPOLLIN | EPOLLRDHUP;
+				if (epoll_ctl(efd, EPOLL_CTL_ADD, cfg.sockfd, &event) < 0) {
+					fprintf(stderr, "epoll_ctl failed\n");
+					goto exit;
+				}
+			} else {
+				cfg.sockfd = events[i].data.fd;
+
+				if (events[i].events & EPOLLERR ||
+					events[i].events & EPOLLHUP ||
+						events[i].events & EPOLLRDHUP) {
+					trader_free(trader_by_sock(market, cfg.sockfd));
+					close(cfg.sockfd);
+
+					continue;
+				}
+
+				if (do_income(market, &cfg))
+					close(cfg.sockfd);
+			}
+		}
+	}
+
+exit:
+	close(sockfd);
+	free(events);
+	free(market);
+
+	return ret;
+}
diff --git a/tools/sim/market.h b/tools/sim/market.h
new file mode 100644
index 0000000..b5ba14e
--- /dev/null
+++ b/tools/sim/market.h
@@ -0,0 +1,96 @@
+#include <string.h>
+
+#include "engine.h"
+
+#define	MAX_TRADERS	100
+
+struct trader {
+	struct fix_session *session;
+	unsigned long id;
+	char name[32];
+	bool active;
+};
+
+struct market {
+	struct trader traders[MAX_TRADERS];
+	unsigned long traders_num;
+	struct order_book book;
+};
+
+static inline struct trader *trader_by_sock(struct market *market, int sockfd)
+{
+	struct trader *traders = market->traders;
+	struct trader *trader;
+	int i;
+
+	for (i = 0; i < market->traders_num; i++) {
+		trader = traders + i;
+
+		if (trader->session && trader->session->sockfd == sockfd)
+			return trader;
+	}
+
+	return NULL;
+}
+
+static inline struct trader *trader_by_name(struct market *market, char *name)
+{
+	struct trader *traders = market->traders;
+	struct trader *trader;
+	int i;
+
+	for (i = 0; i < market->traders_num; i++) {
+		trader = traders + i;
+
+		if (!strcmp(trader->name, name))
+			return trader;
+	}
+
+	return NULL;
+}
+
+static inline struct trader *trader_by_id(struct market *market, unsigned
long id)
+{
+	struct trader *traders = market->traders;
+	struct trader *trader;
+	int i;
+
+	for (i = 0; i < market->traders_num; i++) {
+		trader = traders + i;
+
+		if (trader->id == id)
+			return trader;
+	}
+
+	return NULL;
+}
+
+static inline struct trader *trader_new(struct market *market)
+{
+	struct trader *traders = market->traders;
+	struct trader *trader;
+
+	if (market->traders_num >= MAX_TRADERS)
+		return NULL;
+
+	trader = traders + market->traders_num;
+	trader->id = market->traders_num++;
+	trader->active = false;
+
+	return trader;
+}
+
+static inline void trader_free(struct trader *trader)
+{
+	if (!trader)
+		goto done;
+
+	if (trader->session)
+		fix_session_free(trader->session);
+
+	trader->session = NULL;
+	trader->active = false;
+
+done:
+	return;
+}
diff --git a/tools/sim/trader.c b/tools/sim/trader.c
new file mode 100644
index 0000000..5b42a6d
--- /dev/null
+++ b/tools/sim/trader.c
@@ -0,0 +1,188 @@
+#include "libtrading/proto/fix_message.h"
+#include "libtrading/proto/fix_session.h"
+
+#include "libtrading/array.h"
+#include "libtrading/die.h"
+
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <errno.h>
+
+#define	FIX_FIELDS_NUM	32
+
+static const char *program;
+
+static void usage(void)
+{
+	fprintf(stderr, "\n usage: %s -h host -p port\n\n", program);
+
+	exit(EXIT_FAILURE);
+}
+
+static int socket_setopt(int sockfd, int level, int optname, int optval)
+{
+	return setsockopt(sockfd, level, optname, (void *) &optval, sizeof(optval));
+}
+
+static int do_trader(struct fix_session_cfg *cfg)
+{
+	struct fix_session *session = NULL;
+	struct fix_field *fields = NULL;
+	double price, qty;
+	int ret = -1;
+	int nr = 0;
+	int side;
+
+	session = fix_session_new(cfg);
+	if (!session) {
+		fprintf(stderr, "FIX session cannot be created\n");
+		goto exit;
+	}
+
+	ret = fix_session_logon(session);
+	if (ret) {
+		fprintf(stderr, "Trader Logon failed\n");
+		goto exit;
+	}
+
+	fprintf(stdout, "Trader Logged on\n");
+
+	fields = calloc(FIX_FIELDS_NUM, sizeof(struct fix_field));
+	if (!fields) {
+		fprintf(stderr, "Cannot allocate memory\n");
+		goto exit;
+	}
+
+	while (true) {
+		nr = 0;
+
+		fprintf(stdout, "Buy/Sell/Quit (1/2/0): ");
+		if (scanf("%d", &side) != 1)
+			continue;
+
+		if (side == 1)
+			fields[nr++] = FIX_STRING_FIELD(Side, "1");
+		else if (side == 2)
+			fields[nr++] = FIX_STRING_FIELD(Side, "2");
+		else if (side == 0)
+			break;
+		else
+			continue;
+
+		fprintf(stdout, "Price: ");
+		if (scanf("%lf", &price) != 1)
+			continue;
+
+		if (price <= 0)
+			continue;
+
+		fields[nr++] = FIX_FLOAT_FIELD(Price, price);
+
+		fprintf(stdout, "Quantity: ");
+		if (scanf("%lf", &qty) != 1)
+			continue;
+
+		if (qty <= 0)
+			continue;
+
+		fields[nr++] = FIX_FLOAT_FIELD(OrderQty, qty);
+
+		fix_session_new_order_single(session, fields, nr);
+	}
+
+	ret = fix_session_logout(session, NULL);
+	if (!ret)
+		fprintf(stdout, "Trader logged out\n");
+	else
+		fprintf(stderr, "Trader logout failed\n");
+
+exit:
+	fix_session_free(session);
+	free(fields);
+
+	return ret;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fix_session_cfg cfg;
+	const char *host = NULL;
+	struct sockaddr_in sa;
+	int saved_errno = 0;
+	struct hostent *he;
+	int port = 0;
+	int ret = 0;
+	char **ap;
+	int opt;
+
+	program = basename(argv[0]);
+
+	while ((opt = getopt(argc, argv, "h:p:")) != -1) {
+		switch (opt) {
+		case 'p':
+			port = atoi(optarg);
+			break;
+		case 'h':
+			host = optarg;
+			break;
+		default: /* '?' */
+			usage();
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (!port || !host)
+		usage();
+
+	strncpy(cfg.target_comp_id, "SELLSIDE", ARRAY_SIZE(cfg.target_comp_id));
+	strncpy(cfg.sender_comp_id, "BUYSIDE", ARRAY_SIZE(cfg.sender_comp_id));
+	cfg.dialect	= &fix_dialects[FIX_4_4];
+
+	he = gethostbyname(host);
+	if (!he)
+		error("Unable to look up %s (%s)", host, hstrerror(h_errno));
+
+	for (ap = he->h_addr_list; *ap; ap++) {
+		cfg.sockfd = socket(he->h_addrtype, SOCK_STREAM, IPPROTO_TCP);
+		if (cfg.sockfd < 0) {
+			saved_errno = errno;
+			continue;
+		}
+
+		sa = (struct sockaddr_in) {
+			.sin_family		= he->h_addrtype,
+			.sin_port		= htons(port),
+		};
+		memcpy(&sa.sin_addr, *ap, he->h_length);
+
+		if (connect(cfg.sockfd, (const struct sockaddr *)&sa, sizeof(struct 
sockaddr_in)) < 0) {
+			saved_errno = errno;
+			close(cfg.sockfd);
+			cfg.sockfd = -1;
+			continue;
+		}
+		break;
+	}
+
+	if (cfg.sockfd < 0)
+		error("Unable to connect to a socket (%s)", strerror(saved_errno));
+
+	if (socket_setopt(cfg.sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0)
+		die("cannot set socket option TCP_NODELAY");
+
+	ret = do_trader(&cfg);
+
+	shutdown(cfg.sockfd, SHUT_RDWR);
+
+	if (close(cfg.sockfd) < 0)
+		die("close");
+
+	return ret;
+}
-- 
1.7.9.5

Re: [RFC/PATCH] Primitive financial exchange

From:
Marat Stanichenko
Date:
2014-01-22 @ 09:31
> I'm seeing a SIGSEGV in the server when a trader connects to it

May I kindly ask to apply the following patch. Let me know whether it helps

Signed-off-by: Marat Stanichenko <mstanichenko@gmail.com>
---
 tools/sim/market.c |    3 +++
 1 file changed, 3 insertions(+)

diff --git a/tools/sim/market.c b/tools/sim/market.c
index 3f56306..19cedf9 100644
--- a/tools/sim/market.c
+++ b/tools/sim/market.c
@@ -1,5 +1,6 @@
 #include "libtrading/proto/fix_message.h"
 #include "libtrading/proto/fix_session.h"
+#include "libtrading/array.h"
 #include "libtrading/die.h"
 #include "market.h"
 
@@ -220,6 +221,8 @@ int main(int argc, char *argv[])
 		goto exit;
 	}
 
+	strncpy(cfg.sender_comp_id, "SELLSIDE", ARRAY_SIZE(cfg.sender_comp_id));
+	strncpy(cfg.target_comp_id, "BUYSIDE", ARRAY_SIZE(cfg.target_comp_id));
 	cfg.dialect = &fix_dialects[FIX_4_4];
 
 	while (true) {
-- 
1.7.9.5

Re: [RFC/PATCH] Primitive financial exchange

From:
Marat Stanichenko
Date:
2014-01-24 @ 13:01
Version 2: SIGSEGV fixes included

I would like to introduce a primitive version of financial exchange. This is by
no means intended to be a full-featured exchange (although it may be extended to!)
Therefore, current implementation should not be considered as an 
individual project
but solely as a part of the library. I can think of several reasons why 
this should
be included into the project:

* A proof of concept. It may show that libtrading suits for various projects.
* A kind of template. Users may rely on existing implementation once running
  their own projects.
* A testing area. The more library is used, the greater chance we realize what
  feauters are to be implemented and what bugs are to be fixed.
* Performance measurment. Once we have a ready to use exchange simulator we can
  simulate real life activity to measure particular performance statistics.

Current implementation is very basic. It consists of primitive matching engine
which supports limit orders only. Once an order is matched the corresponding
trade details are printed out.

For the sake of simplicity all orders are stored in a linear array. On the one
hand it speeds up particular operations such a order cancellation on the other
hand it causes obvious problems such as maximum number of orders accepted. Active
orders are stored in bidirectional lists. A single list per order book level.

Not so much you can do at the moment but in order to have a try just run a market
and see whether limit orders are processed correctly e.g.

$ ./tools/sim/market -p 7070
Market is running on port 7070...
Buyer 0 Seller 1, price 10, size 50
Buyer 0 Seller 2, price 10, size 50

$ ./tools/sim/trader -h localhost -p 7070
Trader Logged on
Buy/Sell/Quit (1/2/0): 1
Price: 10
Quantity: 100

$ ./tools/sim/trader -h localhost -p 7070
Trader Logged on
Buy/Sell/Quit (1/2/0): 2
Price: 9
Quantity: 50

$ ./tools/sim/trader -h localhost -p 7070
Trader Logged on
Buy/Sell/Quit (1/2/0): 2
Price: 11
Quantity: 100
Buy/Sell/Quit (1/2/0): 2
Price: 8
Quantity: 100

Signed-off-by: Marat Stanichenko <mstanichenko@gmail.com>
---
 Makefile           |    8 ++
 tools/sim/engine.c |  179 +++++++++++++++++++++++++++++++++
 tools/sim/engine.h |   30 ++++++
 tools/sim/market.c |  284 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 tools/sim/market.h |   96 ++++++++++++++++++
 tools/sim/trader.c |  188 ++++++++++++++++++++++++++++++++++
 6 files changed, 785 insertions(+)
 create mode 100644 tools/sim/engine.c
 create mode 100644 tools/sim/engine.h
 create mode 100644 tools/sim/market.c
 create mode 100644 tools/sim/market.h
 create mode 100644 tools/sim/trader.c

diff --git a/Makefile b/Makefile
index e3b76df..aee3836 100644
--- a/Makefile
+++ b/Makefile
@@ -81,6 +81,8 @@ PROGRAMS += tools/fast/fast_server
 PROGRAMS += tools/fix/fix_client
 PROGRAMS += tools/fix/fix_server
 PROGRAMS += tools/fix/fix_perf
+PROGRAMS += tools/sim/market
+PROGRAMS += tools/sim/trader
 PROGRAMS += tools/tape/tape
 
 DEFINES =
@@ -105,6 +107,12 @@ ifeq ($(uname_S),Darwin)
 	COMPAT_OBJS += lib/compat/clock_gettime.o
 endif
 
+market_EXTRA_DEPS += lib/die.o
+market_EXTRA_DEPS += tools/sim/engine.o
+market_EXTRA_LIBS += -lm
+
+trader_EXTRA_DEPS += lib/die.o
+
 fix_client_EXTRA_DEPS += lib/die.o
 fix_client_EXTRA_DEPS += tools/fix/test.o
 fix_client_EXTRA_LIBS += -lm
diff --git a/tools/sim/engine.c b/tools/sim/engine.c
new file mode 100644
index 0000000..db1ea1d
--- /dev/null
+++ b/tools/sim/engine.c
@@ -0,0 +1,179 @@
+#include <string.h>
+#include <stdio.h>
+
+#include "engine.h"
+
+void order_book_init(struct order_book *book)
+{
+	memset(book, 0, sizeof(struct order_book));
+
+	book->best_ask = MAX_LEVEL + 1;
+	book->best_bid = 0;
+}
+
+static void order_insert(struct level *level, struct order *order)
+{
+	if (level->head != NULL) {
+		level->tail->next = order;
+		order->prev = level->tail;
+	} else {
+		level->head = order;
+		order->prev = NULL;
+	}
+
+	level->tail = order;
+	order->next = NULL;
+
+	return;
+}
+
+static void order_remove(struct level *level, struct order *order)
+{
+	struct order *prev = order->prev;
+	struct order *next = order->next;
+
+	if (prev == NULL)
+		level->head = next;
+	else
+		prev->next = next;
+
+	if (next == NULL)
+		level->tail = prev;
+	else
+		next->prev = prev;
+
+	return;
+}
+
+static int do_trade(unsigned long buyer, unsigned long seller, unsigned 
long price, size_t size)
+{
+	fprintf(stdout, "Buyer %lu Seller %lu, price %lu, size %zu\n", buyer, 
seller, price, size);
+
+	return 0;
+}
+
+static int match(struct order_book *book, struct order *order)
+{
+	struct level *level;
+	struct order *head;
+
+	if (order->side == 0) {
+		level = book->levels + order->level;
+
+		while (order->level >= book->best_ask) {
+			level = book->levels + book->best_ask;
+			head = level->head;
+
+			while (head != NULL) {
+				if (head->size < order->size) {
+					do_trade(order->trader, head->trader, book->best_ask, head->size);
+
+					order->size -= head->size;
+					head->size = 0;
+				} else {
+					do_trade(order->trader, head->trader, book->best_ask, order->size);
+
+					head->size -= order->size;
+					order->size = 0;
+				}
+
+				if (!head->size) {
+					order_remove(level, head);
+					head = level->head;
+				}
+
+				if (!order->size)
+					goto done;
+			}
+
+			book->best_ask++;
+		}
+
+		if (order->level > book->best_bid)
+			book->best_bid = order->level;
+	} else {
+		level = book->levels + order->level;
+
+		while (order->level <= book->best_bid) {
+			level = book->levels + book->best_bid;
+			head = level->head;
+
+			while (head != NULL) {
+				if (head->size < order->size) {
+					do_trade(head->trader, order->trader, book->best_bid, head->size);
+
+					order->size -= head->size;
+					head->size = 0;
+				} else {
+					do_trade(head->trader, order->trader, book->best_bid, order->size);
+
+					head->size -= order->size;
+					order->size = 0;
+				}
+
+				if (!head->size) {
+					order_remove(level, head);
+					head = level->head;
+				}
+
+				if (!order->size)
+					goto done;
+			}
+
+			book->best_bid--;
+		}
+
+		if (order->level < book->best_ask)
+			book->best_ask = order->level;
+	}
+
+	order_insert(level, order);
+
+done:
+	return 0;
+}
+
+int do_limit(struct order_book *book, struct order *limit)
+{
+	struct order *order;
+
+	if (book->orders_num >= MAX_ORDERS)
+		goto reject;
+
+	if (limit->level > MAX_LEVEL || limit->level < 1)
+		goto reject;
+
+	if (!limit->size)
+		goto reject;
+
+	order = book->orders + book->orders_num;
+	memcpy(order, limit, sizeof(struct order));
+	book->orders_num++;
+
+	return match(book, order);
+
+reject:
+	return -1;
+}
+
+int do_cancel(struct order_book *book, unsigned long id)
+{
+	struct order *order = book->orders + id;
+	struct level *level;
+
+	if (!book->orders_num || id >= book->orders_num)
+		goto reject;
+
+	if (!order->size)
+		goto reject;
+
+	level = book->levels + order->level;
+	order_remove(level, order);
+
+	order->size = 0;
+
+	return 0;
+
+reject:
+	return -1;
+}
diff --git a/tools/sim/engine.h b/tools/sim/engine.h
new file mode 100644
index 0000000..dbac751
--- /dev/null
+++ b/tools/sim/engine.h
@@ -0,0 +1,30 @@
+#define	MAX_ORDERS	10000
+#define	MAX_LEVEL	100
+
+struct order {
+	unsigned long trader;
+	unsigned long level;
+	struct order *next;
+	struct order *prev;
+	unsigned long side;
+	size_t size;
+};
+
+struct level {
+	struct order *head;
+	struct order *tail;
+};
+
+struct order_book {
+	struct level levels[MAX_LEVEL + 2];
+	struct order orders[MAX_ORDERS];
+	unsigned long orders_num;
+	unsigned long best_ask;
+	unsigned long best_bid;
+	unsigned long book_id;
+	char code[32];
+};
+
+int do_cancel(struct order_book *book, unsigned long order_id);
+int do_limit(struct order_book *book, struct order *order);
+void order_book_init(struct order_book *book);
diff --git a/tools/sim/market.c b/tools/sim/market.c
new file mode 100644
index 0000000..7ae3b5a
--- /dev/null
+++ b/tools/sim/market.c
@@ -0,0 +1,284 @@
+#include "libtrading/proto/fix_message.h"
+#include "libtrading/proto/fix_session.h"
+#include "libtrading/array.h"
+#include "libtrading/die.h"
+#include "market.h"
+
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/epoll.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+
+#define	EPOLL_MAXEVENTS	100
+
+static const char *program;
+
+static int socket_setopt(int sockfd, int level, int optname, int optval)
+{
+	return setsockopt(sockfd, level, optname, (void *) &optval, sizeof(optval));
+}
+
+static void usage(void)
+{
+	fprintf(stderr, "\n usage: %s -p port\n\n", program);
+
+	exit(EXIT_FAILURE);
+}
+
+static void market_init(struct market *market)
+{
+	memset(market, 0, sizeof(struct market));
+
+	order_book_init(&market->book);
+
+	return;
+}
+
+static inline struct fix_field *fix_field_by_tag(struct fix_message *msg,
enum fix_tag tag)
+{
+	struct fix_field *field = NULL;
+	int i;
+
+	for (i = 0; i < msg->nr_fields; i++) {
+		field = msg->fields + i;
+
+		if (field->tag == tag)
+			return field;
+	}
+
+	return NULL;
+}
+
+static int do_income(struct market *market, struct fix_session_cfg *cfg)
+{
+	struct fix_message *recv_msg;
+	struct fix_message send_msg;
+	struct fix_field *field;
+	struct trader *trader;
+	struct order order;
+
+	trader = trader_by_sock(market, cfg->sockfd);
+	if (!trader) {
+		trader = trader_new(market);
+		if (!trader)
+			goto fail;
+
+		trader->session = fix_session_new(cfg);
+		if (!trader->session)
+			goto fail;
+	}
+
+	recv_msg = fix_session_recv(trader->session, 0);
+	if (!recv_msg)
+		goto done;
+
+	send_msg.nr_fields = 0;
+
+	if (!trader->active) {
+		if (!fix_message_type_is(recv_msg, FIX_MSG_TYPE_LOGON))
+			goto logout;
+
+		field = fix_field_by_tag(recv_msg, SenderCompID);
+		if (!field)
+			goto logout;
+
+		strncpy(trader->name, field->string_value, 32);
+
+		trader->active = true;
+
+		send_msg.type = FIX_MSG_TYPE_LOGON;
+		fix_session_send(trader->session, &send_msg, 0);
+
+		goto done;
+	}
+
+	if (fix_session_admin(trader->session, recv_msg))
+		goto done;
+
+	if (fix_message_type_is(recv_msg, FIX_MSG_TYPE_LOGOUT)) {
+		goto logout;
+	} else if (fix_message_type_is(recv_msg, FIX_MSG_TYPE_NEW_ORDER_SINGLE)) {
+		order.trader = trader->id;
+
+		field = fix_field_by_tag(recv_msg, Side);
+		if (!field)
+			goto done;
+
+		if (field->string_value[0] == '1')
+			order.side = 0;
+		else if (field->string_value[0] == '2')
+			order.side = 1;
+		else
+			goto done;
+
+		field = fix_field_by_tag(recv_msg, Price);
+		if (!field)
+			goto done;
+
+		order.level = round(field->float_value);
+
+		field = fix_field_by_tag(recv_msg, OrderQty);
+		if (!field)
+			goto done;
+
+		order.size = round(field->float_value);
+
+		if (do_limit(&market->book, &order))
+			goto done;
+	}
+
+done:
+	return 0;
+
+fail:
+	return -1;
+
+logout:
+	send_msg.type = FIX_MSG_TYPE_LOGOUT;
+	fix_session_send(trader->session, &send_msg, 0);
+
+	trader_free(trader);
+	close(cfg->sockfd);
+
+	return 0;
+}
+
+int main(int argc, char *argv[])
+{
+	struct epoll_event *events = NULL;
+	struct market *market = NULL;
+	struct fix_session_cfg cfg;
+	struct epoll_event event;
+	struct sockaddr_in sa;
+	int sockfd = -1;
+	int port = 0;
+	int efd = -1;
+	int ret = -1;
+	int num;
+	int opt;
+	int i;
+
+	program = basename(argv[0]);
+
+	while ((opt = getopt(argc, argv, "p:")) != -1) {
+		switch (opt) {
+		case 'p':
+			port = atoi(optarg);
+			break;
+		default:
+			usage();
+		}
+	}
+
+	if (!port)
+		usage();
+
+	market = calloc(1, sizeof(struct market));
+	if (!market)
+		die("couldn't create a market");
+
+	market_init(market);
+	sockfd = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
+	if (sockfd < 0)
+		die("cannot create a socket");
+
+	if (socket_setopt(sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0)
+		die("cannot set a socket option: TCP_NODELAY");
+
+	if (socket_setopt(sockfd, SOL_SOCKET, SO_REUSEADDR, 1) < 0)
+		die("cannot set a socket option: SO_REUSEADDR");
+
+	sa = (struct sockaddr_in) {
+		.sin_family		= AF_INET,
+		.sin_port		= htons(port),
+		.sin_addr		= (struct in_addr) {
+			.s_addr			= INADDR_ANY,
+		},
+	};
+
+	if (bind(sockfd, (const struct sockaddr *)&sa, sizeof(struct sockaddr_in)) < 0)
+		die("bind failed");
+
+	fprintf(stdout, "Market is running on port %d...\n", port);
+
+	if (listen(sockfd, 10) < 0)
+		die("listen failed");
+
+	efd = epoll_create1(0);
+	if (efd < 0)
+		die("epoll_create1 failed");
+
+	events = calloc(EPOLL_MAXEVENTS, sizeof(struct epoll_event));
+	if (!events)
+		die("calloc failed");
+
+	memset(&event, 0, sizeof(struct epoll_event));
+
+	event.data.fd = sockfd;
+	event.events = EPOLLIN | EPOLLRDHUP;
+	if (epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &event)) {
+		fprintf(stderr, "epoll_ctl failed\n");
+		goto exit;
+	}
+
+	strncpy(cfg.sender_comp_id, "SELLSIDE", ARRAY_SIZE(cfg.sender_comp_id));
+	strncpy(cfg.target_comp_id, "BUYSIDE", ARRAY_SIZE(cfg.target_comp_id));
+	cfg.dialect = &fix_dialects[FIX_4_4];
+
+	while (true) {
+		num = epoll_wait(efd, events, EPOLL_MAXEVENTS, -1);
+		if (num < 0) {
+			fprintf(stderr, "epoll_wait failed\n");
+			goto exit;
+		}
+
+		for (i = 0; i < num; i++) {
+			if (events[i].data.fd == sockfd) {
+				if (events[i].events & EPOLLERR ||
+					events[i].events & EPOLLHUP ||
+						events[i].events & EPOLLRDHUP) {
+					fprintf(stderr, "listening socket error\n");
+					goto exit;
+				}
+
+				cfg.sockfd = accept(sockfd, NULL, NULL);
+				if (cfg.sockfd < 0) {
+					fprintf(stderr, "accept failed\n");
+					goto exit;
+				}
+
+				event.data.fd = cfg.sockfd;
+				event.events = EPOLLIN | EPOLLRDHUP;
+				if (epoll_ctl(efd, EPOLL_CTL_ADD, cfg.sockfd, &event) < 0) {
+					fprintf(stderr, "epoll_ctl failed\n");
+					goto exit;
+				}
+			} else {
+				cfg.sockfd = events[i].data.fd;
+
+				if (events[i].events & EPOLLERR ||
+					events[i].events & EPOLLHUP ||
+						events[i].events & EPOLLRDHUP) {
+					trader_free(trader_by_sock(market, cfg.sockfd));
+					close(cfg.sockfd);
+
+					continue;
+				}
+
+				if (do_income(market, &cfg))
+					close(cfg.sockfd);
+			}
+		}
+	}
+
+exit:
+	close(sockfd);
+	free(events);
+	free(market);
+
+	return ret;
+}
diff --git a/tools/sim/market.h b/tools/sim/market.h
new file mode 100644
index 0000000..b5ba14e
--- /dev/null
+++ b/tools/sim/market.h
@@ -0,0 +1,96 @@
+#include <string.h>
+
+#include "engine.h"
+
+#define	MAX_TRADERS	100
+
+struct trader {
+	struct fix_session *session;
+	unsigned long id;
+	char name[32];
+	bool active;
+};
+
+struct market {
+	struct trader traders[MAX_TRADERS];
+	unsigned long traders_num;
+	struct order_book book;
+};
+
+static inline struct trader *trader_by_sock(struct market *market, int sockfd)
+{
+	struct trader *traders = market->traders;
+	struct trader *trader;
+	int i;
+
+	for (i = 0; i < market->traders_num; i++) {
+		trader = traders + i;
+
+		if (trader->session && trader->session->sockfd == sockfd)
+			return trader;
+	}
+
+	return NULL;
+}
+
+static inline struct trader *trader_by_name(struct market *market, char *name)
+{
+	struct trader *traders = market->traders;
+	struct trader *trader;
+	int i;
+
+	for (i = 0; i < market->traders_num; i++) {
+		trader = traders + i;
+
+		if (!strcmp(trader->name, name))
+			return trader;
+	}
+
+	return NULL;
+}
+
+static inline struct trader *trader_by_id(struct market *market, unsigned
long id)
+{
+	struct trader *traders = market->traders;
+	struct trader *trader;
+	int i;
+
+	for (i = 0; i < market->traders_num; i++) {
+		trader = traders + i;
+
+		if (trader->id == id)
+			return trader;
+	}
+
+	return NULL;
+}
+
+static inline struct trader *trader_new(struct market *market)
+{
+	struct trader *traders = market->traders;
+	struct trader *trader;
+
+	if (market->traders_num >= MAX_TRADERS)
+		return NULL;
+
+	trader = traders + market->traders_num;
+	trader->id = market->traders_num++;
+	trader->active = false;
+
+	return trader;
+}
+
+static inline void trader_free(struct trader *trader)
+{
+	if (!trader)
+		goto done;
+
+	if (trader->session)
+		fix_session_free(trader->session);
+
+	trader->session = NULL;
+	trader->active = false;
+
+done:
+	return;
+}
diff --git a/tools/sim/trader.c b/tools/sim/trader.c
new file mode 100644
index 0000000..5b42a6d
--- /dev/null
+++ b/tools/sim/trader.c
@@ -0,0 +1,188 @@
+#include "libtrading/proto/fix_message.h"
+#include "libtrading/proto/fix_session.h"
+
+#include "libtrading/array.h"
+#include "libtrading/die.h"
+
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <string.h>
+#include <libgen.h>
+#include <stdio.h>
+#include <netdb.h>
+#include <errno.h>
+
+#define	FIX_FIELDS_NUM	32
+
+static const char *program;
+
+static void usage(void)
+{
+	fprintf(stderr, "\n usage: %s -h host -p port\n\n", program);
+
+	exit(EXIT_FAILURE);
+}
+
+static int socket_setopt(int sockfd, int level, int optname, int optval)
+{
+	return setsockopt(sockfd, level, optname, (void *) &optval, sizeof(optval));
+}
+
+static int do_trader(struct fix_session_cfg *cfg)
+{
+	struct fix_session *session = NULL;
+	struct fix_field *fields = NULL;
+	double price, qty;
+	int ret = -1;
+	int nr = 0;
+	int side;
+
+	session = fix_session_new(cfg);
+	if (!session) {
+		fprintf(stderr, "FIX session cannot be created\n");
+		goto exit;
+	}
+
+	ret = fix_session_logon(session);
+	if (ret) {
+		fprintf(stderr, "Trader Logon failed\n");
+		goto exit;
+	}
+
+	fprintf(stdout, "Trader Logged on\n");
+
+	fields = calloc(FIX_FIELDS_NUM, sizeof(struct fix_field));
+	if (!fields) {
+		fprintf(stderr, "Cannot allocate memory\n");
+		goto exit;
+	}
+
+	while (true) {
+		nr = 0;
+
+		fprintf(stdout, "Buy/Sell/Quit (1/2/0): ");
+		if (scanf("%d", &side) != 1)
+			continue;
+
+		if (side == 1)
+			fields[nr++] = FIX_STRING_FIELD(Side, "1");
+		else if (side == 2)
+			fields[nr++] = FIX_STRING_FIELD(Side, "2");
+		else if (side == 0)
+			break;
+		else
+			continue;
+
+		fprintf(stdout, "Price: ");
+		if (scanf("%lf", &price) != 1)
+			continue;
+
+		if (price <= 0)
+			continue;
+
+		fields[nr++] = FIX_FLOAT_FIELD(Price, price);
+
+		fprintf(stdout, "Quantity: ");
+		if (scanf("%lf", &qty) != 1)
+			continue;
+
+		if (qty <= 0)
+			continue;
+
+		fields[nr++] = FIX_FLOAT_FIELD(OrderQty, qty);
+
+		fix_session_new_order_single(session, fields, nr);
+	}
+
+	ret = fix_session_logout(session, NULL);
+	if (!ret)
+		fprintf(stdout, "Trader logged out\n");
+	else
+		fprintf(stderr, "Trader logout failed\n");
+
+exit:
+	fix_session_free(session);
+	free(fields);
+
+	return ret;
+}
+
+int main(int argc, char *argv[])
+{
+	struct fix_session_cfg cfg;
+	const char *host = NULL;
+	struct sockaddr_in sa;
+	int saved_errno = 0;
+	struct hostent *he;
+	int port = 0;
+	int ret = 0;
+	char **ap;
+	int opt;
+
+	program = basename(argv[0]);
+
+	while ((opt = getopt(argc, argv, "h:p:")) != -1) {
+		switch (opt) {
+		case 'p':
+			port = atoi(optarg);
+			break;
+		case 'h':
+			host = optarg;
+			break;
+		default: /* '?' */
+			usage();
+			exit(EXIT_FAILURE);
+		}
+	}
+
+	if (!port || !host)
+		usage();
+
+	strncpy(cfg.target_comp_id, "SELLSIDE", ARRAY_SIZE(cfg.target_comp_id));
+	strncpy(cfg.sender_comp_id, "BUYSIDE", ARRAY_SIZE(cfg.sender_comp_id));
+	cfg.dialect	= &fix_dialects[FIX_4_4];
+
+	he = gethostbyname(host);
+	if (!he)
+		error("Unable to look up %s (%s)", host, hstrerror(h_errno));
+
+	for (ap = he->h_addr_list; *ap; ap++) {
+		cfg.sockfd = socket(he->h_addrtype, SOCK_STREAM, IPPROTO_TCP);
+		if (cfg.sockfd < 0) {
+			saved_errno = errno;
+			continue;
+		}
+
+		sa = (struct sockaddr_in) {
+			.sin_family		= he->h_addrtype,
+			.sin_port		= htons(port),
+		};
+		memcpy(&sa.sin_addr, *ap, he->h_length);
+
+		if (connect(cfg.sockfd, (const struct sockaddr *)&sa, sizeof(struct 
sockaddr_in)) < 0) {
+			saved_errno = errno;
+			close(cfg.sockfd);
+			cfg.sockfd = -1;
+			continue;
+		}
+		break;
+	}
+
+	if (cfg.sockfd < 0)
+		error("Unable to connect to a socket (%s)", strerror(saved_errno));
+
+	if (socket_setopt(cfg.sockfd, IPPROTO_TCP, TCP_NODELAY, 1) < 0)
+		die("cannot set socket option TCP_NODELAY");
+
+	ret = do_trader(&cfg);
+
+	shutdown(cfg.sockfd, SHUT_RDWR);
+
+	if (close(cfg.sockfd) < 0)
+		die("close");
+
+	return ret;
+}
-- 
1.7.9.5

Re: [RFC/PATCH] Primitive financial exchange

From:
Marat Stanichenko
Date:
2014-01-23 @ 18:26
> I am still seeing this with the above patch applied

Shame on me Pekka :(
Please, apply this one also

1. Initialize epoll_event structure with zeros to avoid valgrind warning messages
	Syscall param epoll_ctl(event) points to uninitialised byte(s)

2. The number of fields of the FIX messages sent must be set explicitly

Signed-off-by: Marat Stanichenko <mstanichenko@gmail.com>
---
 tools/sim/market.c |    4 ++++
 1 file changed, 4 insertions(+)

diff --git a/tools/sim/market.c b/tools/sim/market.c
index 19cedf9..7ae3b5a 100644
--- a/tools/sim/market.c
+++ b/tools/sim/market.c
@@ -76,6 +76,8 @@ static int do_income(struct market *market, struct 
fix_session_cfg *cfg)
 	if (!recv_msg)
 		goto done;
 
+	send_msg.nr_fields = 0;
+
 	if (!trader->active) {
 		if (!fix_message_type_is(recv_msg, FIX_MSG_TYPE_LOGON))
 			goto logout;
@@ -214,6 +216,8 @@ int main(int argc, char *argv[])
 	if (!events)
 		die("calloc failed");
 
+	memset(&event, 0, sizeof(struct epoll_event));
+
 	event.data.fd = sockfd;
 	event.events = EPOLLIN | EPOLLRDHUP;
 	if (epoll_ctl(efd, EPOLL_CTL_ADD, sockfd, &event)) {
-- 
1.7.9.5