Compare commits
9 commits
6fc5502b99
...
7c2a25a6b0
Author | SHA1 | Date | |
---|---|---|---|
Bruno BELANYI | 7c2a25a6b0 | ||
Bruno BELANYI | a95df7a00f | ||
Bruno BELANYI | 654be9fc70 | ||
Bruno BELANYI | 22fe2ad421 | ||
Bruno BELANYI | 3998b9839f | ||
Bruno BELANYI | 8bba52d3b9 | ||
Bruno BELANYI | e601250e7b | ||
Bruno BELANYI | d159cfb877 | ||
Bruno BELANYI | 8474ed0c69 |
|
@ -43,6 +43,10 @@
|
||||||
pkg-config
|
pkg-config
|
||||||
];
|
];
|
||||||
|
|
||||||
|
buildInputs = with final; [
|
||||||
|
boost
|
||||||
|
];
|
||||||
|
|
||||||
checkInputs = with final; [
|
checkInputs = with final; [
|
||||||
gtest
|
gtest
|
||||||
];
|
];
|
||||||
|
|
|
@ -9,10 +9,13 @@ add_subdirectory(utils)
|
||||||
|
|
||||||
configure_file(config.h.in config.h)
|
configure_file(config.h.in config.h)
|
||||||
|
|
||||||
|
find_package(Boost REQUIRED COMPONENTS thread)
|
||||||
|
|
||||||
target_link_libraries(kraken PRIVATE
|
target_link_libraries(kraken PRIVATE
|
||||||
csv
|
csv
|
||||||
engine
|
engine
|
||||||
parse
|
parse
|
||||||
|
Boost::thread
|
||||||
)
|
)
|
||||||
|
|
||||||
install(TARGETS kraken)
|
install(TARGETS kraken)
|
||||||
|
|
|
@ -66,6 +66,6 @@ struct FlushOrder {
|
||||||
auto operator<=>(FlushOrder const&) const = default;
|
auto operator<=>(FlushOrder const&) const = default;
|
||||||
};
|
};
|
||||||
|
|
||||||
using Order = std::variant<TradeOrder, CancelOrder, FlushOrder>;
|
using Order = std::variant<FlushOrder, TradeOrder, CancelOrder>;
|
||||||
|
|
||||||
} // namespace kraken
|
} // namespace kraken
|
||||||
|
|
|
@ -4,10 +4,7 @@
|
||||||
|
|
||||||
namespace kraken::csv {
|
namespace kraken::csv {
|
||||||
|
|
||||||
namespace {
|
csv_line_type read_csv_line(std::string const& line) {
|
||||||
|
|
||||||
// for convenience, use a stringstream which does not accept string_view inputs
|
|
||||||
csv_line_type parse_line(std::string const& line) {
|
|
||||||
auto parsed = csv_line_type{};
|
auto parsed = csv_line_type{};
|
||||||
|
|
||||||
auto input = std::istringstream(line);
|
auto input = std::istringstream(line);
|
||||||
|
@ -18,8 +15,6 @@ csv_line_type parse_line(std::string const& line) {
|
||||||
return parsed;
|
return parsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
csv_type read_csv(std::istream& input, CsvHeader header) {
|
csv_type read_csv(std::istream& input, CsvHeader header) {
|
||||||
auto parsed = std::vector<csv_line_type>{};
|
auto parsed = std::vector<csv_line_type>{};
|
||||||
|
|
||||||
|
@ -30,7 +25,7 @@ csv_type read_csv(std::istream& input, CsvHeader header) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
parsed.emplace_back(parse_line(std::move(line)));
|
parsed.emplace_back(read_csv_line(std::move(line)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return parsed;
|
return parsed;
|
||||||
|
|
|
@ -14,6 +14,9 @@ enum class CsvHeader {
|
||||||
KEEP,
|
KEEP,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Parse a single CSV line, no error checking.
|
||||||
|
csv_line_type read_csv_line(std::string const& input);
|
||||||
|
|
||||||
/// Parse a CSV file from an input-stream, return a vector of parsed lines.
|
/// Parse a CSV file from an input-stream, return a vector of parsed lines.
|
||||||
csv_type read_csv(std::istream& input, CsvHeader header = CsvHeader::SKIP);
|
csv_type read_csv(std::istream& input, CsvHeader header = CsvHeader::SKIP);
|
||||||
|
|
||||||
|
|
|
@ -10,6 +10,10 @@ csv::csv_type const& CsvEngineListener::output() const {
|
||||||
return output_;
|
return output_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
csv::csv_type& CsvEngineListener::output() {
|
||||||
|
return output_;
|
||||||
|
}
|
||||||
|
|
||||||
void CsvEngineListener::on_acknowledgement(User user, UserOrderId id) {
|
void CsvEngineListener::on_acknowledgement(User user, UserOrderId id) {
|
||||||
output_.emplace_back(csv::csv_line_type{
|
output_.emplace_back(csv::csv_line_type{
|
||||||
"A",
|
"A",
|
||||||
|
|
|
@ -14,6 +14,7 @@ struct CsvEngineListener : EngineListener {
|
||||||
virtual ~CsvEngineListener();
|
virtual ~CsvEngineListener();
|
||||||
|
|
||||||
csv::csv_type const& output() const;
|
csv::csv_type const& output() const;
|
||||||
|
csv::csv_type& output();
|
||||||
|
|
||||||
/// Called when a new trade or cancel order has been acknowledged.
|
/// Called when a new trade or cancel order has been acknowledged.
|
||||||
void on_acknowledgement(User user, UserOrderId id) override;
|
void on_acknowledgement(User user, UserOrderId id) override;
|
||||||
|
|
|
@ -91,10 +91,14 @@ Engine::Engine(std::shared_ptr<EngineListener> listener,
|
||||||
CrossBehaviour cross_behaviour)
|
CrossBehaviour cross_behaviour)
|
||||||
: listener_(listener), cross_behaviour_(cross_behaviour) {}
|
: listener_(listener), cross_behaviour_(cross_behaviour) {}
|
||||||
|
|
||||||
|
void Engine::process_single_order(Order const& order) {
|
||||||
|
std::visit([this](auto const& trade_order) { (*this)(trade_order); },
|
||||||
|
order);
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::process_orders(std::vector<Order> const& orders) {
|
void Engine::process_orders(std::vector<Order> const& orders) {
|
||||||
for (auto const& order : orders) {
|
for (auto const& order : orders) {
|
||||||
std::visit([this](auto const& trade_order) { (*this)(trade_order); },
|
process_single_order(order);
|
||||||
order);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -25,6 +25,9 @@ struct Engine {
|
||||||
Engine(std::shared_ptr<EngineListener> listener,
|
Engine(std::shared_ptr<EngineListener> listener,
|
||||||
CrossBehaviour cross_behaviour = CrossBehaviour::REJECT);
|
CrossBehaviour cross_behaviour = CrossBehaviour::REJECT);
|
||||||
|
|
||||||
|
/// Process a single order, triggerring the listener appropriately.
|
||||||
|
void process_single_order(Order const& order);
|
||||||
|
|
||||||
/// Process orders, triggerring the listener on each event.
|
/// Process orders, triggerring the listener on each event.
|
||||||
void process_orders(std::vector<Order> const& orders);
|
void process_orders(std::vector<Order> const& orders);
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <boost/lockfree/spsc_queue.hpp>
|
||||||
|
|
||||||
|
#include "csv/read-csv.hh"
|
||||||
#include "csv/write-csv.hh"
|
#include "csv/write-csv.hh"
|
||||||
#include "engine/csv-engine-listener.hh"
|
#include "engine/csv-engine-listener.hh"
|
||||||
#include "engine/engine.hh"
|
#include "engine/engine.hh"
|
||||||
|
@ -15,13 +19,41 @@ int main(int argc, char** argv) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto const orders = kraken::parse::parse_orders(std::cin);
|
// Up to 512 pending orders.
|
||||||
|
auto pending_orders = boost::lockfree::spsc_queue<kraken::Order>(512);
|
||||||
|
|
||||||
auto listener = std::make_shared<kraken::engine::CsvEngineListener>();
|
auto writer = std::jthread([&](std::stop_token stop_token) {
|
||||||
|
auto listener = std::make_shared<kraken::engine::CsvEngineListener>();
|
||||||
|
auto engine = kraken::engine::Engine(listener, cross_behaviour);
|
||||||
|
|
||||||
auto engine = kraken::engine::Engine(listener, cross_behaviour);
|
while (true) {
|
||||||
|
auto order = kraken::Order();
|
||||||
|
while (!pending_orders.pop(order)) {
|
||||||
|
// FIXME: busy wait
|
||||||
|
// Check that we didn't miss an order between last 'pop' and
|
||||||
|
// stop request, just in case.
|
||||||
|
if (stop_token.stop_requested() && pending_orders.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
engine.process_single_order(order);
|
||||||
|
auto& output = listener->output();
|
||||||
|
kraken::csv::write_csv(std::cout, output);
|
||||||
|
output.resize(0);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
engine.process_orders(orders);
|
auto reader = std::jthread([&]() {
|
||||||
|
for (std::string line; std::getline(std::cin, line);) {
|
||||||
|
auto const order = kraken::parse::parse_single_order(
|
||||||
|
kraken::csv::read_csv_line(line));
|
||||||
|
while (!pending_orders.push(order)) {
|
||||||
|
// FIXME: busy wait
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// EOF, bring process orders and bring
|
||||||
|
writer.request_stop();
|
||||||
|
});
|
||||||
|
|
||||||
kraken::csv::write_csv(std::cout, listener->output());
|
reader.join();
|
||||||
}
|
}
|
||||||
|
|
|
@ -57,20 +57,24 @@ CancelOrder cancel_from_raw(csv::csv_line_type const& raw_order) {
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
|
Order parse_single_order(csv::csv_line_type const& order) {
|
||||||
|
if (order[0] == "N") {
|
||||||
|
return trade_from_raw(order);
|
||||||
|
} else if (order[0] == "C") {
|
||||||
|
return cancel_from_raw(order);
|
||||||
|
} else if (order[0] == "F") {
|
||||||
|
return FlushOrder{};
|
||||||
|
} else {
|
||||||
|
throw ParseError("Not a valid order");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<Order> parse_orders(std::istream& input) {
|
std::vector<Order> parse_orders(std::istream& input) {
|
||||||
auto const raw_orders = read_csv(input, kraken::csv::CsvHeader::KEEP);
|
auto const raw_orders = read_csv(input, kraken::csv::CsvHeader::KEEP);
|
||||||
auto orders = std::vector<Order>{};
|
auto orders = std::vector<Order>{};
|
||||||
|
|
||||||
for (auto const& raw_order : raw_orders) {
|
for (auto const& raw_order : raw_orders) {
|
||||||
if (raw_order[0] == "N") {
|
orders.emplace_back(parse_single_order(raw_order));
|
||||||
orders.emplace_back(trade_from_raw(raw_order));
|
|
||||||
} else if (raw_order[0] == "C") {
|
|
||||||
orders.emplace_back(cancel_from_raw(raw_order));
|
|
||||||
} else if (raw_order[0] == "F") {
|
|
||||||
orders.emplace_back(FlushOrder{});
|
|
||||||
} else {
|
|
||||||
throw ParseError("Not a valid order");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return orders;
|
return orders;
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "book/order.hh"
|
#include "book/order.hh"
|
||||||
|
#include "csv/csv.hh"
|
||||||
|
|
||||||
namespace kraken::parse {
|
namespace kraken::parse {
|
||||||
|
|
||||||
|
@ -12,6 +13,10 @@ struct ParseError : public std::runtime_error {
|
||||||
using std::runtime_error::runtime_error;
|
using std::runtime_error::runtime_error;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// Convert a single CSV input line to an `Order`.
|
||||||
|
Order parse_single_order(csv::csv_line_type const& order);
|
||||||
|
|
||||||
|
/// Parse orders from an input stream as CSV.
|
||||||
std::vector<Order> parse_orders(std::istream& input);
|
std::vector<Order> parse_orders(std::istream& input);
|
||||||
|
|
||||||
} // namespace kraken::parse
|
} // namespace kraken::parse
|
||||||
|
|
Loading…
Reference in a new issue