This library provides an event-machine based FIX server and client connection implementation.
Create an EM::Connection
subclass and include the FE::ClientConnection
module. You will then have to implement
the callbacks for the various message types you are interested in.
require 'fix/engine'
module Referee
class FixConnection < EM::Connection
include FE::ClientConnection
attr_accessor :exchange
#
# When a logon message is received we request a market snapshot
# and subscribe for continuous updates
#
def on_logon(msg)
mdr = FP::Messages::MarketDataRequest.new
mdr.md_req_id = 'foo'
mdr.subscription_request_type = :updates
mdr.market_depth = :full
mdr.md_update_type = :incremental
mdr.instruments.build do |i|
i.symbol = 'EUR/XBT'
end
[:bid, :ask, :trade, :open, :vwap, :close].each do |mdet|
mdr.md_entry_types.build do |m|
m.md_entry_type = mdet
end
end
send_msg(mdr)
end
# Called when a market data snapshot is received
def on_market_data_snapshot(msg)
update_book_with(msg)
end
# Called upon each subsequent update
def on_market_data_incremental_refresh(msg)
update_book_with(msg)
end
# Update the local order book copy with the new data
def update_book_with(msg)
msg.md_entries.each do |mde|
exchange.book[mde.md_entry_type].set_depth_at(BigDecimal(mde.md_entry_px), BigDecimal(mde.md_entry_size))
end
end
end
end
Once your connection class has been created you can establish a connection in a running EventMachine reactor. See the referee gem for the full code example.
require 'referee/exchange'
require 'referee/fix_connection'
module Referee
module Exchanges
class Paymium < Referee::Exchange
FIX_SERVER = 'fix.paymium.com'
FIX_PORT = 8359
def symbol
'PAYM'
end
def currency
'EUR'
end
def connect
FE::Logger.logger.level = Logger::WARN
EM.connect(FIX_SERVER, FIX_PORT, FixConnection) do |conn|
conn.target_comp_id = 'PAYMIUM'
conn.comp_id = 'BC-U458625'
conn.username = 'BC-U458625'
conn.exchange = self
end
end
end
end
end
You can start a simple FIX acceptor that will maintain a session by running the fix-engine
executable.
The basic FIX acceptor requires a COMP_ID
environment variable to be set.
$ COMP_ID=MY_COMP_ID fix-engine
> D, [2015-01-07T12:47:07.807867 #87486] DEBUG -- : Starting FIX engine v0.0.31, listening on <0.0.0.0:8359>, exit with <Ctrl-C>
> D, [2015-01-07T12:47:12.379787 #87486] DEBUG -- : Client connected <127.0.0.1:54204>, expecting logon message in the next 10s
> D, [2015-01-07T12:47:12.816626 #87486] DEBUG -- : Received a <Fix::Protocol::Messages::Logon> from <127.0.0.1:54204> with sequence number <1>
> D, [2015-01-07T12:47:12.820093 #87486] DEBUG -- : Peer authenticated as <JAVA_TESTS> with heartbeat interval of <30s> and message sequence number start <1>
> D, [2015-01-07T12:47:12.820259 #87486] DEBUG -- : Heartbeat interval for <127.0.0.1:54204> : <30s>
> D, [2015-01-07T12:47:12.820899 #87486] DEBUG -- : Sending <Fix::Protocol::Messages::Logon> to <127.0.0.1:54204> with sequence number <1>
In order to handle business messages appropriately you need to implement a connection handler
that includes the FE::ServerConnection
module and use that as a connection handler.
class MyHandler < EM::Connection
include FE::ServerConnection
def on_market_data_request
# Fetch market data and send the relevant response
# ...
end
end
server = FE::Server.new('127.0.0.1', 8095, MyHandler) do |conn|
conn.comp_id = 'MY_COMP_ID'
end
# This will also start an EventMachine reactor
server.run!
# This would be used inside an already-running reactor
EM.run do
server.start_server
end