Compare commits

...
Author SHA1 Message Date
Eric Harding e39313ecf6 Add merge_synthetic_book sample test code 2019-11-15 17:42:49 -05:00
Eric Harding 8095100bee p/invoke for synthetic book computation 2019-11-15 16:11:02 -05:00
6 changed files with 225 additions and 45 deletions

No files matched your search

@@ -4,6 +4,8 @@ using System.Runtime.InteropServices;
using System.Security;
using Google.Protobuf;
using security_key = System.UInt32;
using bts.co2;
using System.Linq;
namespace mbts.co2
{
@@ -85,6 +87,7 @@ namespace mbts.co2
[DllImport("libspark")]
internal unsafe static extern void post_on_scan_security_thread(Action callback);
}
internal static class Co2NativeTestMethods
@@ -133,5 +136,59 @@ namespace mbts.co2
[DllImport("libspark")]
internal static extern void co2_test_get_constants(out ulong max_depth, out ulong max_implied_depth, out sbyte nan_exponent);
internal unsafe delegate void raw_receive_pb_or_string(byte* buffer, int buffer_size);
[DllImport("libspark")]
private static unsafe extern void raw_co2_compute_synthetic_book(
byte[] security_buffer,
int security_length,
IntPtr[] leg_books,
int[] leg_lengths,
int leg_books_length,
raw_receive_pb_or_string callback);
internal static unsafe BookMessageImmutable co2_compute_synthetic_book(SecurityImmutable synthetic, params BookMessageImmutable[] books) {
BookMessage result = null;
string error = null;
var security_buffer = synthetic.ToByteArray();
// var h = GCHandle.Alloc(null, GCHandleType.Pinned).
var book_buffers =
books
.Select(b => b.ToByteArray())
.ToArray();
GCHandle[] book_buffer_handles =
book_buffers
.Select(b => GCHandle.Alloc(b,GCHandleType.Pinned))
.ToArray();
IntPtr[] book_buffer_pointers =
book_buffer_handles
.Select(h => h.AddrOfPinnedObject())
.ToArray();
var lengths =
book_buffers
.Select(b => b.Length)
.ToArray();
raw_co2_compute_synthetic_book(
security_buffer,
security_buffer.Length,
book_buffer_pointers,
lengths,
book_buffers.Length,
(byte* buffer, int length) => {
if (length < 0) {
error = new string((char*)buffer,0,-1 * length);
}
else {
result = Co2NativeMethods.deserialize<BookMessage>(buffer, (ulong)length);
}
});
if(error != null) {
// C# style multiple return values
throw new Exception(error);
}
return new BookMessageImmutable(result);
}
}
}
+11
View File
@@ -52,6 +52,17 @@ namespace mbts.co2 {
return _mantissa.GetHashCode();
}
public static IntPrice from_double(double price, sbyte precision) {
if (double.IsNaN(price)) return NaN;
if (Math.Abs(price%1) != 0) {
double rounding = 0.5 * Math.Pow(10,-precision);
price += (price > 0) ? rounding : -rounding;
price *= Math.Pow(10,precision);
return new IntPrice((long)price, (sbyte)-precision);
}
return new IntPrice((long)price, 0);
}
public static IntPrice NaN = new IntPrice(0,127);
}
+40
View File
@@ -286,6 +286,46 @@ namespace mbts.co2.tests
assert_about_equal(dt_utc_now.Second, DateTime.UtcNow.Second);
}
[Test,Ignore("Sample code")]
public void merge_synthetic_book() {
Security security = new Security();
PriceSizeMessage mkPriceSize(double price, UInt32 size) {
var iprice = IntPrice.from_double(price, 11);
return new PriceSizeMessage() {
Mantissa = iprice.mantissa,
Exponent = iprice.exponent,
Size = size
};
}
IEnumerable<PriceSizeMessage> mkPrices(params (double,uint)[] args) {
return args.Select((ps) => mkPriceSize(ps.Item1,ps.Item2));
}
var leg1 = new BookMessage() {
Bids = {
mkPriceSize(2000, 10),
mkPriceSize(1999.75, 5),
mkPriceSize(1999.5, 7)
},
Asks = {
mkPriceSize(2000.25,15),
mkPriceSize(2000.5, 5),
mkPriceSize(2001,10)
}
}.immutable();
var leg2 = new BookMessage() {
Bids = {
mkPrices( (2003, 5), (2002.75, 15), (2002.5, 12))
},
Asks = {
mkPrices( (2003.25, 12), (2003.5, 3), (2003.75,5))
}
}.immutable();
var result = Co2NativeTestMethods.co2_compute_synthetic_book(security.immutable(), leg1, leg2);
Assert.IsTrue(result.Bids.First().Size == 5);
Assert.IsTrue(result.Asks.First().Size == 2);
}
}
}
+69 -1
View File
@@ -4,6 +4,7 @@
#include "lib/config/config.h"
#include "lib/md/protos/co2.pb.h"
#include "lib/utils/TimeStamp.h"
#include "lib/md/test/md_test_utils.h"
#include <tuple>
#include <vector>
@@ -15,7 +16,7 @@ using namespace co2;
std::vector<MDProducerHandle> create_test_securities(MDProducer& producer, const char* postfix = "M");
void assign_ids(std::vector<MDProducerHandle>::iterator begin, std::vector<MDProducerHandle>::iterator end, security_key start_key = 1000);
// void assign_ids(std::vector<MDProducerHandle>::iterator begin, std::vector<MDProducerHandle>::iterator end, security_key start_key = 1000);
extern "C" BTS_EXPORT void co2_get_1234_handle(size_t& size, MDConsumerHandle& handle, uintptr_t* hotaddr, uintptr_t* coldaddr, uintptr_t* headeraddr) {
// Test that marshaling is working
@@ -169,5 +170,72 @@ extern "C" BTS_EXPORT void co2_get_sample_datetime(datetime::LLDateTime& now, da
utc_now = datetime::ll_now();
}
// Book compute_synthetic_book(const api::Security& security, const std::vector<Book>& leg_books);
typedef void(*raw_receive_pb_or_string)(const char*, int);
extern "C" BTS_EXPORT void raw_co2_compute_synthetic_book(
const char* security_buffer,
int security_len,
const char** book_buffers,
const int* book_lengths,
int book_buffers_length,
raw_receive_pb_or_string cb) {
// Deserialize Security
api::Security security;
if (!security.ParseFromArray(security_buffer, security_len)) {
const char* err = "Security parse error";
cb(err, -1 * strlen(err));
return;
}
// Deserialize BookMessage and convert to Book
std::vector<Book> books;
for (int x = 0; x < book_buffers_length; ++x) {
BookMessage bm;
if (!bm.ParseFromArray(book_buffers[x], book_lengths[x])) {
auto err = fmt::format("Book parse error: {}", x);
cb(err.c_str(), -1 * err.size());
return;
}
Book book;
for (const auto& bid : bm.bids()) {
IntPrice ip { bid.mantissa(), (int8_t)bid.exponent() };
book.bids.push_back({ ip.price(), bid.size() });
}
for (const auto& ask : bm.asks()) {
IntPrice ip { ask.mantissa(), (int8_t)ask.exponent() };
book.asks.push_back({ ip.price(), ask.size() });
}
books.push_back(book);
}
// Make the call we wanted to make in the first place
Book bres = compute_synthetic_book(security, books);
// Copy back to a BookMessage
BookMessage bmres;
for (const auto & bid : bres.bids) {
PriceSizeMessage& ps = *bmres.add_bids();
auto ip = IntPrice_from_double<11>(bid.price);
ps.set_mantissa(ip.mantissa());
ps.set_exponent(ip.exponent());
ps.set_size(bid.size);
}
for (const auto & ask : bres.asks) {
PriceSizeMessage& ps = *bmres.add_asks();
auto ip = IntPrice_from_double<11>(ask.price);
ps.set_mantissa(ip.mantissa());
ps.set_exponent(ip.exponent());
ps.set_size(ask.size);
}
// serialize to string
auto str = bmres.SerializeAsString();
cb(str.c_str(), str.size());
}
}}}//namespace
#endif
+48
View File
@@ -1,5 +1,8 @@
#include "md_test_utils.h"
#include "../MDProducer.h"
#include "lib/utils/sane_algorithm.h"
#include "../MDConsumerHandle.h"
#include "Co2TestInstrument.h"
#include <vector>
#include <string>
@@ -38,4 +41,49 @@ void assign_ids(std::vector<MDProducerHandle>::iterator begin, std::vector<MDPro
}
}
Book compute_synthetic_book(const api::Security& security, const std::vector<Book>& leg_books) {
auto synthetic = std::make_shared<SyntheticStrategy>();
synthetic->security.CopyFrom(security);
std::vector<Co2TestInstrument> leg_instruments;
for (const auto& leg : synthetic->security.synthetic().legs()) {
const auto& security = leg.security();
Co2TestInstrument instr(security.symbol(), securitytype_of(security));
convert_security(security, instr.cold().security, true);
leg_instruments.push_back(instr);
synthetic->legs.push_back(instr.consumer());
}
// Write leg books
for (size_t x = 0; x < leg_books.size(); ++x) {
std::array<PriceSize,COCONUT_MAX_DEPTH> bids = {}, asks = {};
std::transform(
leg_books[x].bids.begin(),
leg_books[x].bids.end(),
bids.begin(),
[](const PS& ps) {
return PriceSize_from_double<7>(ps.price, ps.size);
});
std::transform(
leg_books[x].asks.begin(),
leg_books[x].asks.end(),
asks.begin(),
[](const PS& ps) {
return PriceSize_from_double<7>(ps.price, ps.size);
});
auto ts = TimeStamp::now();
leg_instruments[x].producer().update_direct(ts, bids, asks);
}
// Read synthetic book back
MDConsumerHandle synthetic_handle { synthetic };
std::array<PriceSize,COCONUT_MAX_DEPTH> raw_bids, raw_asks;
TimeStamp t;
synthetic_handle.get_direct_book(t, COCONUT_MAX_DEPTH, raw_bids.data(), raw_asks.data());
Book result {};
sane::map(raw_bids, result.bids, [](PriceSize ps) -> PS { return {ps.price(), ps.size()}; });
sane::map(raw_asks, result.asks, [](PriceSize ps) -> PS { return {ps.price(), ps.size()}; });
return result;
}
}}}//namespace
@@ -142,50 +142,6 @@ void set_leg_params(SyntheticStrategy& strat, uint32_t security_key, const spark
}
Book compute_synthetic_book(const api::Security& security, const std::vector<Book>& leg_books) {
auto synthetic = std::make_shared<SyntheticStrategy>();
synthetic->security.CopyFrom(security);
vector<Co2TestInstrument> leg_instruments;
for (const auto& leg : synthetic->security.synthetic().legs()) {
const auto& security = leg.security();
Co2TestInstrument instr(security.symbol(), securitytype_of(security));
convert_security(security, instr.cold().security, true);
leg_instruments.push_back(instr);
synthetic->legs.push_back(instr.consumer());
}
// Write leg books
for (size_t x = 0; x < leg_books.size(); ++x) {
std::array<PriceSize,COCONUT_MAX_DEPTH> bids = {}, asks = {};
std::transform(
leg_books[x].bids.begin(),
leg_books[x].bids.end(),
bids.begin(),
[](const PS& ps) {
return PriceSize_from_double<7>(ps.price, ps.size);
});
std::transform(
leg_books[x].asks.begin(),
leg_books[x].asks.end(),
asks.begin(),
[](const PS& ps) {
return PriceSize_from_double<7>(ps.price, ps.size);
});
auto ts = TimeStamp::now();
leg_instruments[x].producer().update_direct(ts, bids, asks);
}
// Read synthetic book back
MDConsumerHandle synthetic_handle { synthetic };
std::array<PriceSize,COCONUT_MAX_DEPTH> raw_bids, raw_asks;
TimeStamp t;
synthetic_handle.get_direct_book(t, COCONUT_MAX_DEPTH, raw_bids.data(), raw_asks.data());
Book result {};
sane::map(raw_bids, result.bids, [](PriceSize ps) -> PS { return {ps.price(), ps.size()}; });
sane::map(raw_asks, result.asks, [](PriceSize ps) -> PS { return {ps.price(), ps.size()}; });
return result;
}
///////////////////////
// Test Cases