Compare commits

...
4 Commits
Author SHA1 Message Date
Pete Fisher e974edfc6e Merge branch 'master' of bitbucket.org:bluetradesys/spark 2021-03-18 16:22:27 -04:00
Pete Fisher 3b7740cee0 updating submodule to latest 2021-03-18 16:22:12 -04:00
John Colvin 258d4a3fc1 import compute_synthetic_book method from spartan 2020-12-08 23:00:18 -06:00
John Colvin c53f6425cd import IntPrice arithmetic operators from spartan
The compute_synthetic_book method (also to be imported
from spartan) depends on a couple of them.
2020-12-08 22:52:28 -06:00
4 changed files with 194 additions and 15 deletions

No files matched your search

+28 -3
View File
@@ -60,10 +60,13 @@ class IntPrice {
return delta.mantissa() < 0;
}
}
bool operator <=(const IntPrice& other) {
bool operator <=(const IntPrice& other) const {
return *this == other || *this < other;
}
bool operator >=(const IntPrice& other) {
bool operator >(const IntPrice& other) const {
return other < *this;
}
bool operator >=(const IntPrice& other) const {
return !(*this < other);
}
IntPrice operator - (const IntPrice& o) const {
@@ -72,9 +75,31 @@ class IntPrice {
IntPrice operator + (const IntPrice& o) const {
return op(o, [](int64_t m1, int64_t m2) { return m1 + m2; });
}
IntPrice& operator +=(const IntPrice& o) {
auto temp = *this + o;
_mantissa = temp._mantissa;
_exponent = temp._exponent;
return *this;
}
IntPrice& operator -=(const IntPrice& o) {
auto temp = *this - o;
_mantissa = temp._mantissa;
_exponent = temp._exponent;
return *this;
}
IntPrice operator -() {
return IntPrice(-_mantissa, _exponent);
}
IntPrice operator * (const IntPrice& other) const {
return op(other, [](int64_t m1, int64_t m2) { return m1 * m2; });
}
IntPrice operator * (const int& other) const {
return IntPrice(_mantissa * other, _exponent);
}
IntPrice& operator *=(const int& other) {
_mantissa *= other;
return *this;
}
IntPrice operator / (double v) const {
return { (int64_t)(_mantissa / v), exponent() };
}
@@ -183,4 +208,4 @@ inline std::ostream& operator<<(std::ostream& os, const IntPrice& p) {
return os;
}
}}
}}
+155 -6
View File
@@ -1,9 +1,9 @@
#include "BookUtils.h"
namespace bts {
namespace orderman {
using Size = bts::md::Size;
using Side = bts::api::Side;
namespace bts::orderman {
using Size = md::Size;
using Side = api::Side;
using PriceSize = md::PriceSize;
Size BookUtils::volume_at_or_better(const Book& book, Side side, Price price) {
return volume_at_or_better(book, side, price.value());
@@ -22,5 +22,154 @@ Size BookUtils::volume_at_or_better(const Book& book, Side side, double price) {
SidePriceComp BookUtils::price_comp(Side side) {
return SidePriceComp(side);
}
} // namespace orderman
} // namespace bts
inline bool is_empty(const PriceSize& ps) {
return ps.size() == 0 || ps.int_price().is_nan();
}
void BookUtils::change_unit_qty(
const BookSide& book_in, std::vector<md::PriceSize>& book_out, int unit_qty) {
//
bts_assert(unit_qty > 0);
int mlev = 0, mlev_qty_used = 0;
std::vector<md::PriceSize> unit_lev_span;
for (uint32_t i = 0; i < book_in.size();) {
int mlev_qty_rem = book_in[i].size() - mlev_qty_used;
if (mlev_qty_rem == 0) {
mlev_qty_used = 0;
mlev++;
i++;
continue;
}
if (unit_lev_span.size() == 0) {
md::Size mlev_units_rem = mlev_qty_rem / unit_qty;
if (mlev_units_rem > 0) {
book_out.emplace_back(book_in[i].int_price(), mlev_units_rem);
mlev_qty_used += mlev_units_rem * unit_qty;
} else {
unit_lev_span.emplace_back(book_in[i].int_price(), mlev_qty_rem);
mlev_qty_used = 0;
mlev++;
i++;
}
} else {
// the unit spans across multiple [input]price levels: compute its
// [output]price. It will be in its own [output]price level.
auto unit_rem = unit_qty;
for (auto lev : unit_lev_span)
unit_rem -= lev.size();
if (mlev_qty_rem >= unit_rem) {
mlev_qty_used += unit_rem;
auto unit_price = md::INT_NAN;
for (uint32_t adj_lev = 0; adj_lev < unit_lev_span.size(); adj_lev++) {
auto leg_price = unit_lev_span[adj_lev].int_price() *
static_cast<int>(unit_lev_span[adj_lev].size());
if (unit_price.is_nan())
unit_price = leg_price;
else
unit_price += leg_price;
}
unit_lev_span.clear();
book_out.emplace_back(unit_price, 1);
}
}
}
}
void BookUtils::compute_synthetic_book(
std::vector<PriceSize>& dest,
const std::vector<std::tuple<Size, md::IntPrice, BookSide>>&
leg_mkts,
uint32_t depth) {
//
if (leg_mkts.size() == 0)
return;
struct Leg {
std::vector<PriceSize> book;
uint32_t curr_level;
Size curr_level_qty_used;
int leg_qty;
Leg() : book(), curr_level(0), curr_level_qty_used(0), leg_qty(0) {}
md::IntPrice curr_price() const {
// Assert that the book isn't 'empty' yet.
// Note: one could argue that nan be returned in this event,
// but since the loop is designed to return "greedily,"
// 'curr_price' should not be accessed after it has been emptied.
// This assertion enforces that the behavior is only changed
// as a result of deliberate action, rather than an inadvertant
// side-effect.
bts_assert(!is_empty());
return book[curr_level].int_price();
}
Size curr_level_qty_rem() const {
return book[curr_level].size() - curr_level_qty_used;
}
// The implied market's volume is limited by its thinnest market.
// When one leg 'is_empty,' the full volume of the implied market
// has been realized.
bool is_empty() const { return curr_level >= book.size(); }
};
std::vector<Leg> legs;
for (const auto& [qty, price_mult, book] : leg_mkts) {
// todo: implement price mult logic (for now just assert nan or 1)
bts_assert(price_mult.is_nan() || price_mult.price() == 1);
auto& l = legs.emplace_back();
bts_assert(qty != 0);
l.leg_qty = qty;
if (book.empty() || is_empty(book.front()))
return;
change_unit_qty(
book, l.book, std::abs(l.leg_qty));
if (l.book.empty())
return;
}
auto any_legs_empty = [&]() {
for (auto& l : legs)
if (l.is_empty())
return true;
return false;
};
while (!any_legs_empty() && depth-- > 0) {
// legs can't be empty, because it is a 1:1 mapping of leg_mkts,
// and it has already been established that the latter isn't empty.
auto limiting_factor = legs[0].curr_level_qty_rem();
for (uint32_t i = 0; i < legs.size(); i++) {
if (legs[i].curr_level_qty_rem() == 0) {
legs[i].curr_level++;
if (legs[i].is_empty()) {
// book is complete
return;
}
legs[i].curr_level_qty_used = Size(0);
}
limiting_factor =
std::min(legs[i].curr_level_qty_rem(), limiting_factor);
if (limiting_factor == 0)
return;
}
bts_assert(limiting_factor > 0);
for (uint32_t i = 0; i < legs.size(); i++)
legs[i].curr_level_qty_used += limiting_factor;
auto marginal_cost = md::INT_NAN;
for (auto& l : legs) {
// The book's prices have already been multiplied by abs(leg_qty);
// Subtract the leg's cost if qty is negative, otherwise add it
// to the synthetic's marginal cost.
auto lcost = l.curr_price();
if (l.leg_qty < 0)
lcost = -lcost;
if (marginal_cost.is_nan())
marginal_cost = lcost;
else
marginal_cost += lcost;
}
dest.emplace_back(marginal_cost, limiting_factor);
}
}
} // namespace bts::orderman
+10 -5
View File
@@ -6,13 +6,18 @@
#include "lib/orderman/orderman_utils/Book.h"
#include "lib/orderman/orderman_utils/PriceComp.h"
namespace bts {
namespace orderman {
namespace bts::orderman {
struct BookUtils {
static md::Size volume_at_or_better(const Book& book, api::Side side, Price price);
static md::Size volume_at_or_better(const Book& book, api::Side side, double price);
static SidePriceComp price_comp(api::Side side);
static void change_unit_qty(
const BookSide& book_in, std::vector<md::PriceSize>& book_out, int unit_qty);
static void compute_synthetic_book(
std::vector<md::PriceSize>& dest,
const std::vector<std::tuple<md::Size, md::IntPrice, BookSide>>& leg_mkts,
uint32_t depth);
};
} // namespace orderman
} // namespace bts
} // namespace bts::orderman