Compare commits

...
31 Commits
Author SHA1 Message Date
John Colvin b95da40fec feat: Impl synthetic book 2022-11-28 10:42:05 -05:00
John Colvin 755f841a6e refactor: Change spreader leg qty to int
The synth book computation code doesn't support doubles
2022-11-25 13:20:34 -05:00
John Colvin 2d10c94b69 chore: Make csharp code compatible with net471, lang version 7 2022-11-25 13:20:33 -05:00
John Colvin dfc5fcc16a fix build (client): Change 'WIN32' macros to '_WIN32'
Not sure exactly how this broke, but the WIN32 macro is no longer defined.
2022-11-20 14:22:42 -05:00
Taha Afzal 864e2a3edc Comments 2022-11-10 07:18:42 -05:00
Taha Afzal b9142202e0 UI code complete 2022-11-09 23:13:16 -05:00
Taha Afzal 7353a6dc3c Added get_data() to LegControl.cs 2022-11-08 23:26:10 -05:00
Taha Afzal b55eecc8b9 Eagle front end 2022-11-06 18:52:38 -05:00
Taha Afzal 3f0078d582 Added sample test 2022-05-09 23:08:59 -04:00
Taha Afzal e47186f0fd Fixing tests 2022-05-08 22:47:10 -04:00
Taha Afzal 4b92292fa1 Pushed a sample test 2022-05-04 09:31:09 -04:00
Taha Afzal 3a8125bba3 Namespace changes and moving all shared files to spreader_order directory 2022-05-02 23:27:30 -04:00
Taha Afzal 0b14bd1ca3 ChildOrder changed to bts::spreader::ChildOrder 2022-05-01 12:41:39 -04:00
Taha Afzal 63252b017b Cleaning up the moved events 2022-04-30 22:31:37 -04:00
Taha Afzal df1efb2295 Moving events to bts::spreader 2022-04-29 18:19:29 -04:00
Taha Afzal 3f37777eb9 'Moved' book and accompaying classes over 2022-04-29 00:21:34 -04:00
Taha Afzal 04629f28e8 Removing SpreaderOrder's inheritence from Order 2022-04-26 23:35:43 -04:00
Taha Afzal 47c4d8c4a7 Order is being routed to IOrderGateway 2022-04-26 00:40:17 -04:00
Taha Afzal eb87ebcfa1 Removed IAlternateOrderGateway, fixing tests now 2022-04-24 22:21:02 -04:00
Taha Afzal 195fd11e2e IOrderGateway accessor added to ISpreaderManager 2022-04-24 15:45:35 -04:00
Taha Afzal 413c24d147 Completed LegOrderRouter, which will be used by SpreaderChildOrderState to send orders 2022-04-24 01:18:58 -04:00
Taha Afzal fe4be118ee Adding LegOrderRouter 2022-04-24 00:16:09 -04:00
Taha Afzal 7ff09902b8 IOrderGateway::report_spreader_state takes an bts::integration::OrderUpdate now 2022-04-23 21:53:17 -04:00
Taha Afzal f90e41ea65 Moved the new interface, types into their own files 2022-04-20 23:58:05 -04:00
Taha Afzal ff2546da56 Adding function that returns OrderDef to ISpreaderManager 2022-04-20 21:46:45 -04:00
Taha Afzal f7426d9f0f All tests moved to use SpreaderWrapper 2022-04-18 23:43:06 -04:00
Taha Afzal 36bbcf849c More tests for SpreaderWrapper 2022-04-18 22:43:19 -04:00
Taha Afzal bd39d407aa More spreader tests pass 2022-04-17 23:57:07 -04:00
Taha Afzal 81dde8e9df Moving tests over to use SpreaderWrapper instead of SpreaderOrder 2022-04-17 18:48:51 -04:00
Taha Afzal 3f4a4fbac8 The 3 wrappers SpreaderOrderBuilder, SpreaderOrderServices and SpreaderPreActivationOrderServices do not call into the Spark infrastructure any more 2022-04-17 15:08:56 -04:00
Taha Afzal 2e6676b151 Added Spreader Wrapper 2022-04-16 23:55:39 -04:00
219 changed files with 14787 additions and 9286 deletions

No files matched your search

+25
View File
@@ -0,0 +1,25 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.2.32630.192
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SpreaderFrontEnd", "SpreaderFrontEnd\SpreaderFrontEnd.csproj", "{28EE9ABE-BBA6-4587-8BEB-3B0CD979ED22}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{28EE9ABE-BBA6-4587-8BEB-3B0CD979ED22}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{28EE9ABE-BBA6-4587-8BEB-3B0CD979ED22}.Debug|Any CPU.Build.0 = Debug|Any CPU
{28EE9ABE-BBA6-4587-8BEB-3B0CD979ED22}.Release|Any CPU.ActiveCfg = Release|Any CPU
{28EE9ABE-BBA6-4587-8BEB-3B0CD979ED22}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3E2EF0DA-32BB-4A7A-9089-B132E1677AA7}
EndGlobalSection
EndGlobal
@@ -0,0 +1,36 @@
using System;
using System.Linq;
namespace SpreaderFrontEnd {
public class Book : IEquatable<Book> {
public Book(BookSide bids = null, BookSide asks = null) {
this.bids = bids ?? new BookSide();
this.asks = asks ?? new BookSide();
}
public BookSide bids { get; }
public BookSide asks { get; }
public Book Clone() {
return new Book(
new BookSide(bids.levels.ToList()),
new BookSide(asks.levels.ToList()));
}
public bool Equals(Book other) {
return other != null && other.bids.Equals(bids) && other.asks.Equals(asks);
}
public override bool Equals(object obj) {
return Equals(obj as Book);
}
public override int GetHashCode() {
return HashCodeHelper.combine(bids.GetHashCode(), asks.GetHashCode());
}
public override string ToString() {
return $"bid: {bids} | ask: {asks}";
}
}
}
@@ -0,0 +1,38 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace SpreaderFrontEnd {
public class BookSide : IEquatable<BookSide> {
public BookSide() {
levels = new List<PriceLevel>();
}
public BookSide(IEnumerable<PriceLevel> levels) {
this.levels = levels?.ToList() ?? new List<PriceLevel>();
}
public IList<PriceLevel> levels { get; }
public BookSide Clone() {
return new BookSide(levels.ToList());
}
public bool Equals(BookSide other) {
return other != null && levels.SequenceEqual(other.levels);
}
public override bool Equals(object obj) {
return Equals(obj as BookSide);
}
public override int GetHashCode() {
return levels.Select(l => l.GetHashCode()).Skip(1).Aggregate(
levels[0].GetHashCode(), HashCodeHelper.combine);
}
public override string ToString() {
return string.Join("; ", levels);
}
}
}
@@ -0,0 +1,120 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
namespace SpreaderFrontEnd {
public class LegCursor {
public LegCursor(int leg_size, IList<PriceLevel> levels) {
this.leg_size = leg_size;
this.levels = levels;
}
/// <summary>
/// The [signed] quantity of this leg in the SpreaderContract to which it belongs
/// </summary>
public int leg_size { get; }
/// <summary>
/// The [unsigned] quantity of this leg in the SpreaderContract to which it belongs
/// </summary>
public int leg_size_abs {
get {
return Math.Abs(leg_size);
}
}
/// <summary>
/// The price levels of the underlying BookSide
/// </summary>
public IList<PriceLevel> levels { get; }
/// <summary>
/// The index of the current level. When <see cref="consume_unit_qty(uint, out List{PriceLevel})"/>
/// is called, quantity consumption begins at this level. Once this level's quantity is fully
/// consumed, <see cref="cursor"/> is incremented, and <see cref="curr_level_consumed_qty"/>
/// is reset to 0.
/// </summary>
public int cursor { get; private set; }
/// <summary>
/// The quantity that has been consumed at the current level (i.e., the level at index
/// <see cref="cursor"/>)
/// </summary>
public uint curr_level_consumed_qty { get; private set; }
/// <summary>
/// Consumes the specified quantity of units at the best available price(s)
/// </summary>
/// <param name="unit_qty">
/// The unit quantity to consume
/// </param>
/// <returns>True if the entire quantity is consumed, otherwise false.</returns>
public bool consume_unit_qty(uint unit_qty, out List<PriceLevel> consumed_levels) {
consumed_levels = new List<PriceLevel>();
uint marg_qty = (uint)(unit_qty * leg_size_abs);
var consumed_qty = _consume_raw_qty(marg_qty, ref consumed_levels);
Debug.Assert(consumed_qty % leg_size_abs == 0);
uint consumed_units = (uint)(consumed_qty / leg_size_abs);
return consumed_units == unit_qty;
}
/// <summary>
/// Gets the number of units (raw_quantity / leg_size_abs) available at the best price
/// </summary>
public PriceLevel curr_synth_level() {
if (cursor >= levels.Count) {
return PriceLevel.empty;
}
var curr_level = levels[cursor];
Debug.Assert(curr_level.qty >= curr_level_consumed_qty);
var curr_level_rem_qty = curr_level.qty - curr_level_consumed_qty;
if (curr_level_rem_qty >= leg_size_abs) {
return new PriceLevel(
price: curr_level.price * leg_size_abs,
qty: (uint)(curr_level_rem_qty / leg_size_abs));
}
double unit_cost = curr_level.price * curr_level_rem_qty;
var unit_rem = leg_size_abs - curr_level_rem_qty;
for (int i = cursor + 1; i < levels.Count; i++) {
curr_level = levels[i];
if (curr_level.qty >= unit_rem) {
unit_cost += curr_level.price * unit_rem;
return new PriceLevel(price: unit_cost, qty: 1);
}
unit_cost += curr_level.price * curr_level.qty;
unit_rem -= curr_level.qty;
}
return PriceLevel.empty;
}
/// <summary>
/// Consumes as much of the specified [raw] quantity as possible
/// </summary>
/// <returns>
/// The quantity that was actually consumed
/// </returns>
uint _consume_raw_qty(uint raw_qty, ref List<PriceLevel> dest) {
if (levels == null || cursor >= levels.Count) {
return 0;
}
var curr_level = levels[cursor];
// Cursor should be incremented when curr_level_consumed_qty == levels[cursor].qty
// (i.e., curr_level.qty should never be less than curr_level_consumed_qty)
Debug.Assert(curr_level.qty > curr_level_consumed_qty);
var curr_level_qty_rem = curr_level.qty - curr_level_consumed_qty;
if (curr_level_qty_rem > raw_qty) {
curr_level_consumed_qty += raw_qty;
dest.Add(new PriceLevel(price: curr_level.price, qty: raw_qty));
return raw_qty;
}
// The input qty is greater than the qty that remains at this level. Consume the rest on
// the next level.
cursor++;
curr_level_consumed_qty = 0;
dest.Add(new PriceLevel(price: curr_level.price, qty: curr_level_qty_rem));
return curr_level_qty_rem + _consume_raw_qty(raw_qty - curr_level_qty_rem, ref dest);
}
}
}
@@ -0,0 +1,44 @@
using System;
namespace SpreaderFrontEnd {
public struct PriceLevel : IEquatable<PriceLevel> {
public PriceLevel(double price, uint qty) {
this.price = price;
this.qty = qty;
}
public static PriceLevel empty {
get {
return new PriceLevel(double.NaN, 0);
}
}
public double price { get; }
public uint qty { get; }
public PriceLevel subtract_qty(uint value) {
System.Diagnostics.Debug.Assert(value <= qty);
return new PriceLevel(price, qty - value);
}
public PriceLevel add_qty(uint value) {
return new PriceLevel(price, qty + value);
}
public override string ToString() {
return $"{price} ({qty})";
}
public bool Equals(PriceLevel other) {
return qty == other.qty && price.Equals(other.price);
}
public override bool Equals(object obj) {
return obj is PriceLevel other && Equals(other);
}
public override int GetHashCode() {
return HashCodeHelper.combine(price.GetHashCode(), qty.GetHashCode());
}
}
}
@@ -0,0 +1,49 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
namespace SpreaderFrontEnd {
public static class SpreaderBookCalculator {
public static Book compute_spreader_book(
SpreaderContract contract, IEnumerable<Book> leg_books) {
if (contract == null) {
throw new ArgumentNullException(nameof(contract));
}
if (leg_books == null) {
throw new ArgumentNullException(nameof(leg_books));
}
var leg_book_list = leg_books as IList<Book> ?? leg_books.ToList();
var synth_book = new SyntheticBook(contract, leg_book_list);
return synth_book.to_book();
}
public static BookSide compute_spreader_book_side(
SpreaderContract contract, IList<Book> leg_books, bool is_bid) {
Debug.Assert(contract.spread_legs.Count == leg_books.Count);
var leg_cursors = leg_books.Select((b, i) => new LegCursor(
contract.spread_legs[i].qty,
contract.spread_legs[i].qty < 0 == is_bid ? b.asks.levels : b.bids.levels)).ToList();
var level_qty = leg_cursors.Select(lc => lc.curr_synth_level().qty).Min();
var result = new BookSide();
while (level_qty > 0) {
double level_price = 0;
for (int i = 0; i < leg_cursors.Count; i++) {
var lc = leg_cursors[i];
bool did_consume = lc.consume_unit_qty(level_qty, out var consumed_levels);
Debug.Assert(did_consume);
var leg_cost = consumed_levels.Select(l => l.price * l.qty).Sum() / level_qty;
if (lc.leg_size < 0) {
level_price -= leg_cost;
} else {
level_price += leg_cost;
}
}
result.levels.Add(new PriceLevel(price: level_price, qty: level_qty));
level_qty = leg_cursors.Select(lc => lc.curr_synth_level().qty).Min();
}
return result;
}
}
}
@@ -0,0 +1,17 @@
using System.Collections.Generic;
namespace SpreaderFrontEnd {
class SyntheticBook {
public SyntheticBook(SpreaderContract contract, IList<Book> leg_books) {
bids = new SyntheticBookSide(contract, leg_books, true);
asks = new SyntheticBookSide(contract, leg_books, false);
}
public SyntheticBookSide bids { get; }
public SyntheticBookSide asks { get; }
public Book to_book() {
return new Book(bids: bids.to_book_side(), asks: asks.to_book_side());
}
}
}
@@ -0,0 +1,28 @@
using System.Collections;
using System.Collections.Generic;
namespace SpreaderFrontEnd {
class SyntheticBookSide : IEnumerable<PriceLevel> {
readonly SpreaderContract _contract;
readonly IList<Book> _leg_books;
readonly bool _is_bid;
public SyntheticBookSide(SpreaderContract contract, IList<Book> leg_books, bool is_bid) {
_contract = contract;
_leg_books = leg_books;
_is_bid = is_bid;
}
public BookSide to_book_side() {
return new BookSide(this);
}
public IEnumerator<PriceLevel> GetEnumerator() {
return new SyntheticBookSideEnumerator(_contract, _leg_books, _is_bid);
}
IEnumerator IEnumerable.GetEnumerator() {
return GetEnumerator();
}
}
}
@@ -0,0 +1,59 @@
using System.Collections.Generic;
using System.Linq;
using System.Diagnostics;
using System.Collections;
namespace SpreaderFrontEnd {
class SyntheticBookSideEnumerator : IEnumerator<PriceLevel> {
readonly List<LegCursor> _leg_cursors;
PriceLevel _current;
uint _level_qty;
public SyntheticBookSideEnumerator(
SpreaderContract contract, IList<Book> leg_books, bool is_bid) {
Debug.Assert(contract.spread_legs.Count == leg_books.Count);
_leg_cursors = new List<LegCursor>(leg_books.Count);
for (int i = 0; i < leg_books.Count; i++) {
_leg_cursors.Add(new LegCursor(
contract.spread_legs[i].qty,
contract.spread_legs[i].qty < 0 == is_bid ?
leg_books[i].asks.levels : leg_books[i].bids.levels));
}
}
public PriceLevel Current => _current;
object IEnumerator.Current => _current;
public void Dispose() { }
public bool MoveNext() {
_level_qty = _leg_cursors.Select(lc => lc.curr_synth_level().qty).Min();
if (_level_qty == 0) {
return false;
}
double level_price = 0;
for (int i = 0; i < _leg_cursors.Count; i++) {
var lc = _leg_cursors[i];
bool did_consume = lc.consume_unit_qty(_level_qty, out var consumed_levels);
Debug.Assert(did_consume);
var leg_cost = consumed_levels.Select(l => l.price * l.qty).Sum() / _level_qty;
if (lc.leg_size < 0) {
level_price -= leg_cost;
} else {
level_price += leg_cost;
}
}
_current = new PriceLevel(price: level_price, qty: _level_qty);
return true;
}
public void Reset() {
for (int i = 0; i < _leg_cursors.Count; i++) {
var lc = _leg_cursors[i];
_leg_cursors[i] = new LegCursor(lc.leg_size, lc.levels);
}
}
}
}
@@ -0,0 +1,8 @@
namespace SpreaderFrontEnd {
static class HashCodeHelper {
// Copied from https://referencesource.microsoft.com/#System.Numerics/System/Numerics/HashCodeHelper.cs
public static int combine(int h1, int h2) {
return (((h1 << 5) + h1) ^ h2);
}
}
}
+744
View File
@@ -0,0 +1,744 @@
using System.Windows.Forms;
namespace SpreaderFrontEnd
{
partial class LegControl
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Component Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this._combobox_security = new System.Windows.Forms.ComboBox();
this.label1 = new System.Windows.Forms.Label();
this.label2 = new System.Windows.Forms.Label();
this.label3 = new System.Windows.Forms.Label();
this.label5 = new System.Windows.Forms.Label();
this.groupBox1 = new System.Windows.Forms.GroupBox();
this._textbox_quote_qho_length = new System.Windows.Forms.TextBox();
this._textbox_quote_insidemarketdistance_towarddistance = new System.Windows.Forms.TextBox();
this._textbox_quote_conditional_ticks = new System.Windows.Forms.TextBox();
this._textbox_quote_throttle_outsideticks = new System.Windows.Forms.TextBox();
this._textbox_quote_throttle_insideticks = new System.Windows.Forms.TextBox();
this._textbox_quote_showqty_qty = new System.Windows.Forms.TextBox();
this.label11 = new System.Windows.Forms.Label();
this._checkbox_quote_insidemarketdistance = new System.Windows.Forms.CheckBox();
this.label10 = new System.Windows.Forms.Label();
this._checkbox_quote_conditional = new System.Windows.Forms.CheckBox();
this.label9 = new System.Windows.Forms.Label();
this.label8 = new System.Windows.Forms.Label();
this._checkbox_quote_throttle = new System.Windows.Forms.CheckBox();
this.label7 = new System.Windows.Forms.Label();
this._checkbox_quote_showqty = new System.Windows.Forms.CheckBox();
this.label6 = new System.Windows.Forms.Label();
this.groupBox2 = new System.Windows.Forms.GroupBox();
this._textbox_prehedge_payless_paylessticks = new System.Windows.Forms.TextBox();
this._textbox_prehedge_payup_payupticks = new System.Windows.Forms.TextBox();
this._textbox_prehedge_payup_threshold = new System.Windows.Forms.TextBox();
this._textbox_prehedge_payless_threshold = new System.Windows.Forms.TextBox();
this.label13 = new System.Windows.Forms.Label();
this.label14 = new System.Windows.Forms.Label();
this.label15 = new System.Windows.Forms.Label();
this._checkbox_prehedge_payup = new System.Windows.Forms.CheckBox();
this.label16 = new System.Windows.Forms.Label();
this._checkbox_prehedge_payless = new System.Windows.Forms.CheckBox();
this.groupBox3 = new System.Windows.Forms.GroupBox();
this._textbox_posthedge_sizebasedcross_crossticks = new System.Windows.Forms.TextBox();
this._textbox_posthedge_sizebasedcross_threshold = new System.Windows.Forms.TextBox();
this._textbox_posthedge_timesbasedcross_crossticks = new System.Windows.Forms.TextBox();
this._textbox_posthedge_timesbasedcross_timems = new System.Windows.Forms.TextBox();
this._textbox_posthedge_ticksawaycross_crossticks = new System.Windows.Forms.TextBox();
this._textbox_posthedge_ticksawaycross_threshold = new System.Windows.Forms.TextBox();
this.label20 = new System.Windows.Forms.Label();
this.label21 = new System.Windows.Forms.Label();
this._checkbox_posthedge_sizebasedcross = new System.Windows.Forms.CheckBox();
this.label12 = new System.Windows.Forms.Label();
this.label17 = new System.Windows.Forms.Label();
this.label18 = new System.Windows.Forms.Label();
this._checkbox_posthedge_timebasedcross = new System.Windows.Forms.CheckBox();
this.label19 = new System.Windows.Forms.Label();
this._checkbox_posthedge_ticksawaycross = new System.Windows.Forms.CheckBox();
this._textbox_leg_qty = new System.Windows.Forms.TextBox();
this._checkbox_leg_enablequoting = new System.Windows.Forms.CheckBox();
this._textbox_leg_leanqtyfactor = new System.Windows.Forms.TextBox();
this._combobox_mdtype = new System.Windows.Forms.ComboBox();
this._textbox_leg_price_mult = new System.Windows.Forms.TextBox();
this.label4 = new System.Windows.Forms.Label();
this._label_leanqty_factor = new System.Windows.Forms.Label();
this.groupBox1.SuspendLayout();
this.groupBox2.SuspendLayout();
this.groupBox3.SuspendLayout();
this.SuspendLayout();
//
// _combobox_security
//
this._combobox_security.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList;
this._combobox_security.FormattingEnabled = true;
this._combobox_security.Location = new System.Drawing.Point(157, 15);
this._combobox_security.Name = "_combobox_security";
this._combobox_security.Size = new System.Drawing.Size(100, 23);
this._combobox_security.TabIndex = 0;
//
// label1
//
this.label1.AutoSize = true;
this.label1.Location = new System.Drawing.Point(16, 23);
this.label1.Name = "label1";
this.label1.Size = new System.Drawing.Size(49, 15);
this.label1.TabIndex = 1;
this.label1.Text = "Security";
//
// label2
//
this.label2.AutoSize = true;
this.label2.Location = new System.Drawing.Point(16, 58);
this.label2.Name = "label2";
this.label2.Size = new System.Drawing.Size(53, 15);
this.label2.TabIndex = 2;
this.label2.Text = "Quantity";
//
// label3
//
this.label3.AutoSize = true;
this.label3.Location = new System.Drawing.Point(16, 128);
this.label3.Name = "label3";
this.label3.Size = new System.Drawing.Size(89, 15);
this.label3.TabIndex = 3;
this.label3.Text = "Enable Quoting";
//
// label5
//
this.label5.AutoSize = true;
this.label5.Location = new System.Drawing.Point(16, 198);
this.label5.Name = "label5";
this.label5.Size = new System.Drawing.Size(98, 15);
this.label5.TabIndex = 5;
this.label5.Text = "Market Data Type";
//
// groupBox1
//
this.groupBox1.Controls.Add(this._textbox_quote_qho_length);
this.groupBox1.Controls.Add(this._textbox_quote_insidemarketdistance_towarddistance);
this.groupBox1.Controls.Add(this._textbox_quote_conditional_ticks);
this.groupBox1.Controls.Add(this._textbox_quote_throttle_outsideticks);
this.groupBox1.Controls.Add(this._textbox_quote_throttle_insideticks);
this.groupBox1.Controls.Add(this._textbox_quote_showqty_qty);
this.groupBox1.Controls.Add(this.label11);
this.groupBox1.Controls.Add(this._checkbox_quote_insidemarketdistance);
this.groupBox1.Controls.Add(this.label10);
this.groupBox1.Controls.Add(this._checkbox_quote_conditional);
this.groupBox1.Controls.Add(this.label9);
this.groupBox1.Controls.Add(this.label8);
this.groupBox1.Controls.Add(this._checkbox_quote_throttle);
this.groupBox1.Controls.Add(this.label7);
this.groupBox1.Controls.Add(this._checkbox_quote_showqty);
this.groupBox1.Controls.Add(this.label6);
this.groupBox1.ForeColor = System.Drawing.Color.Black;
this.groupBox1.Location = new System.Drawing.Point(12, 238);
this.groupBox1.Name = "groupBox1";
this.groupBox1.Size = new System.Drawing.Size(265, 369);
this.groupBox1.TabIndex = 6;
this.groupBox1.TabStop = false;
this.groupBox1.Text = "Quote";
//
// _textbox_quote_qho_length
//
this._textbox_quote_qho_length.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_quote_qho_length.Location = new System.Drawing.Point(145, 28);
this._textbox_quote_qho_length.Name = "_textbox_quote_qho_length";
this._textbox_quote_qho_length.Size = new System.Drawing.Size(100, 23);
this._textbox_quote_qho_length.TabIndex = 6;
//
// _textbox_quote_insidemarketdistance_towarddistance
//
this._textbox_quote_insidemarketdistance_towarddistance.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_quote_insidemarketdistance_towarddistance.Location = new System.Drawing.Point(145, 314);
this._textbox_quote_insidemarketdistance_towarddistance.Name = "_textbox_quote_insidemarketdistance_towarddistance";
this._textbox_quote_insidemarketdistance_towarddistance.Size = new System.Drawing.Size(100, 23);
this._textbox_quote_insidemarketdistance_towarddistance.TabIndex = 15;
//
// _textbox_quote_conditional_ticks
//
this._textbox_quote_conditional_ticks.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_quote_conditional_ticks.Location = new System.Drawing.Point(145, 250);
this._textbox_quote_conditional_ticks.Name = "_textbox_quote_conditional_ticks";
this._textbox_quote_conditional_ticks.Size = new System.Drawing.Size(100, 23);
this._textbox_quote_conditional_ticks.TabIndex = 13;
//
// _textbox_quote_throttle_outsideticks
//
this._textbox_quote_throttle_outsideticks.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_quote_throttle_outsideticks.Location = new System.Drawing.Point(145, 186);
this._textbox_quote_throttle_outsideticks.Name = "_textbox_quote_throttle_outsideticks";
this._textbox_quote_throttle_outsideticks.Size = new System.Drawing.Size(100, 23);
this._textbox_quote_throttle_outsideticks.TabIndex = 11;
//
// _textbox_quote_throttle_insideticks
//
this._textbox_quote_throttle_insideticks.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_quote_throttle_insideticks.Location = new System.Drawing.Point(145, 156);
this._textbox_quote_throttle_insideticks.Name = "_textbox_quote_throttle_insideticks";
this._textbox_quote_throttle_insideticks.Size = new System.Drawing.Size(100, 23);
this._textbox_quote_throttle_insideticks.TabIndex = 10;
//
// _textbox_quote_showqty_qty
//
this._textbox_quote_showqty_qty.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_quote_showqty_qty.Location = new System.Drawing.Point(145, 92);
this._textbox_quote_showqty_qty.Name = "_textbox_quote_showqty_qty";
this._textbox_quote_showqty_qty.Size = new System.Drawing.Size(100, 23);
this._textbox_quote_showqty_qty.TabIndex = 8;
//
// label11
//
this.label11.AutoSize = true;
this.label11.Location = new System.Drawing.Point(45, 322);
this.label11.Name = "label11";
this.label11.Size = new System.Drawing.Size(93, 15);
this.label11.TabIndex = 16;
this.label11.Text = "Toward Distance";
//
// _checkbox_quote_insidemarketdistance
//
this._checkbox_quote_insidemarketdistance.AutoSize = true;
this._checkbox_quote_insidemarketdistance.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_quote_insidemarketdistance.Location = new System.Drawing.Point(15, 288);
this._checkbox_quote_insidemarketdistance.Name = "_checkbox_quote_insidemarketdistance";
this._checkbox_quote_insidemarketdistance.Size = new System.Drawing.Size(131, 19);
this._checkbox_quote_insidemarketdistance.TabIndex = 14;
this._checkbox_quote_insidemarketdistance.Text = "Inside Market Dist.";
this._checkbox_quote_insidemarketdistance.UseVisualStyleBackColor = true;
this._checkbox_quote_insidemarketdistance.CheckedChanged += new System.EventHandler(this._checkbox_quote_insidemarketdistance_CheckedChanged);
//
// label10
//
this.label10.AutoSize = true;
this.label10.Location = new System.Drawing.Point(47, 258);
this.label10.Name = "label10";
this.label10.Size = new System.Drawing.Size(33, 15);
this.label10.TabIndex = 14;
this.label10.Text = "Ticks";
//
// _checkbox_quote_conditional
//
this._checkbox_quote_conditional.AutoSize = true;
this._checkbox_quote_conditional.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_quote_conditional.Location = new System.Drawing.Point(15, 224);
this._checkbox_quote_conditional.Name = "_checkbox_quote_conditional";
this._checkbox_quote_conditional.Size = new System.Drawing.Size(88, 19);
this._checkbox_quote_conditional.TabIndex = 12;
this._checkbox_quote_conditional.Text = "Conditional";
this._checkbox_quote_conditional.UseVisualStyleBackColor = true;
this._checkbox_quote_conditional.CheckedChanged += new System.EventHandler(this._checkbox_quote_conditional_CheckedChanged);
//
// label9
//
this.label9.AutoSize = true;
this.label9.Location = new System.Drawing.Point(45, 194);
this.label9.Name = "label9";
this.label9.Size = new System.Drawing.Size(77, 15);
this.label9.TabIndex = 12;
this.label9.Text = "Outside Ticks";
//
// label8
//
this.label8.AutoSize = true;
this.label8.Location = new System.Drawing.Point(45, 164);
this.label8.Name = "label8";
this.label8.Size = new System.Drawing.Size(67, 15);
this.label8.TabIndex = 11;
this.label8.Text = "Inside Ticks";
//
// _checkbox_quote_throttle
//
this._checkbox_quote_throttle.AutoSize = true;
this._checkbox_quote_throttle.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_quote_throttle.Location = new System.Drawing.Point(15, 130);
this._checkbox_quote_throttle.Name = "_checkbox_quote_throttle";
this._checkbox_quote_throttle.Size = new System.Drawing.Size(72, 19);
this._checkbox_quote_throttle.TabIndex = 9;
this._checkbox_quote_throttle.Text = "Throttle";
this._checkbox_quote_throttle.UseVisualStyleBackColor = true;
this._checkbox_quote_throttle.CheckedChanged += new System.EventHandler(this._checkbox_quote_throttle_CheckedChanged);
//
// label7
//
this.label7.AutoSize = true;
this.label7.Location = new System.Drawing.Point(45, 100);
this.label7.Name = "label7";
this.label7.Size = new System.Drawing.Size(53, 15);
this.label7.TabIndex = 9;
this.label7.Text = "Quantity";
//
// _checkbox_quote_showqty
//
this._checkbox_quote_showqty.AutoSize = true;
this._checkbox_quote_showqty.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_quote_showqty.Location = new System.Drawing.Point(15, 66);
this._checkbox_quote_showqty.Name = "_checkbox_quote_showqty";
this._checkbox_quote_showqty.Size = new System.Drawing.Size(108, 19);
this._checkbox_quote_showqty.TabIndex = 7;
this._checkbox_quote_showqty.Text = "Show Quantity";
this._checkbox_quote_showqty.UseVisualStyleBackColor = true;
this._checkbox_quote_showqty.CheckedChanged += new System.EventHandler(this._checkbox_quote_showqty_CheckedChanged);
//
// label6
//
this.label6.AutoSize = true;
this.label6.Location = new System.Drawing.Point(15, 36);
this.label6.Name = "label6";
this.label6.Size = new System.Drawing.Size(121, 15);
this.label6.TabIndex = 6;
this.label6.Text = "Queue Holder Length";
//
// groupBox2
//
this.groupBox2.Controls.Add(this._textbox_prehedge_payless_paylessticks);
this.groupBox2.Controls.Add(this._textbox_prehedge_payup_payupticks);
this.groupBox2.Controls.Add(this._textbox_prehedge_payup_threshold);
this.groupBox2.Controls.Add(this._textbox_prehedge_payless_threshold);
this.groupBox2.Controls.Add(this.label13);
this.groupBox2.Controls.Add(this.label14);
this.groupBox2.Controls.Add(this.label15);
this.groupBox2.Controls.Add(this._checkbox_prehedge_payup);
this.groupBox2.Controls.Add(this.label16);
this.groupBox2.Controls.Add(this._checkbox_prehedge_payless);
this.groupBox2.Location = new System.Drawing.Point(13, 626);
this.groupBox2.Name = "groupBox2";
this.groupBox2.Size = new System.Drawing.Size(264, 234);
this.groupBox2.TabIndex = 7;
this.groupBox2.TabStop = false;
this.groupBox2.Text = "Pre-Hedge";
//
// _textbox_prehedge_payless_paylessticks
//
this._textbox_prehedge_payless_paylessticks.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_prehedge_payless_paylessticks.Location = new System.Drawing.Point(144, 90);
this._textbox_prehedge_payless_paylessticks.Name = "_textbox_prehedge_payless_paylessticks";
this._textbox_prehedge_payless_paylessticks.Size = new System.Drawing.Size(100, 23);
this._textbox_prehedge_payless_paylessticks.TabIndex = 18;
//
// _textbox_prehedge_payup_payupticks
//
this._textbox_prehedge_payup_payupticks.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_prehedge_payup_payupticks.Location = new System.Drawing.Point(144, 187);
this._textbox_prehedge_payup_payupticks.Name = "_textbox_prehedge_payup_payupticks";
this._textbox_prehedge_payup_payupticks.Size = new System.Drawing.Size(100, 23);
this._textbox_prehedge_payup_payupticks.TabIndex = 21;
//
// _textbox_prehedge_payup_threshold
//
this._textbox_prehedge_payup_threshold.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_prehedge_payup_threshold.Location = new System.Drawing.Point(144, 156);
this._textbox_prehedge_payup_threshold.Name = "_textbox_prehedge_payup_threshold";
this._textbox_prehedge_payup_threshold.Size = new System.Drawing.Size(100, 23);
this._textbox_prehedge_payup_threshold.TabIndex = 20;
//
// _textbox_prehedge_payless_threshold
//
this._textbox_prehedge_payless_threshold.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_prehedge_payless_threshold.Location = new System.Drawing.Point(144, 59);
this._textbox_prehedge_payless_threshold.Name = "_textbox_prehedge_payless_threshold";
this._textbox_prehedge_payless_threshold.Size = new System.Drawing.Size(100, 23);
this._textbox_prehedge_payless_threshold.TabIndex = 17;
//
// label13
//
this.label13.AutoSize = true;
this.label13.Location = new System.Drawing.Point(44, 98);
this.label13.Name = "label13";
this.label13.Size = new System.Drawing.Size(74, 15);
this.label13.TabIndex = 14;
this.label13.Text = "Payless Ticks";
//
// label14
//
this.label14.AutoSize = true;
this.label14.Location = new System.Drawing.Point(44, 195);
this.label14.Name = "label14";
this.label14.Size = new System.Drawing.Size(69, 15);
this.label14.TabIndex = 12;
this.label14.Text = "Payup Ticks";
//
// label15
//
this.label15.AutoSize = true;
this.label15.Location = new System.Drawing.Point(44, 164);
this.label15.Name = "label15";
this.label15.Size = new System.Drawing.Size(59, 15);
this.label15.TabIndex = 11;
this.label15.Text = "Threshold";
//
// _checkbox_prehedge_payup
//
this._checkbox_prehedge_payup.AutoSize = true;
this._checkbox_prehedge_payup.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_prehedge_payup.Location = new System.Drawing.Point(15, 129);
this._checkbox_prehedge_payup.Name = "_checkbox_prehedge_payup";
this._checkbox_prehedge_payup.Size = new System.Drawing.Size(64, 19);
this._checkbox_prehedge_payup.TabIndex = 19;
this._checkbox_prehedge_payup.Text = "Pay Up";
this._checkbox_prehedge_payup.UseVisualStyleBackColor = true;
this._checkbox_prehedge_payup.CheckedChanged += new System.EventHandler(this._checkbox_prehedge_payup_CheckedChanged);
//
// label16
//
this.label16.AutoSize = true;
this.label16.Location = new System.Drawing.Point(44, 67);
this.label16.Name = "label16";
this.label16.Size = new System.Drawing.Size(59, 15);
this.label16.TabIndex = 9;
this.label16.Text = "Threshold";
//
// _checkbox_prehedge_payless
//
this._checkbox_prehedge_payless.AutoSize = true;
this._checkbox_prehedge_payless.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_prehedge_payless.Location = new System.Drawing.Point(15, 32);
this._checkbox_prehedge_payless.Name = "_checkbox_prehedge_payless";
this._checkbox_prehedge_payless.Size = new System.Drawing.Size(71, 19);
this._checkbox_prehedge_payless.TabIndex = 16;
this._checkbox_prehedge_payless.Text = "Pay Less";
this._checkbox_prehedge_payless.UseVisualStyleBackColor = true;
this._checkbox_prehedge_payless.CheckedChanged += new System.EventHandler(this._checkbox_prehedge_payless_CheckedChanged);
//
// groupBox3
//
this.groupBox3.Controls.Add(this._textbox_posthedge_sizebasedcross_crossticks);
this.groupBox3.Controls.Add(this._textbox_posthedge_sizebasedcross_threshold);
this.groupBox3.Controls.Add(this._textbox_posthedge_timesbasedcross_crossticks);
this.groupBox3.Controls.Add(this._textbox_posthedge_timesbasedcross_timems);
this.groupBox3.Controls.Add(this._textbox_posthedge_ticksawaycross_crossticks);
this.groupBox3.Controls.Add(this._textbox_posthedge_ticksawaycross_threshold);
this.groupBox3.Controls.Add(this.label20);
this.groupBox3.Controls.Add(this.label21);
this.groupBox3.Controls.Add(this._checkbox_posthedge_sizebasedcross);
this.groupBox3.Controls.Add(this.label12);
this.groupBox3.Controls.Add(this.label17);
this.groupBox3.Controls.Add(this.label18);
this.groupBox3.Controls.Add(this._checkbox_posthedge_timebasedcross);
this.groupBox3.Controls.Add(this.label19);
this.groupBox3.Controls.Add(this._checkbox_posthedge_ticksawaycross);
this.groupBox3.Location = new System.Drawing.Point(13, 882);
this.groupBox3.Name = "groupBox3";
this.groupBox3.Size = new System.Drawing.Size(264, 319);
this.groupBox3.TabIndex = 8;
this.groupBox3.TabStop = false;
this.groupBox3.Text = "Post-Hedge";
//
// _textbox_posthedge_sizebasedcross_crossticks
//
this._textbox_posthedge_sizebasedcross_crossticks.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_posthedge_sizebasedcross_crossticks.Location = new System.Drawing.Point(144, 276);
this._textbox_posthedge_sizebasedcross_crossticks.Name = "_textbox_posthedge_sizebasedcross_crossticks";
this._textbox_posthedge_sizebasedcross_crossticks.Size = new System.Drawing.Size(100, 23);
this._textbox_posthedge_sizebasedcross_crossticks.TabIndex = 30;
//
// _textbox_posthedge_sizebasedcross_threshold
//
this._textbox_posthedge_sizebasedcross_threshold.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_posthedge_sizebasedcross_threshold.Location = new System.Drawing.Point(144, 246);
this._textbox_posthedge_sizebasedcross_threshold.Name = "_textbox_posthedge_sizebasedcross_threshold";
this._textbox_posthedge_sizebasedcross_threshold.Size = new System.Drawing.Size(100, 23);
this._textbox_posthedge_sizebasedcross_threshold.TabIndex = 29;
//
// _textbox_posthedge_timesbasedcross_crossticks
//
this._textbox_posthedge_timesbasedcross_crossticks.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_posthedge_timesbasedcross_crossticks.Location = new System.Drawing.Point(144, 182);
this._textbox_posthedge_timesbasedcross_crossticks.Name = "_textbox_posthedge_timesbasedcross_crossticks";
this._textbox_posthedge_timesbasedcross_crossticks.Size = new System.Drawing.Size(100, 23);
this._textbox_posthedge_timesbasedcross_crossticks.TabIndex = 27;
//
// _textbox_posthedge_timesbasedcross_timems
//
this._textbox_posthedge_timesbasedcross_timems.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_posthedge_timesbasedcross_timems.Location = new System.Drawing.Point(144, 152);
this._textbox_posthedge_timesbasedcross_timems.Name = "_textbox_posthedge_timesbasedcross_timems";
this._textbox_posthedge_timesbasedcross_timems.Size = new System.Drawing.Size(100, 23);
this._textbox_posthedge_timesbasedcross_timems.TabIndex = 26;
//
// _textbox_posthedge_ticksawaycross_crossticks
//
this._textbox_posthedge_ticksawaycross_crossticks.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_posthedge_ticksawaycross_crossticks.Location = new System.Drawing.Point(144, 88);
this._textbox_posthedge_ticksawaycross_crossticks.Name = "_textbox_posthedge_ticksawaycross_crossticks";
this._textbox_posthedge_ticksawaycross_crossticks.Size = new System.Drawing.Size(100, 23);
this._textbox_posthedge_ticksawaycross_crossticks.TabIndex = 24;
//
// _textbox_posthedge_ticksawaycross_threshold
//
this._textbox_posthedge_ticksawaycross_threshold.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_posthedge_ticksawaycross_threshold.Location = new System.Drawing.Point(144, 58);
this._textbox_posthedge_ticksawaycross_threshold.Name = "_textbox_posthedge_ticksawaycross_threshold";
this._textbox_posthedge_ticksawaycross_threshold.Size = new System.Drawing.Size(100, 23);
this._textbox_posthedge_ticksawaycross_threshold.TabIndex = 23;
//
// label20
//
this.label20.AutoSize = true;
this.label20.Location = new System.Drawing.Point(44, 284);
this.label20.Name = "label20";
this.label20.Size = new System.Drawing.Size(65, 15);
this.label20.TabIndex = 17;
this.label20.Text = "Cross Ticks";
//
// label21
//
this.label21.AutoSize = true;
this.label21.Location = new System.Drawing.Point(44, 254);
this.label21.Name = "label21";
this.label21.Size = new System.Drawing.Size(59, 15);
this.label21.TabIndex = 16;
this.label21.Text = "Threshold";
//
// _checkbox_posthedge_sizebasedcross
//
this._checkbox_posthedge_sizebasedcross.AutoSize = true;
this._checkbox_posthedge_sizebasedcross.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_posthedge_sizebasedcross.Location = new System.Drawing.Point(15, 220);
this._checkbox_posthedge_sizebasedcross.Name = "_checkbox_posthedge_sizebasedcross";
this._checkbox_posthedge_sizebasedcross.Size = new System.Drawing.Size(117, 19);
this._checkbox_posthedge_sizebasedcross.TabIndex = 28;
this._checkbox_posthedge_sizebasedcross.Text = "Size Based Cross";
this._checkbox_posthedge_sizebasedcross.UseVisualStyleBackColor = true;
this._checkbox_posthedge_sizebasedcross.CheckedChanged += new System.EventHandler(this._checkbox_posthedge_sizebasedcross_CheckedChanged);
//
// label12
//
this.label12.AutoSize = true;
this.label12.Location = new System.Drawing.Point(44, 96);
this.label12.Name = "label12";
this.label12.Size = new System.Drawing.Size(65, 15);
this.label12.TabIndex = 14;
this.label12.Text = "Cross Ticks";
//
// label17
//
this.label17.AutoSize = true;
this.label17.Location = new System.Drawing.Point(44, 190);
this.label17.Name = "label17";
this.label17.Size = new System.Drawing.Size(65, 15);
this.label17.TabIndex = 12;
this.label17.Text = "Cross Ticks";
//
// label18
//
this.label18.AutoSize = true;
this.label18.Location = new System.Drawing.Point(44, 160);
this.label18.Name = "label18";
this.label18.Size = new System.Drawing.Size(57, 15);
this.label18.TabIndex = 11;
this.label18.Text = "Time(ms)";
//
// _checkbox_posthedge_timebasedcross
//
this._checkbox_posthedge_timebasedcross.AutoSize = true;
this._checkbox_posthedge_timebasedcross.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_posthedge_timebasedcross.Location = new System.Drawing.Point(15, 126);
this._checkbox_posthedge_timebasedcross.Name = "_checkbox_posthedge_timebasedcross";
this._checkbox_posthedge_timebasedcross.Size = new System.Drawing.Size(122, 19);
this._checkbox_posthedge_timebasedcross.TabIndex = 25;
this._checkbox_posthedge_timebasedcross.Text = "Time Based Cross";
this._checkbox_posthedge_timebasedcross.UseVisualStyleBackColor = true;
this._checkbox_posthedge_timebasedcross.CheckedChanged += new System.EventHandler(this._checkbox_posthedge_timebasedcross_CheckedChanged);
//
// label19
//
this.label19.AutoSize = true;
this.label19.Location = new System.Drawing.Point(44, 66);
this.label19.Name = "label19";
this.label19.Size = new System.Drawing.Size(59, 15);
this.label19.TabIndex = 9;
this.label19.Text = "Threshold";
//
// _checkbox_posthedge_ticksawaycross
//
this._checkbox_posthedge_ticksawaycross.AutoSize = true;
this._checkbox_posthedge_ticksawaycross.Font = new System.Drawing.Font("Segoe UI", 9F, System.Drawing.FontStyle.Bold, System.Drawing.GraphicsUnit.Point);
this._checkbox_posthedge_ticksawaycross.Location = new System.Drawing.Point(15, 32);
this._checkbox_posthedge_ticksawaycross.Name = "_checkbox_posthedge_ticksawaycross";
this._checkbox_posthedge_ticksawaycross.Size = new System.Drawing.Size(119, 19);
this._checkbox_posthedge_ticksawaycross.TabIndex = 22;
this._checkbox_posthedge_ticksawaycross.Text = "Ticks Away Cross";
this._checkbox_posthedge_ticksawaycross.UseVisualStyleBackColor = true;
this._checkbox_posthedge_ticksawaycross.CheckedChanged += new System.EventHandler(this._checkbox_posthedge_ticksawaycross_CheckedChanged);
//
// _textbox_leg_qty
//
this._textbox_leg_qty.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_leg_qty.Location = new System.Drawing.Point(157, 50);
this._textbox_leg_qty.Name = "_textbox_leg_qty";
this._textbox_leg_qty.Size = new System.Drawing.Size(100, 23);
this._textbox_leg_qty.TabIndex = 1;
this._textbox_leg_qty.Tag = "Double";
//
// _checkbox_leg_enablequoting
//
this._checkbox_leg_enablequoting.AutoSize = true;
this._checkbox_leg_enablequoting.Location = new System.Drawing.Point(157, 129);
this._checkbox_leg_enablequoting.Name = "_checkbox_leg_enablequoting";
this._checkbox_leg_enablequoting.Size = new System.Drawing.Size(15, 14);
this._checkbox_leg_enablequoting.TabIndex = 3;
this._checkbox_leg_enablequoting.UseVisualStyleBackColor = true;
//
// _textbox_leg_leanqtyfactor
//
this._textbox_leg_leanqtyfactor.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_leg_leanqtyfactor.Location = new System.Drawing.Point(157, 155);
this._textbox_leg_leanqtyfactor.Name = "_textbox_leg_leanqtyfactor";
this._textbox_leg_leanqtyfactor.Size = new System.Drawing.Size(100, 23);
this._textbox_leg_leanqtyfactor.TabIndex = 4;
//
// _combobox_mdtype
//
this._combobox_mdtype.AutoCompleteCustomSource.AddRange(new string[] {
"Merged",
"Direct"});
this._combobox_mdtype.FormattingEnabled = true;
this._combobox_mdtype.Location = new System.Drawing.Point(157, 190);
this._combobox_mdtype.Name = "_combobox_mdtype";
this._combobox_mdtype.Size = new System.Drawing.Size(100, 23);
this._combobox_mdtype.TabIndex = 5;
//
// _textbox_leg_price_mult
//
this._textbox_leg_price_mult.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle;
this._textbox_leg_price_mult.Location = new System.Drawing.Point(157, 85);
this._textbox_leg_price_mult.Name = "_textbox_leg_price_mult";
this._textbox_leg_price_mult.Size = new System.Drawing.Size(100, 23);
this._textbox_leg_price_mult.TabIndex = 2;
this._textbox_leg_price_mult.Tag = "Double";
//
// label4
//
this.label4.AutoSize = true;
this.label4.Location = new System.Drawing.Point(16, 93);
this.label4.Name = "label4";
this.label4.Size = new System.Drawing.Size(87, 15);
this.label4.TabIndex = 10;
this.label4.Text = "Price Multiplier";
//
// _label_leanqty_factor
//
this._label_leanqty_factor.AutoSize = true;
this._label_leanqty_factor.Location = new System.Drawing.Point(16, 163);
this._label_leanqty_factor.Name = "_label_leanqty_factor";
this._label_leanqty_factor.Size = new System.Drawing.Size(90, 15);
this._label_leanqty_factor.TabIndex = 4;
this._label_leanqty_factor.Text = "Lean Qty Factor";
//
// LegControl
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.BackColor = System.Drawing.Color.White;
this.Controls.Add(this._textbox_leg_price_mult);
this.Controls.Add(this.label4);
this.Controls.Add(this._combobox_mdtype);
this.Controls.Add(this._textbox_leg_leanqtyfactor);
this.Controls.Add(this._checkbox_leg_enablequoting);
this.Controls.Add(this._textbox_leg_qty);
this.Controls.Add(this.groupBox3);
this.Controls.Add(this.groupBox2);
this.Controls.Add(this.groupBox1);
this.Controls.Add(this.label5);
this.Controls.Add(this._label_leanqty_factor);
this.Controls.Add(this.label3);
this.Controls.Add(this.label2);
this.Controls.Add(this.label1);
this.Controls.Add(this._combobox_security);
this.Name = "LegControl";
this.Size = new System.Drawing.Size(291, 1220);
this.groupBox1.ResumeLayout(false);
this.groupBox1.PerformLayout();
this.groupBox2.ResumeLayout(false);
this.groupBox2.PerformLayout();
this.groupBox3.ResumeLayout(false);
this.groupBox3.PerformLayout();
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private ComboBox _combobox_security;
private Label label1;
private Label label2;
private Label label3;
private Label label5;
private GroupBox groupBox1;
private Label label6;
private Label label11;
private CheckBox _checkbox_quote_insidemarketdistance;
private Label label10;
private CheckBox _checkbox_quote_conditional;
private Label label9;
private Label label8;
private CheckBox _checkbox_quote_throttle;
private Label label7;
private CheckBox _checkbox_quote_showqty;
private GroupBox groupBox2;
private Label label13;
private Label label14;
private Label label15;
private CheckBox _checkbox_prehedge_payup;
private Label label16;
private CheckBox _checkbox_prehedge_payless;
private GroupBox groupBox3;
private Label label20;
private Label label21;
private CheckBox _checkbox_posthedge_sizebasedcross;
private Label label12;
private Label label17;
private Label label18;
private CheckBox _checkbox_posthedge_timebasedcross;
private Label label19;
private CheckBox _checkbox_posthedge_ticksawaycross;
private TextBox _textbox_leg_qty;
private CheckBox _checkbox_leg_enablequoting;
private TextBox _textbox_leg_leanqtyfactor;
private ComboBox _combobox_mdtype;
private TextBox _textbox_quote_insidemarketdistance_towarddistance;
private TextBox _textbox_quote_conditional_ticks;
private TextBox _textbox_quote_throttle_outsideticks;
private TextBox _textbox_quote_throttle_insideticks;
private TextBox _textbox_quote_showqty_qty;
private TextBox _textbox_prehedge_payless_paylessticks;
private TextBox _textbox_prehedge_payup_payupticks;
private TextBox _textbox_prehedge_payup_threshold;
private TextBox _textbox_prehedge_payless_threshold;
private TextBox _textbox_posthedge_sizebasedcross_crossticks;
private TextBox _textbox_posthedge_sizebasedcross_threshold;
private TextBox _textbox_posthedge_timesbasedcross_crossticks;
private TextBox _textbox_posthedge_timesbasedcross_timems;
private TextBox _textbox_posthedge_ticksawaycross_crossticks;
private TextBox _textbox_posthedge_ticksawaycross_threshold;
private TextBox _textbox_quote_qho_length;
private TextBox _textbox_leg_price_mult;
private Label label4;
private Label _label_leanqty_factor;
}
}
@@ -0,0 +1,305 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.Diagnostics;
using System.Reflection;
namespace SpreaderFrontEnd {
// UI control that represents a spreader leg. This is hidden within the MainSpreaderForm
public partial class LegControl : UserControl {
private SpreaderLegContract _leg_contract = new SpreaderLegContract();
private IEnumerable<string> _securities = new List<string>();
public LegControl() {
InitializeComponent();
}
public void initialize(IEnumerable<string> securities, SpreaderLegContract? leg_contract) {
_securities = securities;
_leg_contract = leg_contract ?? new SpreaderLegContract();
_populate_ui();
}
#region Data -> UI
private void _populate_ui() {
_populate_basic();
_populate_quote();
_populate_prehedge();
_populate_posthedge();
_initialize_enablement();
_assign_events();
}
void _assign_events() {
foreach (System.Reflection.FieldInfo fi in GetType().GetFields(BindingFlags.NonPublic
| BindingFlags.Public
| BindingFlags.Instance)) {
if (fi.FieldType == typeof(TextBox)) {
object? val = fi.GetValue(this);
if (val != null) {
TextBox? textbox = val as TextBox;
if (textbox != null) {
textbox.TextChanged += Textbox_TextChanged;
textbox.EnabledChanged += Textbox_EnabledChanged;
}
}
}
}
}
private void Textbox_EnabledChanged(object? sender, EventArgs e) {
}
private void Textbox_TextChanged(object? sender, EventArgs e) {
if (sender != null && sender is TextBox) {
TextBox? tb = sender as TextBox;
if (tb == null)
return;
if ("Double".Equals(tb.Tag)) {
double res;
bool parsed = Double.TryParse(tb.Text, out res);
tb.ForeColor = parsed ? Color.Black : Color.Red;
} else {
UInt32 res;
bool parsed = UInt32.TryParse(tb.Text, out res);
tb.ForeColor = parsed ? Color.Black : Color.Red;
}
}
}
void _populate_basic() {
// security
_combobox_security.Items.Clear();
List<string> securities_plus_empty = new List<string>(_securities);
securities_plus_empty.Add(String.Empty);
_combobox_security.Items.AddRange(securities_plus_empty.ToArray());
_combobox_security.Text = _leg_contract.security;
// qty etc
_textbox_leg_qty.Text = _leg_contract.qty.ToString();
_textbox_leg_price_mult.Text = _leg_contract.price_mult.ToString();
_checkbox_leg_enablequoting.Checked = _leg_contract.enable_quoting;
_textbox_leg_leanqtyfactor.Text = _leg_contract.lean_qty_factor.ToString();
_combobox_mdtype.Text = _leg_contract.md_type.ToString();
}
void _populate_quote() {
Debug.Assert(_leg_contract.quote_params != null);
var qp = _leg_contract.quote_params;
_textbox_quote_qho_length.Text = qp.qho_length.ToString();
// show qty
_checkbox_quote_showqty.Checked = qp.show_qty != null;
if (qp.show_qty != null) {
_checkbox_quote_showqty.Checked = true;
_textbox_quote_showqty_qty.Text = qp.show_qty.show_qty.ToString();
}
// throttle
_checkbox_quote_throttle.Checked = qp.throttle != null;
if (qp.throttle != null) {
_textbox_quote_throttle_insideticks.Text = qp.throttle.inside_ticks.ToString();
_textbox_quote_throttle_outsideticks.Text = qp.throttle.outside_ticks.ToString();
}
// conditional
_checkbox_quote_conditional.Checked = qp.conditional != null;
if (qp.conditional != null) {
_textbox_quote_conditional_ticks.Text = qp.conditional.ticks.ToString();
}
// inside market dist.
_checkbox_quote_insidemarketdistance.Checked = qp.inside_market_distance != null;
if (qp.inside_market_distance != null) {
_textbox_quote_insidemarketdistance_towarddistance.Text = qp.inside_market_distance.ToString();
}
}
void _populate_prehedge() {
var ph = _leg_contract.pre_hedge_params;
// payless
_checkbox_prehedge_payless.Checked = ph.payless != null;
if (ph.payless != null) {
_textbox_prehedge_payless_threshold.Text = ph.payless.threshold.ToString();
_textbox_prehedge_payless_paylessticks.Text = ph.payless.payless_ticks.ToString();
}
// payup
_checkbox_prehedge_payup.Checked = ph.payup != null;
if (ph.payup != null) {
_textbox_prehedge_payup_threshold.Text = ph.payup.threshold.ToString();
_textbox_prehedge_payup_payupticks.Text = ph.payup.payup_ticks.ToString();
}
}
void _populate_posthedge() {
var ph = _leg_contract.post_hedge_params;
_checkbox_posthedge_ticksawaycross.Checked = ph.ticks_away_cross != null;
// ticks away cross
if (ph.ticks_away_cross != null) {
_textbox_posthedge_ticksawaycross_threshold.Text = ph.ticks_away_cross.threshold.ToString();
_textbox_posthedge_ticksawaycross_crossticks.Text = ph.ticks_away_cross.cross_ticks.ToString();
}
//time based cross
_checkbox_posthedge_timebasedcross.Checked = ph.time_based_cross != null;
if (ph.time_based_cross != null) {
_textbox_posthedge_timesbasedcross_timems.Text = ph.time_based_cross.time_ms.ToString();
_textbox_posthedge_timesbasedcross_crossticks.Text = ph.time_based_cross.cross_ticks.ToString();
}
// size based cross
_checkbox_posthedge_sizebasedcross.Checked = ph.size_based_cross != null;
if (ph.size_based_cross != null) {
_textbox_posthedge_sizebasedcross_threshold.Text = ph.size_based_cross.threshold.ToString();
_textbox_posthedge_sizebasedcross_crossticks.Text = ph.size_based_cross.ToString();
}
}
// forces checkbox events so that the text boxes connected to it are enabled/disabled
private void _initialize_enablement() {
_checkbox_quote_showqty_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
_checkbox_quote_throttle_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
_checkbox_quote_conditional_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
_checkbox_quote_insidemarketdistance_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
_checkbox_prehedge_payless_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
_checkbox_prehedge_payup_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
_checkbox_posthedge_ticksawaycross_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
_checkbox_posthedge_timebasedcross_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
_checkbox_posthedge_sizebasedcross_CheckedChanged(_checkbox_quote_showqty, new EventArgs());
}
#endregion
#region UI -> Data
public ParsedUIData<SpreaderLegContract> get_data() {
SpreaderLegContract rv = new SpreaderLegContract();
try {
// top part
rv.security = _combobox_security.Text;
rv.qty = Int32.Parse(_textbox_leg_qty.Text);
rv.price_mult = Double.Parse(_textbox_leg_price_mult.Text);
rv.enable_quoting = _checkbox_leg_enablequoting.Checked;
rv.lean_qty_factor = Double.Parse(_textbox_leg_leanqtyfactor.Text);
rv.md_type = SpreaderLegContract.MDType.Merged;
// Quote
rv.quote_params = new SpreaderLegContract.QuoteParams();
rv.quote_params.qho_length = String.IsNullOrEmpty(_textbox_quote_qho_length.Text) ? 0 :
UInt32.Parse(_textbox_quote_qho_length.Text);
rv.quote_params.show_qty = null;
if (_checkbox_quote_showqty.Checked) {
rv.quote_params.show_qty = new SpreaderLegContract.QuoteParams.ShowQty();
rv.quote_params.show_qty.show_qty = UInt32.Parse(_textbox_quote_showqty_qty.Text);
}
rv.quote_params.throttle = null;
if (_checkbox_quote_throttle.Checked) {
rv.quote_params.throttle = new SpreaderLegContract.QuoteParams.Throttle();
rv.quote_params.throttle.inside_ticks = UInt32.Parse(_textbox_quote_throttle_insideticks.Text);
rv.quote_params.throttle.outside_ticks = UInt32.Parse(_textbox_quote_throttle_outsideticks.Text);
}
rv.quote_params.conditional = null;
if (_checkbox_quote_conditional.Checked) {
rv.quote_params.conditional = new SpreaderLegContract.QuoteParams.Conditional();
rv.quote_params.conditional.ticks = Int32.Parse(_textbox_quote_conditional_ticks.Text);
}
rv.quote_params.inside_market_distance = null;
if (_checkbox_quote_insidemarketdistance.Checked) {
rv.quote_params.inside_market_distance = new SpreaderLegContract.QuoteParams.InsideMarketDistance();
rv.quote_params.inside_market_distance.toward_distance =
UInt32.Parse(_textbox_quote_insidemarketdistance_towarddistance.Text);
}
//Pre-Hedge
rv.pre_hedge_params = new SpreaderLegContract.PreHedgeParams();
rv.pre_hedge_params.payless = null;
if (_checkbox_prehedge_payless.Checked) {
rv.pre_hedge_params.payless = new SpreaderLegContract.PreHedgeParams.PayLess();
rv.pre_hedge_params.payless.threshold = UInt32.Parse(_textbox_prehedge_payless_threshold.Text);
rv.pre_hedge_params.payless.payless_ticks =
UInt32.Parse(_textbox_prehedge_payless_paylessticks.Text);
}
rv.pre_hedge_params.payup = null;
if (_checkbox_prehedge_payup.Checked) {
rv.pre_hedge_params.payup = new SpreaderLegContract.PreHedgeParams.PayUp();
rv.pre_hedge_params.payup.threshold = UInt32.Parse(_textbox_prehedge_payup_threshold.Text);
rv.pre_hedge_params.payup.payup_ticks = UInt32.Parse(_textbox_prehedge_payup_payupticks.Text);
}
//Post-Hedge
rv.post_hedge_params = new SpreaderLegContract.PostHedgeParams();
rv.post_hedge_params.ticks_away_cross = null;
if (_checkbox_posthedge_ticksawaycross.Checked) {
rv.post_hedge_params.ticks_away_cross = new SpreaderLegContract.PostHedgeParams.TicksAwayCross();
rv.post_hedge_params.ticks_away_cross.threshold = UInt32.Parse(_textbox_posthedge_ticksawaycross_threshold.Text);
rv.post_hedge_params.ticks_away_cross.cross_ticks = UInt32.Parse(_textbox_posthedge_ticksawaycross_crossticks.Text);
}
rv.post_hedge_params.time_based_cross = null;
if (_checkbox_posthedge_timebasedcross.Checked) {
rv.post_hedge_params.time_based_cross = new SpreaderLegContract.PostHedgeParams.TimeBasedCross();
rv.post_hedge_params.time_based_cross.time_ms =
UInt32.Parse(_textbox_posthedge_timesbasedcross_timems.Text);
rv.post_hedge_params.time_based_cross.cross_ticks =
UInt32.Parse(_textbox_posthedge_timesbasedcross_crossticks.Text);
}
rv.post_hedge_params.size_based_cross = null;
if (_checkbox_posthedge_sizebasedcross.Checked) {
rv.post_hedge_params.size_based_cross = new SpreaderLegContract.PostHedgeParams.SizedBasedCross();
rv.post_hedge_params.size_based_cross.threshold = UInt32.Parse(_textbox_posthedge_sizebasedcross_threshold.Text);
rv.post_hedge_params.size_based_cross.cross_ticks = UInt32.Parse(_textbox_posthedge_sizebasedcross_crossticks.Text);
}
}
catch(Exception ex) {
return new ParsedUIData<SpreaderLegContract>(null, true, ex.Message);
}
return new ParsedUIData<SpreaderLegContract>(rv, false, String.Empty);
}
#endregion
public SpreaderLegContract get_contract() {
return null;
}
#region Checkbox Check Change Events
private void _checkbox_quote_showqty_CheckedChanged(object sender, EventArgs e) {
_textbox_quote_showqty_qty.Enabled = _checkbox_quote_showqty.Checked;
}
private void _checkbox_quote_throttle_CheckedChanged(object sender, EventArgs e) {
_textbox_quote_throttle_insideticks.Enabled = _checkbox_quote_throttle.Checked;
_textbox_quote_throttle_outsideticks.Enabled = _checkbox_quote_throttle.Checked;
}
private void _checkbox_quote_conditional_CheckedChanged(object sender, EventArgs e) {
_textbox_quote_conditional_ticks.Enabled = _checkbox_quote_conditional.Checked;
}
private void _checkbox_quote_insidemarketdistance_CheckedChanged(object sender, EventArgs e) {
_textbox_quote_insidemarketdistance_towarddistance.Enabled = _checkbox_quote_insidemarketdistance.Checked;
}
private void _checkbox_prehedge_payless_CheckedChanged(object sender, EventArgs e) {
_textbox_prehedge_payless_threshold.Enabled = _checkbox_prehedge_payless.Checked;
_textbox_prehedge_payless_paylessticks.Enabled = _checkbox_prehedge_payless.Checked;
}
private void _checkbox_prehedge_payup_CheckedChanged(object sender, EventArgs e) {
_textbox_prehedge_payup_threshold.Enabled = _checkbox_prehedge_payup.Checked;
_textbox_prehedge_payup_payupticks.Enabled = _checkbox_prehedge_payup.Checked;
}
private void _checkbox_posthedge_ticksawaycross_CheckedChanged(object sender, EventArgs e) {
_textbox_posthedge_ticksawaycross_threshold.Enabled = _checkbox_posthedge_ticksawaycross.Checked;
_textbox_posthedge_ticksawaycross_crossticks.Enabled = _checkbox_posthedge_ticksawaycross.Checked;
}
private void _checkbox_posthedge_timebasedcross_CheckedChanged(object sender, EventArgs e) {
_textbox_posthedge_timesbasedcross_timems.Enabled = _checkbox_posthedge_timebasedcross.Checked;
_textbox_posthedge_timesbasedcross_crossticks.Enabled = _checkbox_posthedge_timebasedcross.Checked;
}
private void _checkbox_posthedge_sizebasedcross_CheckedChanged(object sender, EventArgs e) {
_textbox_posthedge_sizebasedcross_threshold.Enabled = _checkbox_posthedge_sizebasedcross.Checked;
_textbox_posthedge_sizebasedcross_crossticks.Enabled = _checkbox_posthedge_sizebasedcross.Checked;
}
#endregion
}
}
@@ -0,0 +1,60 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,152 @@
using System.Windows.Forms;
namespace SpreaderFrontEnd
{
partial class MainSpreaderForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this._button_ok = new System.Windows.Forms.Button();
this._button_cancel = new System.Windows.Forms.Button();
this._label_spreader_name = new System.Windows.Forms.Label();
this._textbox_spreader_name = new System.Windows.Forms.TextBox();
this._legcontrol1 = new SpreaderFrontEnd.LegControl();
this._legcontrol2 = new SpreaderFrontEnd.LegControl();
this._legcontrol3 = new SpreaderFrontEnd.LegControl();
this._legcontrol4 = new SpreaderFrontEnd.LegControl();
this._legcontrol5 = new SpreaderFrontEnd.LegControl();
this.SuspendLayout();
//
// _button_ok
//
this._button_ok.Location = new System.Drawing.Point(1231, 1306);
this._button_ok.Name = "_button_ok";
this._button_ok.Size = new System.Drawing.Size(120, 33);
this._button_ok.TabIndex = 4;
this._button_ok.Text = "OK";
this._button_ok.UseVisualStyleBackColor = false;
this._button_ok.Click += new System.EventHandler(this._button_ok_Click);
//
// _button_cancel
//
this._button_cancel.Location = new System.Drawing.Point(1380, 1306);
this._button_cancel.Name = "_button_cancel";
this._button_cancel.Size = new System.Drawing.Size(121, 33);
this._button_cancel.TabIndex = 5;
this._button_cancel.Text = "Cancel";
this._button_cancel.UseVisualStyleBackColor = false;
this._button_cancel.Click += new System.EventHandler(this._button_cancel_Click);
//
// _label_spreader_name
//
this._label_spreader_name.AutoSize = true;
this._label_spreader_name.Location = new System.Drawing.Point(22, 28);
this._label_spreader_name.Name = "_label_spreader_name";
this._label_spreader_name.Size = new System.Drawing.Size(88, 15);
this._label_spreader_name.TabIndex = 6;
this._label_spreader_name.Text = "Spreader Name";
//
// _textbox_spreader_name
//
this._textbox_spreader_name.Location = new System.Drawing.Point(116, 25);
this._textbox_spreader_name.Name = "_textbox_spreader_name";
this._textbox_spreader_name.Size = new System.Drawing.Size(343, 23);
this._textbox_spreader_name.TabIndex = 0;
//
// _legcontrol1
//
this._legcontrol1.BackColor = System.Drawing.Color.White;
this._legcontrol1.Location = new System.Drawing.Point(22, 67);
this._legcontrol1.Name = "_legcontrol1";
this._legcontrol1.Size = new System.Drawing.Size(291, 1220);
this._legcontrol1.TabIndex = 1;
//
// _legcontrol2
//
this._legcontrol2.BackColor = System.Drawing.Color.White;
this._legcontrol2.Location = new System.Drawing.Point(319, 67);
this._legcontrol2.Name = "_legcontrol2";
this._legcontrol2.Size = new System.Drawing.Size(291, 1220);
this._legcontrol2.TabIndex = 2;
//
// _legcontrol3
//
this._legcontrol3.BackColor = System.Drawing.Color.White;
this._legcontrol3.Location = new System.Drawing.Point(616, 67);
this._legcontrol3.Name = "_legcontrol3";
this._legcontrol3.Size = new System.Drawing.Size(291, 1220);
this._legcontrol3.TabIndex = 3;
//
// _legcontrol4
//
this._legcontrol4.BackColor = System.Drawing.Color.White;
this._legcontrol4.Location = new System.Drawing.Point(913, 67);
this._legcontrol4.Name = "_legcontrol4";
this._legcontrol4.Size = new System.Drawing.Size(291, 1220);
this._legcontrol4.TabIndex = 4;
//
// _legcontrol5
//
this._legcontrol5.BackColor = System.Drawing.Color.White;
this._legcontrol5.Location = new System.Drawing.Point(1210, 67);
this._legcontrol5.Name = "_legcontrol5";
this._legcontrol5.Size = new System.Drawing.Size(291, 1220);
this._legcontrol5.TabIndex = 5;
//
// MainSpreaderForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(1517, 1363);
this.Controls.Add(this._legcontrol5);
this.Controls.Add(this._legcontrol4);
this.Controls.Add(this._legcontrol3);
this.Controls.Add(this._legcontrol2);
this.Controls.Add(this._legcontrol1);
this.Controls.Add(this._textbox_spreader_name);
this.Controls.Add(this._label_spreader_name);
this.Controls.Add(this._button_cancel);
this.Controls.Add(this._button_ok);
this.Name = "MainSpreaderForm";
this.Text = "MainSpreaderForm";
this.ResumeLayout(false);
this.PerformLayout();
}
#endregion
private Button _button_ok;
private Button _button_cancel;
private Label _label_spreader_name;
private TextBox _textbox_spreader_name;
private LegControl _legcontrol1;
private LegControl _legcontrol2;
private LegControl _legcontrol3;
private LegControl _legcontrol4;
private LegControl _legcontrol5;
}
}
@@ -0,0 +1,81 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace SpreaderFrontEnd {
// This is the main UI widget. It is passed a list of securities and a SpreaderContract.
// The SpreaderContract can be empty (which will be the case when creating a new spreader).
// It has 3 public facing members (other than the constructor),
// 2 events that tell whether OK or Cancel was hit, respectively,
// and get_spreader_contract that returns a ParsedUIData<SpreaderContract>
public partial class MainSpreaderForm : Form {
private IEnumerable<LegControl> _leg_controls;
public Action on_OK = () => { };
public Action on_cancel = () => { };
private readonly IEnumerable<string> _securities;
private SpreaderContract _spreader_contract;
public MainSpreaderForm(IEnumerable<string> securities,
SpreaderContract spreader_contract)
: base() {
InitializeComponent();
_leg_controls = new LegControl[] { _legcontrol1, _legcontrol2, _legcontrol3, _legcontrol4, _legcontrol5 };
_securities = securities;
System.Diagnostics.Debug.Assert(spreader_contract != null);
// System.Diagnostics.Debug.Assert(spreader_contract.spread_legs.Count() == SpreaderContract.max_nlegs);
_spreader_contract = spreader_contract;
_textbox_spreader_name.Text = _spreader_contract.name;
for (var i = 0; i < _leg_controls.Count(); i++) {
SpreaderLegContract? lc = i < _spreader_contract.spread_legs.Count() ?
_spreader_contract.spread_legs.ElementAt(i) : null;
_leg_controls.ElementAt(i).initialize(securities, lc);
}
}
public ParsedUIData<SpreaderContract> get_spreader_contract() {
if (String.IsNullOrEmpty(_textbox_spreader_name.Text)) {
return new ParsedUIData<SpreaderContract>(null, true, "Spreader name is not set");
}
List<SpreaderLegContract> leg_contracts = new List<SpreaderLegContract>();
uint i = 0;
foreach (LegControl lc in _leg_controls) {
i++;
var leg_res = lc.get_data();
if (leg_res.is_error) {
return new ParsedUIData<SpreaderContract>(null, true, $"Leg: {i} is not set correctly");
}
leg_contracts.Add(leg_res.ui_data);
}
IEnumerable<SpreaderLegContract> leg_contracts_rv =
leg_contracts.Where(l => !String.IsNullOrEmpty(l.security));
if (leg_contracts_rv.Count() <= 1) {
return new ParsedUIData<SpreaderContract>(
null, true, "Spreader needs at least 2 legs");
}
// check the number of legs
return new ParsedUIData<SpreaderContract>(
new SpreaderContract(_textbox_spreader_name.Text, leg_contracts_rv), false, String.Empty);
}
private void _button_ok_Click(object sender, EventArgs e) {
ParsedUIData<SpreaderContract> ui_data = get_spreader_contract();
if (ui_data.is_error) {
string err_txt = $"The following error happened \n{ui_data.error_txt}";
System.Windows.Forms.MessageBox.Show(err_txt, "Spreader Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
return;
}
on_OK();
}
private void _button_cancel_Click(object sender, EventArgs e) {
on_cancel();
}
}
}
@@ -0,0 +1,60 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,20 @@
namespace SpreaderFrontEnd {
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
public class ParsedUIData<T> {
public readonly T ui_data;
public readonly bool is_error;
public readonly string error_txt;
public ParsedUIData(T ui_data, bool is_error, string error_txt) {
this.ui_data = ui_data;
this.is_error = is_error;
this.error_txt = error_txt;
}
}
}
@@ -0,0 +1,18 @@
using System;
using System.Windows.Forms;
namespace SpreaderFrontEnd {
internal static class Program {
/// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main() {
SyntheticBookTest.run();
// To customize application configuration such as set high DPI settings or default font,
// see https://aka.ms/applicationconfiguration.
ApplicationConfiguration.Initialize();
Application.Run(new TestForm());
}
}
}
@@ -0,0 +1,35 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SpreaderFrontEnd
{
[Serializable]
// Represents a spreader contract (anologous ot the protobuf on the C++ side)
// We could have generated managed wrappers from the protobuf, but we did not want
// to make your build process complicated hence opted for this class.
// It is quite simply a wrapper around an enumeration of SpreaderLegContract
public class SpreaderContract
{
public static uint max_nlegs = 5;
public string name = String.Empty;
public IList<SpreaderLegContract> spread_legs = new List<SpreaderLegContract>();
public SpreaderContract(string name, IEnumerable<SpreaderLegContract> spread_legs)
{
this.name = name;
this.spread_legs = spread_legs.ToList();
}
public static SpreaderContract empty
{
get
{
return new SpreaderContract("UNNAMED",
Enumerable.Repeat<SpreaderLegContract>(new SpreaderLegContract(), 5));
}
}
}
}
@@ -0,0 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>WinExe</OutputType>
<TargetFramework>net6.0-windows</TargetFramework>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<LanguageVersion>7</LanguageVersion>
</PropertyGroup>
</Project>
@@ -0,0 +1,90 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace SpreaderFrontEnd
{
[Serializable]
// Represents a single leg of the spreader. It is made up of
// 1. a main section consisting of the security, qty, price mult etc
// 2. Quote parameters
// 3. Pre-Hedge parameters
// 4. Post-Hedge parameters
// It maps pretty directly to the UI (LegControl)
public class SpreaderLegContract
{
public enum MDType {
Direct = 0,
Merged = 1,
}
public string security = String.Empty;
public int qty = 1;
public double price_mult = 1;
public bool enable_quoting = false;
public double lean_qty_factor = 1;
public MDType md_type = MDType.Direct;
public QuoteParams quote_params = new QuoteParams();
public PreHedgeParams pre_hedge_params = new PreHedgeParams();
public PostHedgeParams post_hedge_params = new PostHedgeParams();
public class QuoteParams
{
public class ShowQty {
public UInt32 show_qty = 1;
}
public class Throttle {
public UInt32 inside_ticks = 0;
public UInt32 outside_ticks = 0;
}
public class Conditional {
public Int32 ticks = 0;
}
public class InsideMarketDistance {
public UInt32 toward_distance = 0;
}
public UInt32 qho_length = 0;
public ShowQty? show_qty = null;
public Throttle? throttle = null;
public Conditional? conditional = null;
public InsideMarketDistance? inside_market_distance = null;
}
public class PreHedgeParams
{
public class PayLess {
public UInt32 threshold = 0;
public UInt32 payless_ticks = 0;
}
public class PayUp
{
public UInt32 threshold = 0;
public UInt32 payup_ticks = 0;
}
public PayLess? payless = null;
public PayUp? payup = null;
}
public class PostHedgeParams
{
public class TicksAwayCross {
public UInt32 threshold = 0;
public UInt32 cross_ticks = 0;
}
public class TimeBasedCross {
public UInt32 time_ms = 0;
public UInt32 cross_ticks = 0;
}
public class SizedBasedCross {
public UInt32 threshold = 0;
public UInt32 cross_ticks = 0;
}
public TicksAwayCross? ticks_away_cross = null;
public TimeBasedCross? time_based_cross = null;
public SizedBasedCross? size_based_cross = null;
}
}
}
+79
View File
@@ -0,0 +1,79 @@
using System.Windows.Forms;
namespace SpreaderFrontEnd
{
partial class TestForm
{
/// <summary>
/// Required designer variable.
/// </summary>
private System.ComponentModel.IContainer components = null;
/// <summary>
/// Clean up any resources being used.
/// </summary>
/// <param name="disposing">true if managed resources should be disposed; otherwise, false.</param>
protected override void Dispose(bool disposing)
{
if (disposing && (components != null))
{
components.Dispose();
}
base.Dispose(disposing);
}
#region Windows Form Designer generated code
/// <summary>
/// Required method for Designer support - do not modify
/// the contents of this method with the code editor.
/// </summary>
private void InitializeComponent()
{
this._button_launch_new = new System.Windows.Forms.Button();
this._button_load_existing = new System.Windows.Forms.Button();
this.SuspendLayout();
//
// _button_launch_new
//
this._button_launch_new.Location = new System.Drawing.Point(96, 46);
this._button_launch_new.Name = "_button_launch_new";
this._button_launch_new.Size = new System.Drawing.Size(169, 26);
this._button_launch_new.TabIndex = 0;
this._button_launch_new.Text = "Launch New";
this._button_launch_new.UseVisualStyleBackColor = true;
this._button_launch_new.Click += new System.EventHandler(this._button_launch_new_Click);
//
// _button_load_existing
//
this._button_load_existing.Location = new System.Drawing.Point(96, 114);
this._button_load_existing.Name = "_button_load_existing";
this._button_load_existing.Size = new System.Drawing.Size(169, 26);
this._button_load_existing.TabIndex = 1;
this._button_load_existing.Text = "Load Existing";
this._button_load_existing.UseVisualStyleBackColor = true;
this._button_load_existing.Click += new System.EventHandler(this._button_load_existing_Click);
//
// TestForm
//
this.AutoScaleDimensions = new System.Drawing.SizeF(7F, 15F);
this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font;
this.ClientSize = new System.Drawing.Size(371, 213);
this.Controls.Add(this._button_load_existing);
this.Controls.Add(this._button_launch_new);
this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle;
this.MaximizeBox = false;
this.MinimizeBox = false;
this.Name = "TestForm";
this.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide;
this.Text = "Test Launcher";
this.ResumeLayout(false);
}
#endregion
private Button _button_launch_new;
private Button _button_load_existing;
}
}
@@ -0,0 +1,91 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
namespace SpreaderFrontEnd {
// Test class that demostrates how to use the MainSpreaderForm.
// It has 2 buttons. "Launch New" launches a brand new spreader (empty UI)
// while "Launch Existing" loads the MainSpreaderForm with an existing
// spreader (hard coded arbitrary values)
public partial class TestForm : Form {
private IEnumerable<string> _securities = new string[] {
"ZCG23",
"ZCH23",
"ZCJ23",
"ZCL23",
"ZCM23"
};
public TestForm() {
InitializeComponent();
}
private void _button_launch_new_Click(object sender, EventArgs e) {
_show_form(SpreaderContract.empty);
}
private void _button_load_existing_Click(object sender, EventArgs e) {
var sc = _generate_test_contract();
_show_form(sc);
}
private void _show_form(SpreaderContract spreader_contract) {
MainSpreaderForm f = new MainSpreaderForm(_securities, spreader_contract);
f.on_OK = () => {
MessageBox.Show("Received OK", "OK", MessageBoxButtons.OK, MessageBoxIcon.Information);
f.Close();
};
f.on_cancel = () => {
MessageBox.Show("Received Cancel", "Cancel", MessageBoxButtons.OK, MessageBoxIcon.Warning);
f.Close();
};
f.ShowDialog();
}
private SpreaderContract _generate_test_contract() {
List<SpreaderLegContract> lcs = new List<SpreaderLegContract>();
for (uint i = 0; i < 3; i++) {
SpreaderLegContract lc = _generate_test_leg_contract(i);
lcs.Add(lc);
}
SpreaderContract sc = new SpreaderContract("TEST SPREADER", lcs);
return sc;
}
private SpreaderLegContract _generate_test_leg_contract(uint idx) {
SpreaderLegContract rv = new SpreaderLegContract();
rv.security = _securities.ElementAt(0);
rv.qty = 1;
rv.price_mult = 1;
rv.enable_quoting = true;
rv.lean_qty_factor = 2;
rv.md_type = SpreaderLegContract.MDType.Merged;
// quote
rv.quote_params = new SpreaderLegContract.QuoteParams();
rv.quote_params.qho_length = 0;
rv.quote_params.show_qty = new SpreaderLegContract.QuoteParams.ShowQty();
rv.quote_params.show_qty.show_qty = 10;
rv.quote_params.throttle = new SpreaderLegContract.QuoteParams.Throttle();
rv.quote_params.throttle.inside_ticks = 3;
rv.quote_params.throttle.outside_ticks = 2;
// pre hedge
rv.pre_hedge_params = new SpreaderLegContract.PreHedgeParams();
rv.pre_hedge_params.payless = new SpreaderLegContract.PreHedgeParams.PayLess();
rv.pre_hedge_params.payless.threshold = 1000;
rv.pre_hedge_params.payless.payless_ticks = 7;
rv.pre_hedge_params.payup = new SpreaderLegContract.PreHedgeParams.PayUp();
rv.pre_hedge_params.payup.threshold = 500;
rv.pre_hedge_params.payup.payup_ticks = 12;
// post hedge
rv.post_hedge_params = new SpreaderLegContract.PostHedgeParams();
rv.post_hedge_params.ticks_away_cross = new SpreaderLegContract.PostHedgeParams.TicksAwayCross();
rv.post_hedge_params.ticks_away_cross.threshold = 10;
rv.post_hedge_params.ticks_away_cross.cross_ticks = 3;
rv.post_hedge_params.time_based_cross = new SpreaderLegContract.PostHedgeParams.TimeBasedCross();
rv.post_hedge_params.time_based_cross.time_ms = 20;
rv.post_hedge_params.time_based_cross.cross_ticks = 5;
return rv;
}
}
};
@@ -0,0 +1,60 @@
<root>
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>
@@ -0,0 +1,230 @@
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
namespace SpreaderFrontEnd {
public static class SyntheticBookTest {
/// <summary>
/// Run synthetic book computation tests
/// </summary>
public static int run() {
// Seed the test with a standard butterfly spread. Leg size multipliers will be applied to
// each leg to generate more variations to test.
var legs = new List<SpreaderLegContract> {
new SpreaderLegContract() {
security = "Leg1Sec",
qty = 1
},
new SpreaderLegContract() {
security = "Leg2Sec",
qty = -2
},
new SpreaderLegContract() {
security = "Leg3Sec",
qty = 1
}
};
var leg_multipliers = new List<int> { 1, -1, 2, -2, 3, -3 };
var contract = new SpreaderContract("Test", legs);
// Define a book (seed) for each leg. Quantities will be added to each price level for each
// leg's book to generate more variations to test.
var leg1_book = new Book(
bids: new BookSide(levels: new List<PriceLevel> {
new PriceLevel(price: 3, qty: 10),
new PriceLevel(price: 2, qty: 4),
new PriceLevel(price: 1, qty: 6)
}),
asks: new BookSide(levels: new List<PriceLevel> {
new PriceLevel(price: 4, qty: 5),
new PriceLevel(price: 5, qty: 7),
new PriceLevel(price: 6, qty: 8)
}));
var leg2_book = new Book(
bids: new BookSide(levels: new List<PriceLevel> {
new PriceLevel(price: 3, qty: 1),
new PriceLevel(price: 2, qty: 7),
new PriceLevel(price: 1, qty: 2)
}),
asks: new BookSide(levels: new List<PriceLevel> {
new PriceLevel(price: 4, qty: 3),
new PriceLevel(price: 5, qty: 3),
new PriceLevel(price: 6, qty: 5)
}));
var leg3_book = new Book(
bids: new BookSide(levels: new List<PriceLevel> {
new PriceLevel(price: 3, qty: 2),
new PriceLevel(price: 2, qty: 1),
new PriceLevel(price: 1, qty: 8)
}),
asks: new BookSide(levels: new List<PriceLevel> {
new PriceLevel(price: 4, qty: 4),
new PriceLevel(price: 5, qty: 3),
new PriceLevel(price: 6, qty: 4)
}));
var level_qty_additions = new List<uint> { 1, 2, 3, 4 };
var leg_books = new List<Book> { leg1_book, leg2_book, leg3_book };
for (int i = 0; i < leg_multipliers.Count; i++) {
var leg_mult = leg_multipliers[i];
for (int j = 0; j < contract.spread_legs.Count; j++) {
// Apply leg mult
contract.spread_legs[j].qty *= leg_mult;
foreach (var lqa in level_qty_additions) {
foreach(var lb in leg_books) {
// Apply qty addition to bid-levels
for (int level_idx = 0; level_idx < lb.bids.levels.Count; level_idx++) {
var orig_level = lb.bids.levels[level_idx];
lb.bids.levels[level_idx] = orig_level.add_qty(lqa);
run(leg_books, contract);
// Subtract the qty that was just added so each qty addition is applied to
// the original level
lb.bids.levels[level_idx] = lb.bids.levels[level_idx].subtract_qty(lqa);
}
// Apply qty additions to ask-levels
for (int level_idx = 0; level_idx < lb.asks.levels.Count; level_idx++) {
var orig_level = lb.asks.levels[level_idx];
lb.asks.levels[level_idx] = orig_level.add_qty(lqa);
run(leg_books, contract);
// Subtract the qty that was just added so each qty addition is applied to
// the original level
lb.asks.levels[level_idx] = lb.asks.levels[level_idx].subtract_qty(lqa);
}
}
}
// Divide by leg_mult so the each mult is applied to the original leg
contract.spread_legs[j].qty /= leg_mult;
}
}
return 0;
}
public static void run(List<Book> leg_books, SpreaderContract contract) {
var synth_book = SpreaderBookCalculator.compute_spreader_book(contract, leg_books);
for (int i = 0; i < contract.spread_legs.Count; i++) {
Console.WriteLine($"Leg{i} (qty {contract.spread_legs[i].qty}): {leg_books[i]}");
}
Console.WriteLine($"Synthetic Book: {synth_book}");
var leg_book_tups = _to_leg_book_tuples(leg_books, contract);
var expected_book = _compute_synth_book(leg_book_tups);
Console.WriteLine($"Expected Book: {expected_book}");
Debug.Assert(expected_book.Equals(synth_book));
}
static List<Tuple<Book, int>> _to_leg_book_tuples(List<Book> leg_books, SpreaderContract contract) {
Debug.Assert(leg_books.Count == contract.spread_legs.Count);
var rv = new List<Tuple<Book, int>>();
for (int i = 0; i < leg_books.Count; i++) {
rv.Add(Tuple.Create(leg_books[i], contract.spread_legs[i].qty));
}
return rv;
}
static List<Tuple<BookSide, int>> _to_leg_book_side_tuples(List<Tuple<Book, int>> leg_books, bool is_bid) {
var leg_book_tups = new List<Tuple<BookSide, int>>();
for (int i = 0; i < leg_books.Count; i++) {
var leg_size = leg_books[i].Item2;
var leg_book = leg_books[i].Item1;
var is_buy_leg = leg_size > 0;
var leg_book_side = is_buy_leg == is_bid ? leg_book.bids : leg_book.asks;
leg_book_tups.Add(Tuple.Create(leg_book_side, leg_size));
}
return leg_book_tups;
}
static Book _compute_synth_book(List<Tuple<Book, int>> leg_books) {
var leg_bids = _to_leg_book_side_tuples(leg_books, true);
var leg_asks = _to_leg_book_side_tuples(leg_books, false);
var synth_bids = _compute_synth_book_side(leg_bids);
var synth_asks = _compute_synth_book_side(leg_asks);
return new Book(synth_bids, synth_asks);
}
// <summary>
// Computes the synthetic book in a simple/unoptimized way, to ensure correctness. Books
// generated by the 'optimized' routine, which will be used in practice, should match those
// generated by this one.
// </summary>
static BookSide _compute_synth_book_side(IEnumerable<Tuple<BookSide, int>> leg_books) {
// Clone the books so we can mutate them without changing the input 'leg_books'
var leg_books_list = leg_books.Select(lb => Tuple.Create(lb.Item1.Clone(), lb.Item2)).ToList();
var levels = new List<PriceLevel>();
var top_level = _get_top_level(leg_books_list);
while (top_level.qty > 0) {
levels.Add(top_level);
foreach (var lb in leg_books_list) {
_remove_qty_from_top(lb.Item1, (int)(top_level.qty * Math.Abs(lb.Item2)));
}
top_level = _get_top_level(leg_books_list);
}
return new BookSide(levels);
}
static PriceLevel _get_top_level(IEnumerable<Tuple<BookSide, int>> leg_books) {
var leg_sizes = leg_books.Select(lb => lb.Item2).ToList();
var top_levels = leg_books.Select(_get_top_level).ToList();
var qty = top_levels.Select(l => l.qty).Min();
if (qty == 0) {
return PriceLevel.empty;
}
double price = 0;
for (int i = 0; i < top_levels.Count; i++) {
var leg_price = top_levels[i].price;
var leg_size = leg_sizes[i];
if (leg_size < 0) {
price -= leg_price;
} else {
price += leg_price;
}
}
return new PriceLevel(price, qty);
}
static PriceLevel _get_top_level(Tuple<BookSide, int> leg_book) {
return _get_top_level(leg_book.Item1, leg_book.Item2);
}
static PriceLevel _get_top_level(BookSide book, int leg_size) {
if (book.levels.Count == 0) {
return PriceLevel.empty;
}
var first_level = book.levels.First();
var abs_leg_size = Math.Abs(leg_size);
if (first_level.qty >= abs_leg_size) {
return new PriceLevel(first_level.price * abs_leg_size, (uint)(first_level.qty / abs_leg_size));
}
double price = first_level.qty * first_level.price;
var qty_rem = abs_leg_size - first_level.qty;
for (int i = 1; i < book.levels.Count; i++) {
var level = book.levels[i];
if (level.qty >= qty_rem) {
price += qty_rem * level.price;
return new PriceLevel(price, 1);
}
price += level.qty * level.price;
qty_rem -= level.qty;
}
return PriceLevel.empty;
}
/// <summary>
/// Removes the specified quantity from the top of the book
/// </summary>
static void _remove_qty_from_top(BookSide book, int qty) {
var subtracted_qty = 0;
while (subtracted_qty < qty && book.levels.Count > 0) {
var first_level = book.levels[0];
var qty_to_subtract = qty - subtracted_qty;
if (first_level.qty > qty_to_subtract) {
first_level = first_level.subtract_qty((uint)qty_to_subtract);
book.levels[0] = first_level;
return;
}
book.levels.RemoveAt(0);
subtracted_qty += (int)first_level.qty;
}
}
}
}
@@ -0,0 +1,78 @@
using System;
using System.Reflection;
using System.Runtime.Serialization.Formatters.Binary;
using System.IO;
namespace SpreaderFrontEnd
{
public class Utils
{
public static void bin_serialize<T>(T t, Stream str)
{
#pragma warning disable SYSLIB0011
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(str, t);
#pragma warning restore SYSLIB0011
}
public static T bin_deserialize<T>(Stream str)
{
#pragma warning disable SYSLIB0011
BinaryFormatter bf = new BinaryFormatter();
T rv = (T)bf.Deserialize(str);
#pragma warning restore SYSLIB0011
return rv;
}
public static T? basic_deep_clone<T>(T t)
{
try
{
MemoryStream str = new MemoryStream();
bin_serialize(t, str);
str.Position = 0;
return bin_deserialize<T>(str);
}
catch
{
return default(T);
}
}
public static object? CloneObject(object objSource)
{
//step : 1 Get the type of source object and create a new instance of that type
Type typeSource = objSource.GetType();
object? objTarget = Activator.CreateInstance(typeSource);
//Step2 : Get all the properties of source object type
PropertyInfo[] propertyInfo = typeSource.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
//Step : 3 Assign all source property to taget object 's properties
foreach (PropertyInfo property in propertyInfo)
{
//Check whether property can be written to
if (property.CanWrite)
{
//Step : 4 check whether property type is value type, enum or string type
if (property.PropertyType.IsValueType || property.PropertyType.IsEnum || property.PropertyType.Equals(typeof(System.String)))
{
property.SetValue(objTarget, property.GetValue(objSource, null), null);
}
//else property type is object/complex types, so need to recursively call this method until the end of the tree is reached
else
{
object? objPropertyValue = property.GetValue(objSource, null);
if (objPropertyValue == null)
{
property.SetValue(objTarget, null, null);
}
else
{
property.SetValue(objTarget, CloneObject(objPropertyValue), null);
}
}
}
}
return objTarget;
}
}
}
@@ -0,0 +1,23 @@
{
"runtimeTarget": {
"name": ".NETCoreApp,Version=v6.0",
"signature": ""
},
"compilationOptions": {},
"targets": {
".NETCoreApp,Version=v6.0": {
"SpreaderFrontEnd/1.0.0": {
"runtime": {
"SpreaderFrontEnd.dll": {}
}
}
}
},
"libraries": {
"SpreaderFrontEnd/1.0.0": {
"type": "project",
"serviceable": false,
"sha512": ""
}
}
}
@@ -0,0 +1,15 @@
{
"runtimeOptions": {
"tfm": "net6.0",
"frameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "6.0.0"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "6.0.0"
}
]
}
}
+1 -1
View File
@@ -1 +1 @@
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/domain/Currency.proto;api/domain/SecurityType.proto;api/domain/Venue.proto;api/io/URL.proto;api/md/MarketDataSubscriptionParams.proto;api/md/binance/BinanceMarketDataSubscriptionParams.proto;api/md/deribit/DeribitMarketDataSubscriptionParams.proto;api/md/ftx/FTXMarketDataSubscriptionParams.proto;api/md/glbx/GlbxMarketDataSubscriptionParams.proto;api/md/huobi/HuobiMarketDataSubscriptionParams.proto;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/AnalyticsService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;communincation/websockets/tests/WebsocketSyncClientTests.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/algo_types.proto;config/protos/cme_reference_data.proto;config/protos/exchange_sim_cfg.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/market_data.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/ports.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spartan.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/symbols.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/protos/ui.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain/AggressorSide.cpp;domain/NewOrder.cpp;domain/OrderDefinition.cpp;domain/OrderStatus.cpp;domain/OrderUpdate.cpp;domain/Side.cpp;domain/TimeInForce.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;io/QueryParams.cpp;io/URL.cpp;io/http/CurlHandle.cpp;io/http/HTTPClient.cpp;io/http/HTTPHeaders.cpp;io/http/HTTPRequest.cpp;io/http/HTTPResponse.cpp;io/websocket/WebsocketClient.cpp;io/websocket/WebsocketClientConfiguration.cpp;io/websocket/WebsocketError.cpp;io/websocket/WebsocketMessage.cpp;io/websocket/WebsocketSSLClientInternal.cpp;io/websocket/tests/WebsocketClientTests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOSeLine truncated
set(sources api/apicore.proto;api/apigateway.proto;api/apioptions.proto;api/core/ApiClientInterop.cpp;api/core/ApiServerInterop.cpp;api/domain/Currency.proto;api/domain/SecurityType.proto;api/domain/Venue.proto;api/io/URL.proto;api/md/MarketDataSubscriptionParams.proto;api/md/binance/BinanceMarketDataSubscriptionParams.proto;api/md/deribit/DeribitMarketDataSubscriptionParams.proto;api/md/ftx/FTXMarketDataSubscriptionParams.proto;api/md/glbx/GlbxMarketDataSubscriptionParams.proto;api/md/huobi/HuobiMarketDataSubscriptionParams.proto;api/public/ApiServer.cpp;api/public/ClientChannel.cpp;api/public/QuickbusClientChannel.cpp;api/public/ServerChannel.cpp;api/public/ServiceContext.cpp;api/service_helpers/AdminServiceUtils.cpp;api/service_helpers/AlgoServiceUtils.cpp;api/service_helpers/PositionUtilities.cpp;api/services/AdminService.proto;api/services/AlgoDefinitionService.proto;api/services/AlgoService.proto;api/services/AnalyticsService.proto;api/services/ApiUnitTestService.proto;api/services/AuthService.proto;api/services/BasePositionService.proto;api/services/ErrorReportService.proto;api/services/HelloService.proto;api/services/MBOService.proto;api/services/MarginService.proto;api/services/MarketDataAuxiliaryService.proto;api/services/MarketDataRelay.proto;api/services/MarketDataService.proto;api/services/MoneyFileService.proto;api/services/OrderService.proto;api/services/OutOfProcessURLListener.proto;api/services/PersistenceService.proto;api/services/PositionService.proto;api/services/ShellDiagnosticService.proto;api/services/ShellLatencyService.proto;api/services/SyntheticSecurityService.proto;api/test/ApiTests.cpp;api/test/HelloServiceServer.cpp;communincation/websockets/tests/WebsocketSyncClientTests.cpp;config/ConfigCheck.cpp;config/ConfigDownloader.cpp;config/ConfigErrorCollector.cpp;config/ConfigLog.cpp;config/ConfigPropertyEvaluator.cpp;config/ConfigValueQueue.cpp;config/ConfigurationException.cpp;config/ConfigurationState.cpp;config/PreprocessInputStream.cpp;config/config.cpp;config/defaults/MarketData.conf;config/defaults/Ports.conf;config/defaults/Symbols.conf;config/defaults/cme_reference_data.conf;config/defaults/edge.conf;config/defaults/jetfix.conf;config/defaults/keycode.conf;config/defaults/logging.conf;config/defaults/orderman_cfg.conf;config/defaults/orders_fills_cleanup.conf;config/defaults/position_cfg.conf;config/defaults/span.conf;config/defaults/spreader.conf;config/defaults/testkeys.conf;config/defaults/threads.conf;config/defaults/toggles.conf;config/extensions/extensions.proto;config/invalid/invalid.proto;config/protos/algo_types.proto;config/protos/cme_reference_data.proto;config/protos/exchange_sim_cfg.proto;config/protos/jetfix.proto;config/protos/keycode.proto;config/protos/logging.proto;config/protos/market_data.proto;config/protos/mercury_cfg.proto;config/protos/orderman_cfg.proto;config/protos/orders_fills_cleanup.proto;config/protos/perflog_cfg.proto;config/protos/ports.proto;config/protos/position_cfg.proto;config/protos/quickbus_cfg.proto;config/protos/span.proto;config/protos/spartan.proto;config/protos/spreader.proto;config/protos/subprocess.proto;config/protos/symbols.proto;config/protos/system.proto;config/protos/testkeys.proto;config/protos/threads.proto;config/protos/toggles.proto;config/protos/ui.proto;config/tests/HTTPServer.cpp;config/tests/config_ref_tests.cpp;config/tests/config_tests.cpp;config/text_format/text_format.cpp;domain/AggressorSide.cpp;domain/NewOrder.cpp;domain/OrderDefinition.cpp;domain/OrderStatus.cpp;domain/OrderUpdate.cpp;domain/Side.cpp;domain/TimeInForce.cpp;domain_model/Security.cpp;domain_model/Security.proto;domain_model/SecurityTests.cpp;domain_model/sod.cpp;domain_model/tests/domain_tests.cpp;expression/BooleanGrammar.cpp;expression/ExpressionEvaluator.cpp;expression/ParserExceptions.cpp;expression/Value.cpp;expression/tests/expression_parser_tests.cpp;io/QueryParams.cpp;io/URL.cpp;io/http/CurlHandle.cpp;io/http/HTTPClient.cpp;io/http/HTTPHeaders.cpp;io/http/HTTPRequest.cpp;io/http/HTTPResponse.cpp;io/websocket/WebsocketClient.cpp;io/websocket/WebsocketClientConfiguration.cpp;io/websocket/WebsocketError.cpp;io/websocket/WebsocketMessage.cpp;io/websocket/WebsocketSSLClientInternal.cpp;io/websocket/tests/WebsocketClientTests.cpp;jetfix/codec/FIXArray.cpp;jetfix/common/OutFIX.cpp;jetfix/connection/AsyncReader.cpp;jetfix/connection/IConnection.cpp;jetfix/connection/SocketConnection.cpp;jetfix/gateway/CoreGateway.cpp;jetfix/gateway/JetFIXGateway.cpp;jetfix/gateway/Session.cpp;jetfix/store/FIXFileStore.cpp;jetfix/store/session_name.cpp;jetfix/tests/async_reader_tests.cpp;jetfix/tests/decoding_tests.cpp;jetfix/tests/fix_encoding_stream_tests.cpp;jetfix/tests/fix_fragment_tests.cpp;jetfix/tests/jetfix_performance_tests.cpp;jetfix/tests/sample_fix.cpp;jetfix/tests/store_tests.cpp;logging/LoggerHub.cpp;logging/Sink.cpp;logging/logging.cpp;logging/tests/logging_tests.cpp;md/Diag/MDPeriodicDumper.cpp;md/Diag/ShmDumper.cpp;md/MBOProcessor.cpp;md/MBOSeLine truncated
+2 -2
View File
@@ -13,7 +13,7 @@
#include "lib/config/protos/quickbus_cfg.pb.h"
#include "lib/config/protos/system.pb.h"
#ifdef WIN32
#ifdef _WIN32
#include <excpt.h>
#endif
@@ -59,7 +59,7 @@ CAPI Quickbus *api_create_quickbus(const char *qbconfig, long length) {
}
CAPI void api_destroy_quickbus(Quickbus *qbus) {
#ifndef WIN32
#ifndef _WIN32
if (qbus != nullptr) {
delete qbus;
}
+2 -2
View File
@@ -29,7 +29,7 @@
#include "lib/config_defaults.h"
#include "lib/config_proto_files.h"
#ifdef WIN32
#ifdef _WIN32
#include <Winsock2.h>
#include <d2d1.h>
#else
@@ -549,7 +549,7 @@ void ConfigurationState::generate_all_config_proto(IConfigLog &log) {
&bts_config_dp, config_message, message_desc, ++field_number);
}
} else {
#ifdef WIN32
#ifdef _WIN32
// on linux the linker might throw away config protos that
// are not used in sparkd. On windows we build a dll so that
// should not happen
+2
View File
@@ -1,6 +1,8 @@
#pragma once
#ifndef _WIN32
#include <bits/stdint-uintn.h>
#endif
#include <cstdint>
#include <string>
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef WIN32
#ifndef _WIN32
#include "OutFIX.h"
namespace bts {
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef WIN32
#ifndef _WIN32
#include "AsyncReader.h"
#include "IConnection.h"
+1 -1
View File
@@ -1,3 +1,3 @@
#ifndef WIN32
#ifndef _WIN32
#include "IConnection.h"
#endif
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef WIN32
#ifndef _WIN32
#include "JetFIXGateway.h"
namespace bts {
+1 -1
View File
@@ -34,7 +34,7 @@ struct LogRec {
};
GCC_DIAG_RESTORE()
#ifndef WIN32
#ifndef _WIN32
// static_assert(sizeof(LogRec) == 256, "LogRec should be 128 bytes");
#endif
+3 -3
View File
@@ -13,7 +13,7 @@
#include <fstream>
#include <iostream>
#ifndef WIN32
#ifndef _WIN32
#include <syslog.h>
#endif
@@ -22,7 +22,7 @@ namespace log {
struct InitSysLog {
InitSysLog() {
#ifndef WIN32
#ifndef _WIN32
openlog("spark", 0, LOG_USER);
#endif
}
@@ -72,7 +72,7 @@ Sink console_sink(LoggingConfig const &) {
}
Sink syslog_sink(LoggingConfig const &cfg) {
#ifdef WIN32
#ifdef _WIN32
return null_sink();
#else
auto f = formatter(cfg);
+1 -1
View File
@@ -133,7 +133,7 @@ MDProducer::MDProducer(Platform platform, MDProducerFlags flags)
: _mutex(new std::mutex()), _producers(), _id_recorder(),
_platform(platform), _flags(flags),
_smapper(flags & MDProducerFlags::SecurityMapper) {}
#ifndef WIN32
#ifndef _WIN32
MDProducer::MDProducer(
Platform platform, SecurityIDRecorder recorder, MDProducerFlags flags)
: _mutex(new std::mutex()), _producers(),
+1 -1
View File
@@ -1,6 +1,6 @@
#include "SecurityDB.h"
#include "lib/logging/logging.h"
#ifndef WIN32
#ifndef _WIN32
#include <SQLiteCpp/SQLiteCpp.h>
#include <sqlite/sqlite3.h>
+1 -1
View File
@@ -1,5 +1,5 @@
#include "lib/domain_model/Security.h"
#ifndef WIN32
#ifndef _WIN32
#include "SecurityIDRecorder.h"
#include "lib/config/config.h"
+1 -1
View File
@@ -1,5 +1,5 @@
#pragma once
#ifndef WIN32
#ifndef _WIN32
#include "MDProducerHandle.h"
#include "common.h"
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef WIN32
#ifndef _WIN32
#include "SettlementRecorder.h"
#include "lib/config/config.h"
#include "lib/config/protos/system.pb.h"
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef WIN32
#ifndef _WIN32
#include "SyntheticSecurityService.h"
#include "lib/api/public/ServiceContext.h"
+4 -4
View File
@@ -164,7 +164,7 @@ class Co2Tests {
"}");
}
#ifndef WIN32
#ifndef _WIN32
void test_security_id_recorder(const std::string &db_name) {
// Verify callbacks happen after work complete
auto db1 = sqlite::open_db(db_name.c_str());
@@ -1036,7 +1036,7 @@ class Co2Tests {
REQUIRE(security_count == 5);
}
#ifndef WIN32
#ifndef _WIN32
void test_settlement_recorder() {
using namespace datetime;
security_key k1 = 401, k2 = 402;
@@ -1135,7 +1135,7 @@ TEST_CASE("CO2Tests", "[md]") {
co2_tests->setup();
#ifndef WIN32
#ifndef _WIN32
SECTION("test_security_id_recorder") {
co2_tests->test_security_id_recorder(":memory:");
co2_tests->test_security_id_recorder("file::memory:?cache=shared");
@@ -1174,7 +1174,7 @@ TEST_CASE("CO2Tests", "[md]") {
SECTION("test_retry_unresolved_spread") {
co2_tests->test_retry_unresolved_spread();
}
#ifndef WIN32
#ifndef _WIN32
SECTION("test_settlement_recorder") {
co2_tests->test_settlement_recorder();
}
+1 -1
View File
@@ -1,4 +1,4 @@
#ifndef WIN32
#ifndef _WIN32
#include "OnixsUtils.h"
#include "lib/config/config.h"
#include "lib/config/protos/market_data.pb.h"
+436 -421
View File
@@ -1,449 +1,464 @@
#include "EMExecutionGateway.h"
#include "lib/api/services/AdminService.pb.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/domain_model/Security.pb.h"
#include "lib/domain_model/common.h"
#include "lib/logging/logging.h"
#include "lib/orderman/CancelOrderMsg.h"
#include "lib/orderman/CancelReplaceOrderMsg.h"
#include "lib/orderman/CreateOrderMsg.h"
#include "lib/orderman/OrderMsg.h"
#include "lib/orderman/events/NewAdminDataEvent.h"
#include "lib/orderman/events/NewExecutionReportEvent.h"
#include "lib/orderman/events/NewSecuritiesEvent.h"
#include "lib/orderman/orderman_utils/CancelReject.h"
#include "lib/orderman/orderman_utils/ExchangePosition.h"
#include "lib/orderman/orderman_utils/ExecutionReport.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/os/os.h"
#include "lib/tracing/bts_trace.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/datetime.h"
#include "lib/utils/helper_macros.h"
#include "server/sparkd/md/platforms/emumba/EmMDListener.h"
#include "server/sparkd/md/platforms/emumba/IEMOrderMuxer.h"
// #include "EMExecutionGateway.h"
// #include "lib/api/services/AdminService.pb.h"
// #include "lib/api/services/OrderService.pb.h"
// #include "lib/domain_model/Security.pb.h"
// #include "lib/domain_model/common.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/CancelOrderMsg.h"
// #include "lib/orderman/CancelReplaceOrderMsg.h"
// #include "lib/orderman/CreateOrderMsg.h"
// #include "lib/orderman/OrderMsg.h"
// #include "lib/orderman/events/NewAdminDataEvent.h"
// #include "lib/orderman/events/NewExecutionReportEvent.h"
// #include "lib/orderman/events/NewSecuritiesEvent.h"
// #include "lib/orderman/orderman_utils/CancelReject.h"
// #include "lib/orderman/orderman_utils/ExchangePosition.h"
// #include "lib/orderman/orderman_utils/ExecutionReport.h"
// #include "lib/orderman/orderman_utils/orderman_common.h"
// #include "lib/os/os.h"
// #include "lib/tracing/bts_trace.h"
// #include "lib/utils/bts_assert.h"
// #include "lib/utils/datetime.h"
// #include "lib/utils/helper_macros.h"
// #include "server/sparkd/md/platforms/emumba/EmMDListener.h"
// #include "server/sparkd/md/platforms/emumba/IEMOrderMuxer.h"
#include "lib/utils/Uuid.h"
#include <atomic>
#include <boost/pool/object_pool.hpp>
#include <boost/proto/transform/impl.hpp>
#include <cmath>
#include <cstdint>
#include <emumba-deribit-connector/market_data_common.h>
#include <emumba-deribit-connector/order_entry_common.h>
#include <string>
#include <tbb/concurrent_queue.h>
#include <unordered_map>
#include <variant>
#include <vector>
// #include "lib/utils/Uuid.h"
// #include <atomic>
// #include <boost/pool/object_pool.hpp>
// #include <boost/proto/transform/impl.hpp>
// #include <cmath>
// #include <cstdint>
// #include <emumba-deribit-connector/market_data_common.h>
// #include <emumba-deribit-connector/order_entry_common.h>
// #include <string>
// #include <tbb/concurrent_queue.h>
// #include <unordered_map>
// #include <variant>
// #include <vector>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using EMOrderKey = std::int64_t;
// using EMOrderKey = std::int64_t;
// Order callbacks from order_mux are copied here and placed in the tbb queue
struct EMCallbackMsg {
// didnt make it const since tbb::queue needs a copy cons
emumba::deribit_connector::data_types::error ec;
emumba::order_entry::order_key order_key;
EMOrderState order_state;
};
// // Order callbacks from order_mux are copied here and placed in the tbb queue
// struct EMCallbackMsg {
// // didnt make it const since tbb::queue needs a copy cons
// emumba::deribit_connector::data_types::error ec;
// emumba::order_entry::order_key order_key;
// EMOrderState order_state;
// };
// Position callbacks from order_mux are copied here and placed in the tbb queue
struct EMExchangePositionCallbackMsg {
std::vector<emumba::order_entry::position> positions;
};
// // Position callbacks from order_mux are copied here and placed in the tbb
// queue struct EMExchangePositionCallbackMsg {
// std::vector<emumba::order_entry::position> positions;
// };
struct EMExecutionGateway::PImpl {
PImpl(md::SecurityOracle o) : sec_oracle(o) {
LOG(OrderMan, Info, "EMExecutionGateway::PImpl constructor");
}
// callback cashed data
api::admin::AdminData admin_data = {};
md::SecurityOracle sec_oracle;
// interface (IExecutionGateway) callbacks
OnExeReport on_exe_report;
OnGwStatusChange on_gw_status_change;
OnCancelReject on_cancel_reject;
OnExchangePositionState on_exchange_position_state;
// orders are sent out and callbacks received via IEMOrderMuxer
IEMOrderMuxer *order_mux = nullptr;
size_t msg_seq_num = 0;
boost::object_pool<ExecutionReport> exe_report_pool;
// bts key to em keys
std::unordered_map<OrderKey, EMOrderKey> key_map = {};
// em's key vs order, we keep the create message because it has information
// that we need to build and fire the ExecutionReport (data that is not
// provided by the EM order callback)
std::unordered_map<EMOrderKey, CreateOrderMsg> order_map = {};
// this is updated as we get exchange positions
ExchangePosition exchange_position;
// since we get callbacks from IEMOrderMuxer in a different thread we queue
// them here and then dequeue on the dispatch_incoming_fix_messages call
tbb::concurrent_queue<EMCallbackMsg> order_cb_queue =
tbb::concurrent_queue<EMCallbackMsg>();
tbb::concurrent_queue<EMExchangePositionCallbackMsg> exch_pos_queue =
tbb::concurrent_queue<EMExchangePositionCallbackMsg>();
// periodic query (temporary)
bts::datetime::PTime last_pos_fire_time = bts::datetime::now();
uint32_t pos_fire_period_ms = 5000;
};
// struct EMExecutionGateway::PImpl {
// PImpl(md::SecurityOracle o) : sec_oracle(o) {
// LOG(OrderMan, Info, "EMExecutionGateway::PImpl constructor");
// }
// // callback cashed data
// api::admin::AdminData admin_data = {};
// md::SecurityOracle sec_oracle;
// // interface (IExecutionGateway) callbacks
// OnExeReport on_exe_report;
// OnGwStatusChange on_gw_status_change;
// OnCancelReject on_cancel_reject;
// OnExchangePositionState on_exchange_position_state;
// // orders are sent out and callbacks received via IEMOrderMuxer
// IEMOrderMuxer *order_mux = nullptr;
// size_t msg_seq_num = 0;
// boost::object_pool<ExecutionReport> exe_report_pool;
// // bts key to em keys
// std::unordered_map<OrderKey, EMOrderKey> key_map = {};
// // em's key vs order, we keep the create message because it has
// information
// // that we need to build and fire the ExecutionReport (data that is not
// // provided by the EM order callback)
// std::unordered_map<EMOrderKey, CreateOrderMsg> order_map = {};
// // this is updated as we get exchange positions
// ExchangePosition exchange_position;
// // since we get callbacks from IEMOrderMuxer in a different thread we
// queue
// // them here and then dequeue on the dispatch_incoming_fix_messages call
// tbb::concurrent_queue<EMCallbackMsg> order_cb_queue =
// tbb::concurrent_queue<EMCallbackMsg>();
// tbb::concurrent_queue<EMExchangePositionCallbackMsg> exch_pos_queue =
// tbb::concurrent_queue<EMExchangePositionCallbackMsg>();
// // periodic query (temporary)
// bts::datetime::PTime last_pos_fire_time = bts::datetime::now();
// uint32_t pos_fire_period_ms = 5000;
// };
// helper functions ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// // helper functions
// ///////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
api::orderman::NativeOrderStatus native_order_status(
const EMOrderState &state) {
switch (state.status) {
case emumba::order_entry::status::working:
// TODO: check partially filled
return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_NEW;
case emumba::order_entry::status::completed:
// maybe check fills
// if (state.total_fills.value() > 0)
// return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_FILLED;
return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_FILLED;
case emumba::order_entry::status::canceled:
return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_CANCELED;
case emumba::order_entry::status::rejected:
return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_REJECTED;
case emumba::order_entry::status::untriggered:
return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_UNDEFINED;
}
bts_assert(false);
return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_UNDEFINED;
}
// api::orderman::NativeOrderStatus native_order_status(
// const EMOrderState &state) {
// switch (state.status) {
// case emumba::order_entry::status::working:
// // TODO: check partially filled
// return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_NEW;
// case emumba::order_entry::status::completed:
// // maybe check fills
// // if (state.total_fills.value() > 0)
// // return
// api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_FILLED; return
// api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_FILLED;
// case emumba::order_entry::status::canceled:
// return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_CANCELED;
// case emumba::order_entry::status::rejected:
// return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_REJECTED;
// case emumba::order_entry::status::untriggered:
// return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_UNDEFINED;
// }
// bts_assert(false);
// return api::orderman::NativeOrderStatus::NATIVE_ORDER_STATUS_UNDEFINED;
// }
// get em's qty
double get_quantity(const emumba::market_data::quantity &q) {
const auto &v = q.value();
if (std::holds_alternative<double>(v))
return std::get<double>(v);
return static_cast<double>(std::get<uint32_t>(v));
}
// // get em's qty
// double get_quantity(const emumba::market_data::quantity &q) {
// const auto &v = q.value();
// if (std::holds_alternative<double>(v))
// return std::get<double>(v);
// return static_cast<double>(std::get<uint32_t>(v));
// }
// gets the exchange's (not em's) order id
std::string get_exchange_oid(const emumba::order_entry::order_state &state) {
using namespace emumba::order_entry;
const auto &exch_state = state.exchange_order_state;
bts_assert(std::holds_alternative<deribit_order_state>(exch_state));
const auto &deribit_state = std::get<deribit_order_state>(exch_state);
return deribit_state.exchange_order_id;
}
// // gets the exchange's (not em's) order id
// std::string get_exchange_oid(const emumba::order_entry::order_state &state) {
// using namespace emumba::order_entry;
// const auto &exch_state = state.exchange_order_state;
// bts_assert(std::holds_alternative<deribit_order_state>(exch_state));
// const auto &deribit_state = std::get<deribit_order_state>(exch_state);
// return deribit_state.exchange_order_id;
// }
emumba::order_entry::time_in_force convert_tif(api::orderman::TimeInForce tif) {
using namespace emumba::order_entry;
using namespace bts::api::orderman;
switch (tif) {
case TimeInForce::TIME_IN_FORCE_UNDEFINED:
return time_in_force::day;
case TimeInForce::TIME_IN_FORCE_DAY:
return time_in_force::day;
case TimeInForce::TIME_IN_FORCE_GTC:
return time_in_force::gtc;
case TimeInForce::TIME_IN_FORCE_IOC:
return time_in_force::ioc;
case TimeInForce::TIME_IN_FORCE_DATE:
return time_in_force::date;
default:
bts_assert(false);
return time_in_force::date;
}
}
// emumba::order_entry::time_in_force convert_tif(api::orderman::TimeInForce
// tif) {
// using namespace emumba::order_entry;
// using namespace bts::api::orderman;
// switch (tif) {
// case TimeInForce::TIME_IN_FORCE_UNDEFINED:
// return time_in_force::day;
// case TimeInForce::TIME_IN_FORCE_DAY:
// return time_in_force::day;
// case TimeInForce::TIME_IN_FORCE_GTC:
// return time_in_force::gtc;
// case TimeInForce::TIME_IN_FORCE_IOC:
// return time_in_force::ioc;
// case TimeInForce::TIME_IN_FORCE_DATE:
// return time_in_force::date;
// default:
// bts_assert(false);
// return time_in_force::date;
// }
// }
emumba::order_entry::order_type convert_order_type(
api::orderman::NativeOrderType t) {
using namespace emumba::order_entry;
using namespace bts::api::orderman;
switch (t) {
case NativeOrderType::NATIVE_ORDER_TYPE_LIMIT:
return order_type::limit_order;
case NativeOrderType::NATIVE_ORDER_TYPE_MARKET:
return order_type::market_order;
case NativeOrderType::NATIVE_ORDER_TYPE_MARKET_LIMIT:
return order_type::market_limit_order;
case NativeOrderType::NATIVE_ORDER_TYPE_STOP:
return order_type::stop_market_order;
case NativeOrderType::NATIVE_ORDER_TYPE_STOP_LIMIT:
return order_type::stop_limit_order;
default:
bts_assert(false);
return order_type::limit_order;
}
}
// emumba::order_entry::order_type convert_order_type(
// api::orderman::NativeOrderType t) {
// using namespace emumba::order_entry;
// using namespace bts::api::orderman;
// switch (t) {
// case NativeOrderType::NATIVE_ORDER_TYPE_LIMIT:
// return order_type::limit_order;
// case NativeOrderType::NATIVE_ORDER_TYPE_MARKET:
// return order_type::market_order;
// case NativeOrderType::NATIVE_ORDER_TYPE_MARKET_LIMIT:
// return order_type::market_limit_order;
// case NativeOrderType::NATIVE_ORDER_TYPE_STOP:
// return order_type::stop_market_order;
// case NativeOrderType::NATIVE_ORDER_TYPE_STOP_LIMIT:
// return order_type::stop_limit_order;
// default:
// bts_assert(false);
// return order_type::limit_order;
// }
// }
EMExecutionGateway::EMExecutionGateway(md::SecurityOracle o)
: IExecutionGateway(), me(new PImpl(o)) {
LOG(OrderMan, Info,
"EMExecutionGateway::EMExecutionGateway constructor t_id: {} "
"t_name: {}",
os::get_thread_id(), os::get_application_name());
}
// EMExecutionGateway::EMExecutionGateway(md::SecurityOracle o)
// : IExecutionGateway(), me(new PImpl(o)) {
// LOG(OrderMan, Info,
// "EMExecutionGateway::EMExecutionGateway constructor t_id: {} "
// "t_name: {}",
// os::get_thread_id(), os::get_application_name());
// }
EMExecutionGateway::~EMExecutionGateway() {}
// EMExecutionGateway::~EMExecutionGateway() {}
void EMExecutionGateway::start() {
_init();
LOG(OrderMan, Info,
"EMExecutionGateway::start order_mux aquired: {} t_id: {} "
"t_name: {}",
me->order_mux != nullptr, os::get_thread_id(), os::get_thread_name());
}
// void EMExecutionGateway::start() {
// _init();
// LOG(OrderMan, Info,
// "EMExecutionGateway::start order_mux aquired: {} t_id: {} "
// "t_name: {}",
// me->order_mux != nullptr, os::get_thread_id(), os::get_thread_name());
// }
// this is a periodic timer
void EMExecutionGateway::dispatch_incoming_fix_messages() {
EMCallbackMsg cb_msg = {};
// keep popping messages until no more
while (me->order_cb_queue.try_pop(cb_msg)) {
LOG(OrderMan, Info,
"EMExecutionGateway::dispatch_incoming_fix_messages "
"dispatched cb for key: {}",
cb_msg.order_key.value());
_process_cb_and_fire_er(cb_msg);
}
_test_pos_cb();
}
// // this is a periodic timer
// void EMExecutionGateway::dispatch_incoming_fix_messages() {
// EMCallbackMsg cb_msg = {};
// // keep popping messages until no more
// while (me->order_cb_queue.try_pop(cb_msg)) {
// LOG(OrderMan, Info,
// "EMExecutionGateway::dispatch_incoming_fix_messages "
// "dispatched cb for key: {}",
// cb_msg.order_key.value());
// _process_cb_and_fire_er(cb_msg);
// }
// _test_pos_cb();
// }
void EMExecutionGateway::on_securities(
const mercury::Update<NewSecuritiesEvent> &) {
LOG(OrderMan, Info, "EMExecutionGateway::on_securities t_id: {} t_name: {}",
os::get_thread_id(), os::get_thread_name());
}
// void EMExecutionGateway::on_securities(
// const mercury::Update<NewSecuritiesEvent> &) {
// LOG(OrderMan, Info, "EMExecutionGateway::on_securities t_id: {} t_name:
// {}",
// os::get_thread_id(), os::get_thread_name());
// }
void EMExecutionGateway::on_admin_data(
const mercury::Update<NewAdminDataEvent> &update) {
LOG(OrderMan, Info, "EMExecutionGateway::on_admin_data t_id: {} t_name: {}",
os::get_thread_id(), os::get_thread_name());
if (update.events) {
for (auto const &ev : *(update.events))
me->admin_data.CopyFrom(*ev.admin_data);
}
}
// void EMExecutionGateway::on_admin_data(
// const mercury::Update<NewAdminDataEvent> &update) {
// LOG(OrderMan, Info, "EMExecutionGateway::on_admin_data t_id: {} t_name:
// {}",
// os::get_thread_id(), os::get_thread_name());
// if (update.events) {
// for (auto const &ev : *(update.events))
// me->admin_data.CopyFrom(*ev.admin_data);
// }
// }
void EMExecutionGateway::send_fix(const jetfix::OutFIXPtr &, trace::CorID) {
bts_assert_msg(false, "EMExecutionGateway does not support fix");
}
// void EMExecutionGateway::send_fix(const jetfix::OutFIXPtr &, trace::CorID) {
// bts_assert_msg(false, "EMExecutionGateway does not support fix");
// }
void EMExecutionGateway::send_msgs(
std::vector<OrderMsg> const &msg_vec, trace::CorID) {
if (!_init()) {
LOG(OrderMan, Error, "EMExecutionGateway::send_msgs order_mux not set");
}
LOG(OrderMan, Info, "EMExecutionGateway::send_msgs count: {}",
msg_vec.size());
for (const auto &m : msg_vec) {
if (std::holds_alternative<CreateOrderMsg>(m)) {
const CreateOrderMsg &cr_msg = std::get<CreateOrderMsg>(m);
_send_create(cr_msg);
} else if (std::holds_alternative<CancelReplaceOrderMsg>(m)) {
const CancelReplaceOrderMsg &cxl_repl_msg =
std::get<CancelReplaceOrderMsg>(m);
_send_cxl_replace(cxl_repl_msg);
} else if (std::holds_alternative<CancelOrderMsg>(m)) {
const CancelOrderMsg &cxl_msg = std::get<CancelOrderMsg>(m);
_send_cxl(cxl_msg);
}
}
}
// void EMExecutionGateway::send_msgs(
// std::vector<OrderMsg> const &msg_vec, trace::CorID) {
// if (!_init()) {
// LOG(OrderMan, Error, "EMExecutionGateway::send_msgs order_mux not
// set");
// }
// LOG(OrderMan, Info, "EMExecutionGateway::send_msgs count: {}",
// msg_vec.size());
// for (const auto &m : msg_vec) {
// if (std::holds_alternative<CreateOrderMsg>(m)) {
// const CreateOrderMsg &cr_msg = std::get<CreateOrderMsg>(m);
// _send_create(cr_msg);
// } else if (std::holds_alternative<CancelReplaceOrderMsg>(m)) {
// const CancelReplaceOrderMsg &cxl_repl_msg =
// std::get<CancelReplaceOrderMsg>(m);
// _send_cxl_replace(cxl_repl_msg);
// } else if (std::holds_alternative<CancelOrderMsg>(m)) {
// const CancelOrderMsg &cxl_msg = std::get<CancelOrderMsg>(m);
// _send_cxl(cxl_msg);
// }
// }
// }
OnExeReport &EMExecutionGateway::on_exe_report() { return me->on_exe_report; }
OnGwStatusChange &EMExecutionGateway::on_gw_status_change() {
return me->on_gw_status_change;
}
OnCancelReject &EMExecutionGateway::on_cancel_reject() {
return me->on_cancel_reject;
}
OnExchangePositionState &EMExecutionGateway::on_exchange_position_state() {
return me->on_exchange_position_state;
}
// OnExeReport &EMExecutionGateway::on_exe_report() { return me->on_exe_report;
// } OnGwStatusChange &EMExecutionGateway::on_gw_status_change() {
// return me->on_gw_status_change;
// }
// OnCancelReject &EMExecutionGateway::on_cancel_reject() {
// return me->on_cancel_reject;
// }
// OnExchangePositionState &EMExecutionGateway::on_exchange_position_state() {
// return me->on_exchange_position_state;
// }
bool EMExecutionGateway::_init() {
if (me->order_mux == nullptr) {
me->order_mux = md::EmMDListener::order_mux();
if (me->order_mux != nullptr) {
me->order_mux->order_update_cb() =
[this](const auto &er, const auto &key, const auto &state) {
LOG(OrderMan, Info,
"EMExecutionGateway received order callback for "
"key: {}",
key.value());
me->order_cb_queue.push({er, key, state});
};
me->order_mux->exchange_position_update_cb() =
[this](const auto &exch_pos) {
LOG(OrderMan, Info,
"EMExecutionGateway received exchange position "
"callback, "
"positions: {}",
exch_pos.size());
// lots of copying going on, but this is periodic
EMExchangePositionCallbackMsg pos_cb = {};
for (const auto p_ptr : exch_pos)
pos_cb.positions.push_back(*p_ptr);
me->exch_pos_queue.push(pos_cb);
};
}
}
return me->order_mux != nullptr;
}
// bool EMExecutionGateway::_init() {
// if (me->order_mux == nullptr) {
// me->order_mux = md::EmMDListener::order_mux();
// if (me->order_mux != nullptr) {
// me->order_mux->order_update_cb() =
// [this](const auto &er, const auto &key, const auto &state) {
// LOG(OrderMan, Info,
// "EMExecutionGateway received order callback for "
// "key: {}",
// key.value());
// me->order_cb_queue.push({er, key, state});
// };
// me->order_mux->exchange_position_update_cb() =
// [this](const auto &exch_pos) {
// LOG(OrderMan, Info,
// "EMExecutionGateway received exchange position "
// "callback, "
// "positions: {}",
// exch_pos.size());
// // lots of copying going on, but this is periodic
// EMExchangePositionCallbackMsg pos_cb = {};
// for (const auto p_ptr : exch_pos)
// pos_cb.positions.push_back(*p_ptr);
// me->exch_pos_queue.push(pos_cb);
// };
// }
// }
// return me->order_mux != nullptr;
// }
// converts the CreateOrderMsg to emumba::order_entry::new_order and
// sends it to EM gateway
void EMExecutionGateway::_send_create(const CreateOrderMsg &c) {
LOG(OrderMan, Info, "EMExecutionGateway::_send_create key: {}", c.key);
emumba::order_entry::new_order new_order;
// only assuming deribit for now, as we add exchanges we fix it here
bts_assert_msg(
c.security->has_deribit(), "EMExecutionGateway only handles Deribit");
std::string desc = c.security->deribit().security_desc();
new_order.order_def.sec_id.value() = desc;
new_order.order_def.order_side = c.side == bts::api::SIDE_BUY
? emumba::market_data::side::buy
: emumba::market_data::side::sell;
new_order.order_def.time_in_frc = convert_tif(c.time_in_force);
// price, qty
new_order.px = emumba::market_data::price(c.price.value());
new_order.qty.value() = c.quantity.whole_value();
// order_def.exchange_order
emumba::order_entry::deribit_order_definition deribit_def;
emumba::order_entry::deribit_order_def_optional deribit_def_optionals;
deribit_def.ord_type = convert_order_type(c.order_type);
deribit_def_optionals.mmp = false;
deribit_def.optional_fields = deribit_def_optionals;
new_order.order_def.exchange_order = deribit_def;
// place order now
emumba::order_entry::order_key new_order_key =
me->order_mux->launch_new_order(new_order);
EMOrderKey em_key = new_order_key.value();
me->order_map.insert({em_key, c});
me->key_map.insert({c.key, em_key});
}
// // converts the CreateOrderMsg to emumba::order_entry::new_order and
// // sends it to EM gateway
// void EMExecutionGateway::_send_create(const CreateOrderMsg &c) {
// LOG(OrderMan, Info, "EMExecutionGateway::_send_create key: {}", c.key);
// emumba::order_entry::new_order new_order;
// // only assuming deribit for now, as we add exchanges we fix it here
// bts_assert_msg(
// c.security->has_deribit(), "EMExecutionGateway only handles Deribit");
// std::string desc = c.security->deribit().security_desc();
// new_order.order_def.sec_id.value() = desc;
// new_order.order_def.order_side = c.side == bts::api::SIDE_BUY
// ? emumba::market_data::side::buy
// : emumba::market_data::side::sell;
// new_order.order_def.time_in_frc = convert_tif(c.time_in_force);
// // price, qty
// new_order.px = emumba::market_data::price(c.price.value());
// new_order.qty.value() = c.quantity.whole_value();
// // order_def.exchange_order
// emumba::order_entry::deribit_order_definition deribit_def;
// emumba::order_entry::deribit_order_def_optional deribit_def_optionals;
// deribit_def.ord_type = convert_order_type(c.order_type);
// deribit_def_optionals.mmp = false;
// deribit_def.optional_fields = deribit_def_optionals;
// new_order.order_def.exchange_order = deribit_def;
// // place order now
// emumba::order_entry::order_key new_order_key =
// me->order_mux->launch_new_order(new_order);
// EMOrderKey em_key = new_order_key.value();
// me->order_map.insert({em_key, c});
// me->key_map.insert({c.key, em_key});
// }
void EMExecutionGateway::_send_cxl_replace(
const CancelReplaceOrderMsg &cxl_rpl) {
LOG(OrderMan, Info, "EMExecutionGateway::_send_cxl_replace key: {}",
cxl_rpl.create_fields.key);
auto it = me->key_map.find(cxl_rpl.create_fields.key);
if (it == me->key_map.end()) {
// TODO: should this be an ER ?
LOG(OrderMan, Error, "Could not modify order key: {}, could not find key",
cxl_rpl.create_fields.key);
return;
}
EMOrderKey em_key = it->second;
auto cr_it = me->order_map.find(em_key);
auto &cr_msg = cr_it->second;
// this is controversial: because we update the price qty right away
cr_msg = cxl_rpl.create_fields;
// how do we handle the case of partial fills here, or does EMOrder
// does it ?
const emumba::order_entry::order_update update = {
emumba::order_entry::order_key(em_key),
emumba::market_data::price(cxl_rpl.create_fields.price.value()),
emumba::market_data::quantity(
cxl_rpl.create_fields.quantity.whole_value())};
me->order_mux->update_order(update);
}
// void EMExecutionGateway::_send_cxl_replace(
// const CancelReplaceOrderMsg &cxl_rpl) {
// LOG(OrderMan, Info, "EMExecutionGateway::_send_cxl_replace key: {}",
// cxl_rpl.create_fields.key);
// auto it = me->key_map.find(cxl_rpl.create_fields.key);
// if (it == me->key_map.end()) {
// // TODO: should this be an ER ?
// LOG(OrderMan, Error, "Could not modify order key: {}, could not find
// key",
// cxl_rpl.create_fields.key);
// return;
// }
// EMOrderKey em_key = it->second;
// auto cr_it = me->order_map.find(em_key);
// auto &cr_msg = cr_it->second;
// // this is controversial: because we update the price qty right away
// cr_msg = cxl_rpl.create_fields;
// // how do we handle the case of partial fills here, or does EMOrder
// // does it ?
// const emumba::order_entry::order_update update = {
// emumba::order_entry::order_key(em_key),
// emumba::market_data::price(cxl_rpl.create_fields.price.value()),
// emumba::market_data::quantity(
// cxl_rpl.create_fields.quantity.whole_value())};
// me->order_mux->update_order(update);
// }
void EMExecutionGateway::_send_cxl(const CancelOrderMsg &cxl) {
LOG(OrderMan, Info, "EMExecutionGateway::_send_create key: {}", cxl.key);
auto it = me->key_map.find(cxl.key);
if (it == me->key_map.end()) {
LOG(OrderMan, Error,
"Could not cancel order key: {}, could not find order key", cxl.key);
me->on_cancel_reject(
{cxl.key, CancelRejectReason::UnknownOrder, "Could not find order"});
return;
}
auto key = it->second;
me->order_mux->cancel_order(emumba::order_entry::order_key(key));
}
// void EMExecutionGateway::_send_cxl(const CancelOrderMsg &cxl) {
// LOG(OrderMan, Info, "EMExecutionGateway::_send_create key: {}", cxl.key);
// auto it = me->key_map.find(cxl.key);
// if (it == me->key_map.end()) {
// LOG(OrderMan, Error,
// "Could not cancel order key: {}, could not find order key",
// cxl.key);
// me->on_cancel_reject(
// {cxl.key, CancelRejectReason::UnknownOrder, "Could not find
// order"});
// return;
// }
// auto key = it->second;
// me->order_mux->cancel_order(emumba::order_entry::order_key(key));
// }
void EMExecutionGateway::_process_cb_and_fire_er(const EMCallbackMsg &em_cb) {
// this is the only tricky part here because ...
EMOrderKey key = em_cb.order_key.value();
auto it = me->order_map.find(key);
if (it == me->order_map.end()) {
LOG(OrderMan, Error,
"EMExecutionGateway::_process_cb_and_fire_er key: {} not found", key);
}
const auto &cr_msg = it->second;
// cant reate one on the stack
ExecutionReport *er_p = me->exe_report_pool.construct();
ExecutionReport &er = *er_p;
to_array(cr_msg.routing_record->account(), &er.account_name.value());
to_array(cr_msg.routing_record->operator_id(), &er.operator_id.value());
er.order_key = cr_msg.key;
er.msg_seq_num =
me->msg_seq_num++; // this is needed for fix, here we just ++ it
er.security = cr_msg.security;
er.leg_security = nullptr;
er.side = cr_msg.side;
er.time_in_force = cr_msg.time_in_force;
er.order_type = cr_msg.order_type;
// TODO
er.exec_type = api::orderman::ExecType::EXEC_TYPE_NEW; // TODO
er.order_status = native_order_status(em_cb.order_state);
er.manual_order_indicator =
api::orderman::ManualOrderIndicator::MANUAL_ORDER_INDICATOR_NO;
std::string exch_oid = std::to_string(em_cb.order_key.value());
// TODO, should we put EM order or Deribit ?
// (this is not used by EMNativeOrder so we are ok)
auto &er_exch_oid = std::get<ExchangeOrderID>(er.exchange_order_id).value();
to_array(exch_oid, &er_exch_oid);
er.price.value() = em_cb.order_state.order_px;
er.quantity = em_cb.order_state.order_qty;
// fractional qty
// fills
er.avg_price.value() = em_cb.order_state.avg_filled_price;
er.filled_quantity = em_cb.order_state.total_fills;
if (!em_cb.order_state.trades.empty()) {
er.exec_type = api::orderman::ExecType::EXEC_TYPE_PARTIAL_FILL;
if (em_cb.order_state.status == emumba::order_entry::status::completed) {
er.exec_type = api::orderman::ExecType::EXEC_TYPE_FILL;
}
er.last_price = Price(em_cb.order_state.last_filled_price);
er.last_shares = Quantity(em_cb.order_state.last_filled_qty);
// TODO: this should be part of the update
utils::Uuid guid;
std::string fill_id = guid.to_text_string();
to_array(fill_id, &er.exchange_fill_id.value());
} else {
er.last_shares = Quantity(0);
}
er.max_show = Quantity(0);
er.exchange = cr_msg.security->exchange();
ExchangeTimeStamp64 now = ExchangeTimeStamp64(datetime::unix_nano_now());
er.transact_time = now;
LOG(OrderMan, Info,
"EMExecutionGateway::_process_cb_and_fire_er firing er: {}",
er.to_string());
me->on_exe_report(er, {});
}
// void EMExecutionGateway::_process_cb_and_fire_er(const EMCallbackMsg &em_cb)
// {
// // this is the only tricky part here because ...
// EMOrderKey key = em_cb.order_key.value();
// auto it = me->order_map.find(key);
// if (it == me->order_map.end()) {
// LOG(OrderMan, Error,
// "EMExecutionGateway::_process_cb_and_fire_er key: {} not found",
// key);
// }
// const auto &cr_msg = it->second;
// // cant reate one on the stack
// ExecutionReport *er_p = me->exe_report_pool.construct();
// ExecutionReport &er = *er_p;
// to_array(cr_msg.routing_record->account(), &er.account_name.value());
// to_array(cr_msg.routing_record->operator_id(), &er.operator_id.value());
// er.order_key = cr_msg.key;
// er.msg_seq_num =
// me->msg_seq_num++; // this is needed for fix, here we just ++ it
// er.security = cr_msg.security;
// er.leg_security = nullptr;
// er.side = cr_msg.side;
// er.time_in_force = cr_msg.time_in_force;
// er.order_type = cr_msg.order_type;
// // TODO
// er.exec_type = api::orderman::ExecType::EXEC_TYPE_NEW; // TODO
// er.order_status = native_order_status(em_cb.order_state);
// er.manual_order_indicator =
// api::orderman::ManualOrderIndicator::MANUAL_ORDER_INDICATOR_NO;
// std::string exch_oid = std::to_string(em_cb.order_key.value());
// // TODO, should we put EM order or Deribit ?
// // (this is not used by EMNativeOrder so we are ok)
// auto &er_exch_oid =
// std::get<ExchangeOrderID>(er.exchange_order_id).value();
// to_array(exch_oid, &er_exch_oid);
// er.price.value() = em_cb.order_state.order_px;
// er.quantity = em_cb.order_state.order_qty;
// // fractional qty
// // fills
// er.avg_price.value() = em_cb.order_state.avg_filled_price;
// er.filled_quantity = em_cb.order_state.total_fills;
// if (!em_cb.order_state.trades.empty()) {
// er.exec_type = api::orderman::ExecType::EXEC_TYPE_PARTIAL_FILL;
// if (em_cb.order_state.status == emumba::order_entry::status::completed)
// {
// er.exec_type = api::orderman::ExecType::EXEC_TYPE_FILL;
// }
// er.last_price = Price(em_cb.order_state.last_filled_price);
// er.last_shares = Quantity(em_cb.order_state.last_filled_qty);
// // TODO: this should be part of the update
// utils::Uuid guid;
// std::string fill_id = guid.to_text_string();
// to_array(fill_id, &er.exchange_fill_id.value());
// } else {
// er.last_shares = Quantity(0);
// }
// er.max_show = Quantity(0);
// er.exchange = cr_msg.security->exchange();
// ExchangeTimeStamp64 now = ExchangeTimeStamp64(datetime::unix_nano_now());
// er.transact_time = now;
// LOG(OrderMan, Info,
// "EMExecutionGateway::_process_cb_and_fire_er firing er: {}",
// er.to_string());
// me->on_exe_report(er, {});
// }
void EMExecutionGateway::_test_pos_cb() {
if (me->order_mux == nullptr)
return;
auto msecs = bts::datetime::msecs_since(me->last_pos_fire_time);
if (msecs < me->pos_fire_period_ms) {
return;
}
me->last_pos_fire_time = bts::datetime::now();
// make some thing up and publish
if (me->exchange_position.total_exchange_positions.empty()) {
me->exchange_position.exchange = bts::api::VENUE_DERIBIT;
ExchangePosition::UnitPosition u = {};
u.security = nullptr;
u.position = 1.0;
u.avg_filled_price = Price(20.0);
u.exchange_sec_desc = "xyz";
for (int i = 0; i < 10; i++) {
me->exchange_position.total_exchange_positions.push_back(u);
}
}
me->on_exchange_position_state(me->exchange_position);
}
// void EMExecutionGateway::_test_pos_cb() {
// if (me->order_mux == nullptr)
// return;
// auto msecs = bts::datetime::msecs_since(me->last_pos_fire_time);
// if (msecs < me->pos_fire_period_ms) {
// return;
// }
// me->last_pos_fire_time = bts::datetime::now();
// // make some thing up and publish
// if (me->exchange_position.total_exchange_positions.empty()) {
// me->exchange_position.exchange = bts::api::VENUE_DERIBIT;
// ExchangePosition::UnitPosition u = {};
// u.security = nullptr;
// u.position = 1.0;
// u.avg_filled_price = Price(20.0);
// u.exchange_sec_desc = "xyz";
// for (int i = 0; i < 10; i++) {
// me->exchange_position.total_exchange_positions.push_back(u);
// }
// }
// me->on_exchange_position_state(me->exchange_position);
// }
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
+59 -59
View File
@@ -1,72 +1,72 @@
#pragma once
// #pragma once
#include "IExecutionGateway.h"
#include "lib/orderman/CancelOrderMsg.h"
#include "lib/orderman/CancelReplaceOrderMsg.h"
#include "lib/orderman/CreateOrderMsg.h"
#include <bits/stdint-uintn.h>
#include <cstdint>
// #include "IExecutionGateway.h"
// #include "lib/orderman/CancelOrderMsg.h"
// #include "lib/orderman/CancelReplaceOrderMsg.h"
// #include "lib/orderman/CreateOrderMsg.h"
// #include <bits/stdint-uintn.h>
// #include <cstdint>
// EMExecutionGateway is created by the ExecutionGatewayFactory. It is contained
// in SessionServicesData.
// // EMExecutionGateway is created by the ExecutionGatewayFactory. It is contained
// // in SessionServicesData.
// Order place/cxl/cxl-repls are cummunicated via send_msgs, by OrderManager
// (OrderManager::after_event). EMExecutionGateway holds a pointer to
// IEMOrderMuxer through which all orders are sent out and callbacks reveived to
// the and from the outside world.
// // Order place/cxl/cxl-repls are cummunicated via send_msgs, by OrderManager
// // (OrderManager::after_event). EMExecutionGateway holds a pointer to
// // IEMOrderMuxer through which all orders are sent out and callbacks reveived to
// // the and from the outside world.
// Callbacks are send back to the OrderManager via the 4 callback functions. The
// path that ExchangePosition takes to the OrderService is as follows:
// IEMOrderMuxer -> EMExecutionGateway (in IEMOrderMuxer's thread)
// EMExecutionGateway -> OrderManager (via on_exchange_position_state)
// OrderManager -> ServiceBridge (via Stream<ExchangePositionEvent>)
// ServiceBridge -> OrderServiceServer (via callback passed in ServiceBridge's
// constructor)
// // Callbacks are send back to the OrderManager via the 4 callback functions. The
// // path that ExchangePosition takes to the OrderService is as follows:
// // IEMOrderMuxer -> EMExecutionGateway (in IEMOrderMuxer's thread)
// // EMExecutionGateway -> OrderManager (via on_exchange_position_state)
// // OrderManager -> ServiceBridge (via Stream<ExchangePositionEvent>)
// // ServiceBridge -> OrderServiceServer (via callback passed in ServiceBridge's
// // constructor)
namespace emumba::deribit_connector::data_types {
class error;
}
// namespace emumba::deribit_connector::data_types {
// class error;
// }
namespace emumba::order_entry {
struct order_state;
}
// namespace emumba::order_entry {
// struct order_state;
// }
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
struct EMCallbackMsg;
// Wrapper around EM's order gateway. There are fix overtones to this, that are
// ignored
class EMExecutionGateway : public IExecutionGateway {
public:
EMExecutionGateway(md::SecurityOracle);
virtual ~EMExecutionGateway();
// struct EMCallbackMsg;
// // Wrapper around EM's order gateway. There are fix overtones to this, that are
// // ignored
// class EMExecutionGateway : public IExecutionGateway {
// public:
// EMExecutionGateway(md::SecurityOracle);
// virtual ~EMExecutionGateway();
void start() override;
void dispatch_incoming_fix_messages() override;
void on_securities(const mercury::Update<NewSecuritiesEvent> &) override;
void on_admin_data(const mercury::Update<NewAdminDataEvent> &) override;
void send_fix(const jetfix::OutFIXPtr &fix, trace::CorID trace) override;
void send_msgs(std::vector<OrderMsg> const &, trace::CorID) override;
// void start() override;
// void dispatch_incoming_fix_messages() override;
// void on_securities(const mercury::Update<NewSecuritiesEvent> &) override;
// void on_admin_data(const mercury::Update<NewAdminDataEvent> &) override;
// void send_fix(const jetfix::OutFIXPtr &fix, trace::CorID trace) override;
// void send_msgs(std::vector<OrderMsg> const &, trace::CorID) override;
OnExeReport &on_exe_report() override;
OnGwStatusChange &on_gw_status_change() override;
OnCancelReject &on_cancel_reject() override;
OnExchangePositionState &on_exchange_position_state() override;
// OnExeReport &on_exe_report() override;
// OnGwStatusChange &on_gw_status_change() override;
// OnCancelReject &on_cancel_reject() override;
// OnExchangePositionState &on_exchange_position_state() override;
private:
struct PImpl;
std::unique_ptr<PImpl> me;
// private:
// struct PImpl;
// std::unique_ptr<PImpl> me;
private:
bool _init();
void _send_create(const CreateOrderMsg &);
void _send_cxl_replace(const CancelReplaceOrderMsg &);
void _send_cxl(const CancelOrderMsg &);
void _process_cb_and_fire_er(const EMCallbackMsg &);
// temp
void _test_pos_cb();
};
// private:
// bool _init();
// void _send_create(const CreateOrderMsg &);
// void _send_cxl_replace(const CancelReplaceOrderMsg &);
// void _send_cxl(const CancelOrderMsg &);
// void _process_cb_and_fire_er(const EMCallbackMsg &);
// // temp
// void _test_pos_cb();
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -21,9 +21,9 @@ std::unique_ptr<IExecutionGateway> ExecutionGatewayFactory::create_gateway(
else if (conn.use_ilink3())
return std::make_unique<ilink3::GLBXiLink3ExecutionGateway>(conn, pl, o);
else
return std::make_unique<EMExecutionGateway>(o);
// return std::make_unique<FakeExecutionGateway>(o);
// return std::make_unique<GLBXExecutionGateway>(conn, pl, o);
// return std::make_unique<EMExecutionGateway>(o);
// return std::make_unique<FakeExecutionGateway>(o);
return std::make_unique<GLBXExecutionGateway>(conn, pl, o);
}
} // namespace bts::orderman
+183 -180
View File
@@ -1,194 +1,197 @@
#include "FakeExecutionGateway.h"
#include "lib/api/services/AdminService.pb.h"
#include "lib/api/services/OrderService.pb.h"
#include "lib/domain_model/Security.pb.h"
#include "lib/domain_model/common.h"
#include "lib/logging/logging.h"
#include "lib/orderman/CancelOrderMsg.h"
#include "lib/orderman/CancelReplaceOrderMsg.h"
#include "lib/orderman/CreateOrderMsg.h"
#include "lib/orderman/OrderMsg.h"
#include "lib/orderman/events/CancelRejectEvent.h"
#include "lib/orderman/events/GatewayStatusChangeEvent.h"
#include "lib/orderman/events/NewAdminDataEvent.h"
#include "lib/orderman/events/NewExecutionReportEvent.h"
#include "lib/orderman/events/NewSecuritiesEvent.h"
#include "lib/orderman/orderman_utils/ExecutionReport.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/tracing/bts_trace.h"
#include "lib/utils/Dispatcher.h"
#include "lib/utils/TimeStamp.h"
#include "lib/utils/bts_assert.h"
#include "lib/utils/bts_chrono.h"
#include "lib/utils/helper_macros.h"
#include "server/sparkd/md/platforms/emumba/IEMOrderMuxer.h"
// #include "FakeExecutionGateway.h"
// #include "lib/api/services/AdminService.pb.h"
// #include "lib/api/services/OrderService.pb.h"
// #include "lib/domain_model/Security.pb.h"
// #include "lib/domain_model/common.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/CancelOrderMsg.h"
// #include "lib/orderman/CancelReplaceOrderMsg.h"
// #include "lib/orderman/CreateOrderMsg.h"
// #include "lib/orderman/OrderMsg.h"
// #include "lib/orderman/events/CancelRejectEvent.h"
// #include "lib/orderman/events/GatewayStatusChangeEvent.h"
// #include "lib/orderman/events/NewAdminDataEvent.h"
// #include "lib/orderman/events/NewExecutionReportEvent.h"
// #include "lib/orderman/events/NewSecuritiesEvent.h"
// #include "lib/orderman/orderman_utils/ExecutionReport.h"
// #include "lib/orderman/orderman_utils/orderman_common.h"
// #include "lib/tracing/bts_trace.h"
// #include "lib/utils/Dispatcher.h"
// #include "lib/utils/TimeStamp.h"
// #include "lib/utils/bts_assert.h"
// #include "lib/utils/bts_chrono.h"
// #include "lib/utils/helper_macros.h"
// #include "server/sparkd/md/platforms/emumba/IEMOrderMuxer.h"
#include <atomic>
#include <boost/pool/object_pool.hpp>
#include <cstdint>
#include <set>
#include <string>
#include <unordered_map>
#include <variant>
#include <vector>
// #include <atomic>
// #include <boost/pool/object_pool.hpp>
// #include <cstdint>
// #include <set>
// #include <string>
// #include <unordered_map>
// #include <variant>
// #include <vector>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
struct FakeExecutionGateway::PImpl {
PImpl(md::SecurityOracle o)
: admin_data(), connection_time(), sec_oracle(o) {
LOG(OrderMan, Info, "FakeExecutionGateway::PImpl constructor");
}
// struct FakeExecutionGateway::PImpl {
// PImpl(md::SecurityOracle o)
// : admin_data(), connection_time(), sec_oracle(o) {
// LOG(OrderMan, Info, "FakeExecutionGateway::PImpl constructor");
// }
std::unordered_map<SecurityKey::Value, api::Security const *> securities;
std::unordered_map<int64_t, api::Security const *> securities_by_exchange_id;
OnExeReport on_exe_report;
OnGwStatusChange on_gw_status_change;
OnCancelReject on_cancel_reject;
orderman::OnExchangePositionState on_exchange_position_state;
api::admin::AdminData admin_data;
TimeStamp connection_time;
md::SecurityOracle sec_oracle;
std::vector<OrderMsg> buffered_msgs = {};
unsigned long exchange_oid = 0;
std::vector<ExecutionReport> exe_reports = {};
IEMOrderMuxer *_order_mux = nullptr;
};
// std::unordered_map<SecurityKey::Value, api::Security const *> securities;
// std::unordered_map<int64_t, api::Security const *>
// securities_by_exchange_id; OnExeReport on_exe_report; OnGwStatusChange
// on_gw_status_change; OnCancelReject on_cancel_reject;
// orderman::OnExchangePositionState on_exchange_position_state;
// api::admin::AdminData admin_data;
// TimeStamp connection_time;
// md::SecurityOracle sec_oracle;
// std::vector<OrderMsg> buffered_msgs = {};
// unsigned long exchange_oid = 0;
// std::vector<ExecutionReport> exe_reports = {};
// IEMOrderMuxer *_order_mux = nullptr;
// };
FakeExecutionGateway::FakeExecutionGateway(md::SecurityOracle o)
: IExecutionGateway(), me(new PImpl(o)) {
LOG(OrderMan, Info,
"FakeExecutionGateway::FakeExecutionGateway constructor");
}
// FakeExecutionGateway::FakeExecutionGateway(md::SecurityOracle o)
// : IExecutionGateway(), me(new PImpl(o)) {
// LOG(OrderMan, Info,
// "FakeExecutionGateway::FakeExecutionGateway constructor");
// }
FakeExecutionGateway::~FakeExecutionGateway() {}
// FakeExecutionGateway::~FakeExecutionGateway() {}
void FakeExecutionGateway::start() {
LOG(OrderMan, Info, "FakeExecutionGateway::start");
}
// void FakeExecutionGateway::start() {
// LOG(OrderMan, Info, "FakeExecutionGateway::start");
// }
// this is a periodic timer
void FakeExecutionGateway::dispatch_incoming_fix_messages() {
if (me->buffered_msgs.size() == 0) {
return;
}
std::vector<OrderMsg> aux_msgs = me->buffered_msgs;
me->buffered_msgs.clear();
for (OrderMsg m : aux_msgs) {
if (std::holds_alternative<CreateOrderMsg>(m)) {
CreateOrderMsg cr_msg = std::get<CreateOrderMsg>(m);
LOG(OrderMan, Info,
"FakeExecutionGateway::dispatch_incoming_fix_message key: {} sec: "
"{} p: {} q: {}",
cr_msg.key, cr_msg.security->id(), cr_msg.price, cr_msg.quantity);
ExecutionReport er = {};
std::string account_name = "ACCA1";
std::array<char, 12> an_array = {};
size_t i = 0;
for (const char c : account_name) {
an_array[i++] = c;
}
er.account_name = AccountName(an_array);
to_array(cr_msg.routing_record->account(), &er.account_name.value());
to_array(
cr_msg.routing_record->operator_id(), &er.operator_id.value());
er.order_key = cr_msg.key;
er.msg_seq_num = 0;
er.security = cr_msg.security;
er.leg_security = nullptr;
er.side = cr_msg.side;
er.time_in_force = cr_msg.time_in_force;
er.order_type = cr_msg.order_type;
er.exec_type = api::orderman::EXEC_TYPE_NEW;
er.order_status = api::orderman::NATIVE_ORDER_STATUS_NEW;
er.manual_order_indicator = api::orderman::MANUAL_ORDER_INDICATOR_NO;
ExchangeOrderID64 eoid = ExchangeOrderID64(me->exchange_oid++);
ExchangeOrderIDVar oid = eoid;
er.exchange_order_id = oid;
er.price = cr_msg.price;
er.quantity = cr_msg.quantity;
er.filled_quantity = Quantity(0);
er.last_shares = Quantity(0);
er.msg = "Message Placed";
er.exchange = api::VENUE_SIM;
trace::CorID cid = {};
me->exe_reports.push_back(er);
const auto &er_pheap = me->exe_reports.back();
LOG(OrderMan, Info,
"FakeExecutionGateway::dispatch_incoming_fix_messages er: {}",
er_pheap.to_string());
me->on_exe_report(er_pheap, cid);
} else if (std::holds_alternative<CancelOrderMsg>(m)) {
CancelOrderMsg cxl_m = std::get<CancelOrderMsg>(m);
for (auto &er : me->exe_reports) {
if (er.order_key == cxl_m.key) {
er.order_status = api::orderman::NATIVE_ORDER_STATUS_CANCELED;
er.msg = "Cancelled";
me->on_exe_report(er, {});
LOG(OrderMan, Info,
"FakeExecutionGateway::dispatch_incoming_fix_messages er: "
"{}",
er.to_string());
return;
}
}
} else if (std::holds_alternative<CancelReplaceOrderMsg>(m)) {
CancelReplaceOrderMsg cxl_r_msg = std::get<CancelReplaceOrderMsg>(m);
CreateOrderMsg cr_msg = cxl_r_msg.create_fields;
for (auto &er : me->exe_reports) {
if (er.order_key == cr_msg.key) {
er.order_status = api::orderman::NATIVE_ORDER_STATUS_REPLACED;
er.msg = "Replaced";
er.price = cr_msg.price;
er.quantity = cr_msg.quantity;
me->on_exe_report(er, {});
return;
}
}
}
}
}
// // this is a periodic timer
// void FakeExecutionGateway::dispatch_incoming_fix_messages() {
// if (me->buffered_msgs.size() == 0) {
// return;
// }
// std::vector<OrderMsg> aux_msgs = me->buffered_msgs;
// me->buffered_msgs.clear();
// for (OrderMsg m : aux_msgs) {
// if (std::holds_alternative<CreateOrderMsg>(m)) {
// CreateOrderMsg cr_msg = std::get<CreateOrderMsg>(m);
// LOG(OrderMan, Info,
// "FakeExecutionGateway::dispatch_incoming_fix_message key: {}
// sec: "
// "{} p: {} q: {}",
// cr_msg.key, cr_msg.security->id(), cr_msg.price,
// cr_msg.quantity);
// ExecutionReport er = {};
// std::string account_name = "ACCA1";
// std::array<char, 12> an_array = {};
// size_t i = 0;
// for (const char c : account_name) {
// an_array[i++] = c;
// }
// er.account_name = AccountName(an_array);
// to_array(cr_msg.routing_record->account(),
// &er.account_name.value()); to_array(
// cr_msg.routing_record->operator_id(), &er.operator_id.value());
// er.order_key = cr_msg.key;
// er.msg_seq_num = 0;
// er.security = cr_msg.security;
// er.leg_security = nullptr;
// er.side = cr_msg.side;
// er.time_in_force = cr_msg.time_in_force;
// er.order_type = cr_msg.order_type;
// er.exec_type = api::orderman::EXEC_TYPE_NEW;
// er.order_status = api::orderman::NATIVE_ORDER_STATUS_NEW;
// er.manual_order_indicator =
// api::orderman::MANUAL_ORDER_INDICATOR_NO; ExchangeOrderID64 eoid =
// ExchangeOrderID64(me->exchange_oid++); ExchangeOrderIDVar oid =
// eoid; er.exchange_order_id = oid; er.price = cr_msg.price;
// er.quantity = cr_msg.quantity;
// er.filled_quantity = Quantity(0);
// er.last_shares = Quantity(0);
// er.msg = "Message Placed";
// er.exchange = api::VENUE_SIM;
// trace::CorID cid = {};
// me->exe_reports.push_back(er);
// const auto &er_pheap = me->exe_reports.back();
// LOG(OrderMan, Info,
// "FakeExecutionGateway::dispatch_incoming_fix_messages er: {}",
// er_pheap.to_string());
// me->on_exe_report(er_pheap, cid);
// } else if (std::holds_alternative<CancelOrderMsg>(m)) {
// CancelOrderMsg cxl_m = std::get<CancelOrderMsg>(m);
// for (auto &er : me->exe_reports) {
// if (er.order_key == cxl_m.key) {
// er.order_status = api::orderman::NATIVE_ORDER_STATUS_CANCELED;
// er.msg = "Cancelled";
// me->on_exe_report(er, {});
// LOG(OrderMan, Info,
// "FakeExecutionGateway::dispatch_incoming_fix_messages er:
// "
// "{}",
// er.to_string());
// return;
// }
// }
// } else if (std::holds_alternative<CancelReplaceOrderMsg>(m)) {
// CancelReplaceOrderMsg cxl_r_msg =
// std::get<CancelReplaceOrderMsg>(m); CreateOrderMsg cr_msg =
// cxl_r_msg.create_fields; for (auto &er : me->exe_reports) {
// if (er.order_key == cr_msg.key) {
// er.order_status = api::orderman::NATIVE_ORDER_STATUS_REPLACED;
// er.msg = "Replaced";
// er.price = cr_msg.price;
// er.quantity = cr_msg.quantity;
// me->on_exe_report(er, {});
// return;
// }
// }
// }
// }
// }
void FakeExecutionGateway::on_securities(
const mercury::Update<NewSecuritiesEvent> &) {
LOG(OrderMan, Info, "FakeExecutionGateway::on_securities");
}
// void FakeExecutionGateway::on_securities(
// const mercury::Update<NewSecuritiesEvent> &) {
// LOG(OrderMan, Info, "FakeExecutionGateway::on_securities");
// }
void FakeExecutionGateway::on_admin_data(
const mercury::Update<NewAdminDataEvent> &update) {
LOG(OrderMan, Info, "FakeExecutionGateway::on_admin_data");
if (update.events) {
for (auto const &ev : *(update.events))
me->admin_data.CopyFrom(*ev.admin_data);
}
}
// void FakeExecutionGateway::on_admin_data(
// const mercury::Update<NewAdminDataEvent> &update) {
// LOG(OrderMan, Info, "FakeExecutionGateway::on_admin_data");
// if (update.events) {
// for (auto const &ev : *(update.events))
// me->admin_data.CopyFrom(*ev.admin_data);
// }
// }
void FakeExecutionGateway::send_fix(const jetfix::OutFIXPtr &, trace::CorID) {
// only deals with messages
bts_assert(false);
}
// void FakeExecutionGateway::send_fix(const jetfix::OutFIXPtr &, trace::CorID)
// {
// // only deals with messages
// bts_assert(false);
// }
void FakeExecutionGateway::send_msgs(
std::vector<OrderMsg> const &msg_vec, trace::CorID) {
// this is where it will be sent to the exchange
LOG(OrderMan, Info, "FakeExecutionGateway::send_msgs count: {}",
msg_vec.size());
for (auto &m : msg_vec) {
me->buffered_msgs.emplace_back(m);
}
}
// void FakeExecutionGateway::send_msgs(
// std::vector<OrderMsg> const &msg_vec, trace::CorID) {
// // this is where it will be sent to the exchange
// LOG(OrderMan, Info, "FakeExecutionGateway::send_msgs count: {}",
// msg_vec.size());
// for (auto &m : msg_vec) {
// me->buffered_msgs.emplace_back(m);
// }
// }
OnExeReport &FakeExecutionGateway::on_exe_report() { return me->on_exe_report; }
OnGwStatusChange &FakeExecutionGateway::on_gw_status_change() {
return me->on_gw_status_change;
}
OnCancelReject &FakeExecutionGateway::on_cancel_reject() {
return me->on_cancel_reject;
}
OnExchangePositionState &FakeExecutionGateway::on_exchange_position_state() {
return me->on_exchange_position_state;
}
// OnExeReport &FakeExecutionGateway::on_exe_report() { return
// me->on_exe_report; } OnGwStatusChange
// &FakeExecutionGateway::on_gw_status_change() {
// return me->on_gw_status_change;
// }
// OnCancelReject &FakeExecutionGateway::on_cancel_reject() {
// return me->on_cancel_reject;
// }
// OnExchangePositionState &FakeExecutionGateway::on_exchange_position_state() {
// return me->on_exchange_position_state;
// }
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,33 +1,34 @@
#pragma once
// #pragma once
#include "IExecutionGateway.h"
// #include "IExecutionGateway.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
// Wrapper around EM's order gateway. There are fix overtones to this, that are
// ignored
class FakeExecutionGateway : public IExecutionGateway {
public:
FakeExecutionGateway(md::SecurityOracle);
virtual ~FakeExecutionGateway();
// // Wrapper around EM's order gateway. There are fix overtones to this, that
// are
// // ignored
// class FakeExecutionGateway : public IExecutionGateway {
// public:
// FakeExecutionGateway(md::SecurityOracle);
// virtual ~FakeExecutionGateway();
void start() override;
void dispatch_incoming_fix_messages() override;
void on_securities(const mercury::Update<NewSecuritiesEvent> &) override;
void on_admin_data(const mercury::Update<NewAdminDataEvent> &) override;
void send_fix(const jetfix::OutFIXPtr &fix, trace::CorID trace) override;
void send_msgs(std::vector<OrderMsg> const &, trace::CorID) override;
// void start() override;
// void dispatch_incoming_fix_messages() override;
// void on_securities(const mercury::Update<NewSecuritiesEvent> &) override;
// void on_admin_data(const mercury::Update<NewAdminDataEvent> &) override;
// void send_fix(const jetfix::OutFIXPtr &fix, trace::CorID trace) override;
// void send_msgs(std::vector<OrderMsg> const &, trace::CorID) override;
OnExeReport &on_exe_report() override;
OnGwStatusChange &on_gw_status_change() override;
OnCancelReject &on_cancel_reject() override;
OnExchangePositionState &on_exchange_position_state() override;
// OnExeReport &on_exe_report() override;
// OnGwStatusChange &on_gw_status_change() override;
// OnCancelReject &on_cancel_reject() override;
// OnExchangePositionState &on_exchange_position_state() override;
private:
struct PImpl;
std::unique_ptr<PImpl> me;
};
// private:
// struct PImpl;
// std::unique_ptr<PImpl> me;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -30,7 +30,6 @@
#include "lib/orderman/orders/algo/algo_orders/TopAlgo.h"
#include "lib/orderman/orders/book_algos/OCOOrder.h"
#include "lib/orderman/orders/book_algos/PricerOrder.h"
#include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
#include "lib/orderman/orders/spreader_orders/SpreaderOrder.h"
#include <limits>
@@ -185,7 +184,8 @@ Order &OrderFactory::allocate(
new orderman::AlgoWrapperOrder(std::make_unique<IcebergAlgo>()));
break;
case CustomOrderDefinition::kSpreader:
order_ptr.reset(new orderman::SpreaderOrder());
bts_assert(false);
// order_ptr.reset(new orderman::SpreaderOrder());
break;
case CustomOrderDefinition::kTimeBox:
order_ptr.reset(new orderman::TimeBoxOrder());
@@ -195,29 +195,23 @@ Order &OrderFactory::allocate(
break;
case CustomOrderDefinition::kLean:
order_ptr.reset(
new orderman::AlgoWrapperOrder(std::make_unique<LeanAlgo>()));
new orderman::AlgoWrapperOrder(std::make_unique<IcebergAlgo>()));
break;
case CustomOrderDefinition::kJoin:
order_ptr.reset(
new orderman::AlgoWrapperOrder(std::make_unique<JoinAlgo>()));
new orderman::AlgoWrapperOrder(std::make_unique<IcebergAlgo>()));
break;
case CustomOrderDefinition::kTop:
order_ptr.reset(
new orderman::AlgoWrapperOrder(std::make_unique<TopAlgo>()));
new orderman::AlgoWrapperOrder(std::make_unique<IcebergAlgo>()));
break;
case CustomOrderDefinition::kTrigger:
order_ptr.reset(new orderman::TriggerOrder());
break;
case CustomOrderDefinition::kCover:
order_ptr.reset(
new orderman::AlgoWrapperOrder(std::make_unique<CoverAlgo>()));
break;
case CustomOrderDefinition::kOffset:
order_ptr.reset(
new orderman::AlgoWrapperOrder(std::make_unique<OffsetAlgo>()));
break;
case CustomOrderDefinition::kQueueHolder:
order_ptr.reset(new orderman::QueueHolderOrder());
break;
case CustomOrderDefinition::kOco:
order_ptr.reset(new orderman::OCOOrder());
@@ -227,11 +221,10 @@ Order &OrderFactory::allocate(
break;
case CustomOrderDefinition::kScratch:
order_ptr.reset(
new orderman::AlgoWrapperOrder(std::make_unique<ScratchAlgo>()));
new orderman::AlgoWrapperOrder(std::make_unique<IcebergAlgo>()));
break;
case CustomOrderDefinition::kProrata:
order_ptr.reset(
new orderman::AlgoWrapperOrder(std::make_unique<ProRataAlgo>()));
order_ptr.reset(new orderman::PricerOrder());
break;
case CustomOrderDefinition::kDynamic: {
order_ptr = me->dynamic_order_registry.build(
File diff suppressed because it is too large. Load diff
+117 -117
View File
@@ -1,126 +1,126 @@
#pragma once
// #pragma once
#include "lib/api/services/OrderService.server.pb.h"
#include "lib/orderman/orders/Order.h"
#include <memory>
// #include "lib/api/services/OrderService.server.pb.h"
// #include "lib/orderman/orders/Order.h"
// #include <memory>
namespace bts {
namespace api {
namespace orderman {
class OrderDefinition;
}
} // namespace api
namespace mercury {
class TimerEvent;
}
namespace md {
class PriceSize;
}
namespace orderman {
// namespace bts {
// namespace api {
// namespace orderman {
// class OrderDefinition;
// }
// } // namespace api
// namespace mercury {
// class TimerEvent;
// }
// namespace md {
// class PriceSize;
// }
// namespace orderman {
class OrderStateUpdateEvent;
class BookUpdateEvent;
class PreActivationOrderServices;
// class OrderStateUpdateEvent;
// class BookUpdateEvent;
// class PreActivationOrderServices;
class TriggerOrder : public Order {
private:
enum State {
// remains here till on_actiavte is called
START = 0,
// waiting for trigger conditions, sends
// order and waits to steady
WAITING_FOR_TRIGGER = 1,
// once the order is submitted it gets into
// this state until getting a callback
// from the child order
WAITING_FOR_FIRST_ACK = 2,
// order is out
STEADY = 3,
END = 4
};
// this is the ways a child order
// can be cancelled
enum CancelMode {
// not cancelled, initial value
none = 0,
// meaning the trigger order
// cancelled the child order because
// of time or conditional reasons
trigger,
// the trigger order is itself
// externally cancelled
// through the cancel call
external
};
// class TriggerOrder : public Order {
// private:
// enum State {
// // remains here till on_actiavte is called
// START = 0,
// // waiting for trigger conditions, sends
// // order and waits to steady
// WAITING_FOR_TRIGGER = 1,
// // once the order is submitted it gets into
// // this state until getting a callback
// // from the child order
// WAITING_FOR_FIRST_ACK = 2,
// // order is out
// STEADY = 3,
// END = 4
// };
// // this is the ways a child order
// // can be cancelled
// enum CancelMode {
// // not cancelled, initial value
// none = 0,
// // meaning the trigger order
// // cancelled the child order because
// // of time or conditional reasons
// trigger,
// // the trigger order is itself
// // externally cancelled
// // through the cancel call
// external
// };
public:
TriggerOrder();
~TriggerOrder();
api::orderman::OrderDefinition const &definition() const;
void on_init_new(
api::orderman::OrderDefinition const &,
const PreActivationOrderServices &) override;
void on_init_recovered(
api::orderman::OrderRecord const &,
const PreActivationOrderServices &) override;
OrderOpResult on_activate(Price, Quantity, OrderServices &) override;
OrderOpResult on_update(Price, Quantity) override;
OrderOpResult on_cancel() override;
// public:
// TriggerOrder();
// ~TriggerOrder();
// api::orderman::OrderDefinition const &definition() const;
// void on_init_new(
// api::orderman::OrderDefinition const &,
// const PreActivationOrderServices &) override;
// void on_init_recovered(
// api::orderman::OrderRecord const &,
// const PreActivationOrderServices &) override;
// OrderOpResult on_activate(Price, Quantity, OrderServices &) override;
// OrderOpResult on_update(Price, Quantity) override;
// OrderOpResult on_cancel() override;
private:
// callbacks from book and child order udpates
void _on_book_update(const BookUpdateEvent &);
void _on_child_order_update(OrderStateUpdateEvent const &);
void _on_time(const bts::mercury::TimerEvent &);
// private:
// // callbacks from book and child order udpates
// void _on_book_update(const BookUpdateEvent &);
// void _on_child_order_update(OrderStateUpdateEvent const &);
// void _on_time(const bts::mercury::TimerEvent &);
private:
// called from init_new
bool _on_init_new(
const api::orderman::OrderDefinition &order_def,
const PreActivationOrderServices &pre_os);
void _report_state();
void _switch_state(State new_state);
// this is called in the WAITING_FOR_TRIGGER state,
// to check for the trigger condition and shoot
// an order if the condition is met
void _analyze_trigger_and_shoot();
// this is called in the STEADY state when the
// child order is working. (Based on the
// parameters) if timed-out or the trigger
// condition being false, the
// child order is cancelled
void _analyze_trigger_and_cancel();
api::orderman::OrderStatus _status() const;
void _compute_state();
// for non trailing trigger prices, we need a
// notion if initially the trigger price
// was over the observed market or not
void _observe_market_once();
// based on the trigger market to watch returns
// a pointer to the market (bid, ask, ltp). If not
// present returns a nullptr
const md::PriceSize *_observed_market() const;
// this is the price of the parent order
// (the trigger price)
double _trigger_price() const;
// this is in the trigger action
double _child_order_price() const;
void _cancel_order(CancelMode cancel_mode);
bool _activated() const;
// returns true is the trigger condition is met
static bool _trigger_met(
const md::PriceSize *obs_md,
double trigger_price,
double tick_size,
bool trigger_above_md);
// returns true is p1 >= p2
static bool _gr_than_eq(double p1, double p2, double tick_size);
// private:
// // called from init_new
// bool _on_init_new(
// const api::orderman::OrderDefinition &order_def,
// const PreActivationOrderServices &pre_os);
// void _report_state();
// void _switch_state(State new_state);
// // this is called in the WAITING_FOR_TRIGGER state,
// // to check for the trigger condition and shoot
// // an order if the condition is met
// void _analyze_trigger_and_shoot();
// // this is called in the STEADY state when the
// // child order is working. (Based on the
// // parameters) if timed-out or the trigger
// // condition being false, the
// // child order is cancelled
// void _analyze_trigger_and_cancel();
// api::orderman::OrderStatus _status() const;
// void _compute_state();
// // for non trailing trigger prices, we need a
// // notion if initially the trigger price
// // was over the observed market or not
// void _observe_market_once();
// // based on the trigger market to watch returns
// // a pointer to the market (bid, ask, ltp). If not
// // present returns a nullptr
// const md::PriceSize *_observed_market() const;
// // this is the price of the parent order
// // (the trigger price)
// double _trigger_price() const;
// // this is in the trigger action
// double _child_order_price() const;
// void _cancel_order(CancelMode cancel_mode);
// bool _activated() const;
// // returns true is the trigger condition is met
// static bool _trigger_met(
// const md::PriceSize *obs_md,
// double trigger_price,
// double tick_size,
// bool trigger_above_md);
// // returns true is p1 >= p2
// static bool _gr_than_eq(double p1, double p2, double tick_size);
private:
State _state;
struct PImpl;
std::unique_ptr<PImpl> me;
};
// private:
// State _state;
// struct PImpl;
// std::unique_ptr<PImpl> me;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,203 +1,203 @@
#include "lib/orderman/orders/algo/algo_orders/JoinAlgo.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
// #include "lib/orderman/orders/algo/algo_orders/JoinAlgo.h"
// #include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
// #include "lib/orderman/order_factory/IOrderFactory.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using namespace api::orderman;
// using namespace api::orderman;
enum class JoinAlgo::Status { WAITING_FOR_MD, WAITING_TO_JOIN, JOINED };
// enum class JoinAlgo::Status { WAITING_FOR_MD, WAITING_TO_JOIN, JOINED };
struct JoinAlgo::PImpl {
IAlgoServices *algo;
Quantity min_quantity_to_join;
Status status = Status::WAITING_FOR_MD;
OrderKey child_key = null_order_key();
ChildTemplate *child_template = nullptr;
Book book_at_inception;
Book last_book;
Price joined_price = Price(os::nan());
};
// struct JoinAlgo::PImpl {
// IAlgoServices *algo;
// Quantity min_quantity_to_join;
// Status status = Status::WAITING_FOR_MD;
// OrderKey child_key = null_order_key();
// ChildTemplate *child_template = nullptr;
// Book book_at_inception;
// Book last_book;
// Price joined_price = Price(os::nan());
// };
JoinAlgo::JoinAlgo() : me(new PImpl()) {}
JoinAlgo::~JoinAlgo() {}
IAlgoServices &JoinAlgo::algo() const { return *me->algo; }
// JoinAlgo::JoinAlgo() : me(new PImpl()) {}
// JoinAlgo::~JoinAlgo() {}
// IAlgoServices &JoinAlgo::algo() const { return *me->algo; }
OrderOpResult JoinAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
auto &def = serv.definition();
bts_assert(def.has_custom_def());
bts_assert(def.custom_def().has_join());
// OrderOpResult JoinAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
// auto &def = serv.definition();
// bts_assert(def.has_custom_def());
// bts_assert(def.custom_def().has_join());
// Validate the join part of the definition
auto &join = def.custom_def().join();
if (!join.has_child() &&
OrderKey(def.attach_target_order_key()) == null_order_key()) {
return OrderOpResult{
ORDER_ERROR_INVALID_ORDER_DEFINITON,
"Join order must have either a child order definition or an "
"order to attach to"};
}
// // Validate the join part of the definition
// auto &join = def.custom_def().join();
// if (!join.has_child() &&
// OrderKey(def.attach_target_order_key()) == null_order_key()) {
// return OrderOpResult{
// ORDER_ERROR_INVALID_ORDER_DEFINITON,
// "Join order must have either a child order definition or an "
// "order to attach to"};
// }
if (!serv.is_attaching()) {
// if we are not attaching create a child template so we can
// quickly launch our child when the time comes.
auto child_def = def;
*child_def.mutable_custom_def() = def.custom_def().join().child();
me->child_template = &(serv.make_child_template(child_def));
}
// if (!serv.is_attaching()) {
// // if we are not attaching create a child template so we can
// // quickly launch our child when the time comes.
// auto child_def = def;
// *child_def.mutable_custom_def() = def.custom_def().join().child();
// me->child_template = &(serv.make_child_template(child_def));
// }
return {};
}
// return {};
// }
void JoinAlgo::do_launch(IAlgoServices &algo) {
me->algo = &algo;
update_join_threshold();
bts_assert(me->child_template);
me->child_key =
algo.launch_child(*me->child_template, algo.price(), algo.quantity());
algo.subscribe_book(SecurityKey(algo.security().id()));
}
// void JoinAlgo::do_launch(IAlgoServices &algo) {
// me->algo = &algo;
// update_join_threshold();
// bts_assert(me->child_template);
// me->child_key =
// algo.launch_child(*me->child_template, algo.price(), algo.quantity());
// algo.subscribe_book(SecurityKey(algo.security().id()));
// }
void JoinAlgo::do_launch_attached(
IAlgoServices &algo, OrderKey k, ChildState st) {
me->algo = &algo;
update_join_threshold();
me->child_key = k;
algo.subscribe_book(SecurityKey(algo.security().id()));
on_child_change(k, st);
}
// void JoinAlgo::do_launch_attached(
// IAlgoServices &algo, OrderKey k, ChildState st) {
// me->algo = &algo;
// update_join_threshold();
// me->child_key = k;
// algo.subscribe_book(SecurityKey(algo.security().id()));
// on_child_change(k, st);
// }
void JoinAlgo::do_update(api::orderman::Update const &update) {
LOG(OrderMan, Diag, "JoinOrder order {} receiving update: {}",
algo().order_key().value(), utils::pb::to_string(update));
// void JoinAlgo::do_update(api::orderman::Update const &update) {
// LOG(OrderMan, Diag, "JoinOrder order {} receiving update: {}",
// algo().order_key().value(), utils::pb::to_string(update));
if (update.has_custom() && !update.custom().has_join()) {
algo().terminate_error("Invalid update");
return;
}
// if (update.has_custom() && !update.custom().has_join()) {
// algo().terminate_error("Invalid update");
// return;
// }
Update child_update;
Price price_to_pass = get_price_to_pass(Price(update.price()));
child_update.set_price(price_to_pass.value());
child_update.set_quantity(update.quantity());
if (update.has_custom()) {
update_join_threshold();
auto &join_update = update.custom().join();
*child_update.mutable_custom() = join_update.child();
}
algo().update_child(me->child_key, child_update);
// Update child_update;
// Price price_to_pass = get_price_to_pass(Price(update.price()));
// child_update.set_price(price_to_pass.value());
// child_update.set_quantity(update.quantity());
// if (update.has_custom()) {
// update_join_threshold();
// auto &join_update = update.custom().join();
// *child_update.mutable_custom() = join_update.child();
// }
// algo().update_child(me->child_key, child_update);
// If we were not yet joined it is possible we now can, since we
// could have changed the min_quantity_to_join
if (me->status == Status::WAITING_TO_JOIN)
try_to_join();
}
// // If we were not yet joined it is possible we now can, since we
// // could have changed the min_quantity_to_join
// if (me->status == Status::WAITING_TO_JOIN)
// try_to_join();
// }
void JoinAlgo::on_book(SecurityKey, Book const &b) {
me->last_book = b;
// void JoinAlgo::on_book(SecurityKey, Book const &b) {
// me->last_book = b;
LOG(OrderMan, Diag, "JoinOrder order {} on book. BID: {}@{} ASK: {}@{}",
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
b.side(api::SIDE_SELL)[0].price());
// LOG(OrderMan, Diag, "JoinOrder order {} on book. BID: {}@{} ASK: {}@{}",
// algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
// b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
// b.side(api::SIDE_SELL)[0].price());
switch (me->status) {
case Status::WAITING_FOR_MD: {
auto other_side = securities::other_side(algo().definition().side());
auto &other_side_top = me->last_book.side(other_side)[0];
// switch (me->status) {
// case Status::WAITING_FOR_MD: {
// auto other_side = securities::other_side(algo().definition().side());
// auto &other_side_top = me->last_book.side(other_side)[0];
if (other_side_top.size() > 0) {
me->status = Status::WAITING_TO_JOIN;
me->book_at_inception = b;
LOG(OrderMan, Diag,
"JoinOrder order {} receiving first book: {} Target: {}",
algo().order_key().value(), me->min_quantity_to_join.value(),
other_side_top.price());
try_to_join();
}
} break;
case Status::JOINED:
break;
case Status::WAITING_TO_JOIN:
try_to_join();
break;
}
}
// if (other_side_top.size() > 0) {
// me->status = Status::WAITING_TO_JOIN;
// me->book_at_inception = b;
// LOG(OrderMan, Diag,
// "JoinOrder order {} receiving first book: {} Target: {}",
// algo().order_key().value(), me->min_quantity_to_join.value(),
// other_side_top.price());
// try_to_join();
// }
// } break;
// case Status::JOINED:
// break;
// case Status::WAITING_TO_JOIN:
// try_to_join();
// break;
// }
// }
void JoinAlgo::try_to_join() {
bts_assert(me->status == Status::WAITING_TO_JOIN);
// void JoinAlgo::try_to_join() {
// bts_assert(me->status == Status::WAITING_TO_JOIN);
auto my_side = algo().definition().side();
auto other_side = securities::other_side(my_side);
auto &my_side_psz = me->last_book.side(my_side)[0];
auto &opposite_at_inception_psz = me->book_at_inception.side(other_side)[0];
// auto my_side = algo().definition().side();
// auto other_side = securities::other_side(my_side);
// auto &my_side_psz = me->last_book.side(my_side)[0];
// auto &opposite_at_inception_psz = me->book_at_inception.side(other_side)[0];
if (my_side_psz.size() &&
my_side_psz.size() >= me->min_quantity_to_join.value() &&
securities::is_equal_or_better(
my_side, Price(my_side_psz.price()),
Price(opposite_at_inception_psz.price()))) {
// if (my_side_psz.size() &&
// my_side_psz.size() >= me->min_quantity_to_join.value() &&
// securities::is_equal_or_better(
// my_side, Price(my_side_psz.price()),
// Price(opposite_at_inception_psz.price()))) {
LOG(OrderMan, Diag, "JoinOrder order {} joining: {}@{}",
algo().order_key().value(), algo().quantity().value(),
my_side_psz.price());
// LOG(OrderMan, Diag, "JoinOrder order {} joining: {}@{}",
// algo().order_key().value(), algo().quantity().value(),
// my_side_psz.price());
// Time to join
Update child_update;
me->joined_price = Price(my_side_psz.price());
child_update.set_price(my_side_psz.price());
child_update.set_quantity(algo().quantity().value());
algo().update_child(me->child_key, child_update);
algo().set_ladder_visibility(false);
me->status = Status::JOINED;
} else {
LOG(OrderMan, Diag,
"JoinOrder order {} still waiting to join. Order: {}@{} Top of the "
"book: {}@{}"
" Target: {}@{}",
algo().order_key().value(), algo().quantity().value(),
algo().price().value(), my_side_psz.size(), my_side_psz.price(),
me->min_quantity_to_join.value(), opposite_at_inception_psz.price());
}
}
// // Time to join
// Update child_update;
// me->joined_price = Price(my_side_psz.price());
// child_update.set_price(my_side_psz.price());
// child_update.set_quantity(algo().quantity().value());
// algo().update_child(me->child_key, child_update);
// algo().set_ladder_visibility(false);
// me->status = Status::JOINED;
// } else {
// LOG(OrderMan, Diag,
// "JoinOrder order {} still waiting to join. Order: {}@{} Top of the "
// "book: {}@{}"
// " Target: {}@{}",
// algo().order_key().value(), algo().quantity().value(),
// algo().price().value(), my_side_psz.size(), my_side_psz.price(),
// me->min_quantity_to_join.value(), opposite_at_inception_psz.price());
// }
// }
Price JoinAlgo::get_price_to_pass(Price price) const {
switch (me->status) {
case Status::WAITING_FOR_MD:
case Status::WAITING_TO_JOIN:
return price;
case Status::JOINED:
return me->joined_price;
default:
return Price{os::nan()};
}
}
// Price JoinAlgo::get_price_to_pass(Price price) const {
// switch (me->status) {
// case Status::WAITING_FOR_MD:
// case Status::WAITING_TO_JOIN:
// return price;
// case Status::JOINED:
// return me->joined_price;
// default:
// return Price{os::nan()};
// }
// }
void JoinAlgo::update_join_threshold() {
me->min_quantity_to_join = SpreaderUtils::_quantity_threshold(
algo().definition().custom_def().join().join_threshold(),
algo().quantity());
}
// void JoinAlgo::update_join_threshold() {
// me->min_quantity_to_join = SpreaderUtils::_quantity_threshold(
// algo().definition().custom_def().join().join_threshold(),
// algo().quantity());
// }
void JoinAlgo::on_child_change(OrderKey, ChildState const &st) {
algo().update_fill_state(st.total_fills, st.last_price);
// void JoinAlgo::on_child_change(OrderKey, ChildState const &st) {
// algo().update_fill_state(st.total_fills, st.last_price);
switch (st.status) {
case ChildStatus::WORKING:
return;
case ChildStatus::COMPLETED:
algo().complete();
return;
case ChildStatus::CANCELED:
algo().cancel("Unexpected child cancel");
return;
}
}
// switch (st.status) {
// case ChildStatus::WORKING:
// return;
// case ChildStatus::COMPLETED:
// algo().complete();
// return;
// case ChildStatus::CANCELED:
// algo().cancel("Unexpected child cancel");
// return;
// }
// }
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,37 +1,37 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
// Join starts working N ticks away from the market (usually) and then
// watches for the opposite inside to become same side inside market;
// if there is then enough size on that market, it will join.
// // Join starts working N ticks away from the market (usually) and then
// // watches for the opposite inside to become same side inside market;
// // if there is then enough size on that market, it will join.
class JoinAlgo : public IAlgoOrder {
public:
JoinAlgo();
~JoinAlgo();
// class JoinAlgo : public IAlgoOrder {
// public:
// JoinAlgo();
// ~JoinAlgo();
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_child_change(OrderKey, ChildState const &) override;
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(api::orderman::Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_child_change(OrderKey, ChildState const &) override;
private:
enum class Status;
struct PImpl;
std::unique_ptr<PImpl> me;
// private:
// enum class Status;
// struct PImpl;
// std::unique_ptr<PImpl> me;
IAlgoServices &algo() const;
void update_join_threshold();
bool has_running_child() const;
void try_to_join();
Price get_price_to_pass(Price price) const;
};
// IAlgoServices &algo() const;
// void update_join_threshold();
// bool has_running_child() const;
// void try_to_join();
// Price get_price_to_pass(Price price) const;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,342 +1,342 @@
#include "lib/orderman/orders/algo/algo_orders/LeanAlgo.h"
// #include "lib/orderman/orders/algo/algo_orders/LeanAlgo.h"
#include "lib/domain_model/Security.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orderman_utils/Book.h"
#include "lib/orderman/orderman_utils/orderman_utils.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
// #include "lib/domain_model/Security.h"
// #include "lib/orderman/order_factory/IOrderFactory.h"
// #include "lib/orderman/orderman_utils/Book.h"
// #include "lib/orderman/orderman_utils/orderman_utils.h"
// #include "lib/orderman/orders/algo/IAlgoServices.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using namespace api::orderman;
// using namespace api::orderman;
enum class LeanAlgo::Status { WAITING_FOR_MD, WORKING, DONE };
// enum class LeanAlgo::Status { WAITING_FOR_MD, WORKING, DONE };
struct LeanAlgo::PImpl {
IAlgoServices *algo_services;
double tick_size = os::nan();
Status status = Status::WAITING_FOR_MD;
// the first observed opp inside price
Price anchor_price = Price(os::nan());
Price observed_price = Price(os::nan());
OrderKey child_key = null_order_key();
ChildTemplate *child_template = nullptr;
Book curr_book;
Price out_price = Price(os::nan());
Quantity out_qty = Quantity(0);
Price cross_price = Price(os::nan());
};
// struct LeanAlgo::PImpl {
// IAlgoServices *algo_services;
// double tick_size = os::nan();
// Status status = Status::WAITING_FOR_MD;
// // the first observed opp inside price
// Price anchor_price = Price(os::nan());
// Price observed_price = Price(os::nan());
// OrderKey child_key = null_order_key();
// ChildTemplate *child_template = nullptr;
// Book curr_book;
// Price out_price = Price(os::nan());
// Quantity out_qty = Quantity(0);
// Price cross_price = Price(os::nan());
// };
LeanAlgo::LeanAlgo() : me(new PImpl()) {}
LeanAlgo::~LeanAlgo() {}
IAlgoServices &LeanAlgo::algo() const { return *me->algo_services; }
// LeanAlgo::LeanAlgo() : me(new PImpl()) {}
// LeanAlgo::~LeanAlgo() {}
// IAlgoServices &LeanAlgo::algo() const { return *me->algo_services; }
// initialization /////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
OrderOpResult LeanAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
auto &def = serv.definition();
me->tick_size = serv.security().contract_spec().low_tick();
bts_assert(def.has_custom_def());
bts_assert(def.custom_def().has_lean());
if (serv.is_attaching()) {
} else {
auto child_def = def;
*child_def.mutable_custom_def() = def.custom_def().lean().child();
me->child_template = &(serv.make_child_template(child_def));
}
return {};
}
void LeanAlgo::do_launch(IAlgoServices &algo) {
me->algo_services = &algo;
bts_assert(me->child_template);
_udpate_out_price_qty(algo.price(), algo.quantity());
me->child_key =
algo.launch_child(*me->child_template, algo.price(), algo.quantity());
LOG(OrderMan, Info,
"LeanAlgo::do_launch key:{} child_key:{} price:{} qty:{}",
algo.order_key().value(), me->child_key, algo.price(), algo.quantity());
algo.subscribe_book(SecurityKey(algo.definition().security_key()));
}
void LeanAlgo::do_launch_attached(
IAlgoServices &algo, OrderKey k, ChildState st) {
me->algo_services = &algo;
me->child_key = k;
algo.subscribe_book(SecurityKey(algo.definition().security_key()));
on_child_change(k, st);
}
// updates ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void LeanAlgo::do_update(const api::orderman::Update &update) {
LOG(OrderMan, Diag, "LeanOrder order {} receiving update: {}",
algo().order_key().value(), utils::pb::to_string(update));
// // initialization /////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// OrderOpResult LeanAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
// auto &def = serv.definition();
// me->tick_size = serv.security().contract_spec().low_tick();
// bts_assert(def.has_custom_def());
// bts_assert(def.custom_def().has_lean());
// if (serv.is_attaching()) {
// } else {
// auto child_def = def;
// *child_def.mutable_custom_def() = def.custom_def().lean().child();
// me->child_template = &(serv.make_child_template(child_def));
// }
// return {};
// }
// void LeanAlgo::do_launch(IAlgoServices &algo) {
// me->algo_services = &algo;
// bts_assert(me->child_template);
// _udpate_out_price_qty(algo.price(), algo.quantity());
// me->child_key =
// algo.launch_child(*me->child_template, algo.price(), algo.quantity());
// LOG(OrderMan, Info,
// "LeanAlgo::do_launch key:{} child_key:{} price:{} qty:{}",
// algo.order_key().value(), me->child_key, algo.price(), algo.quantity());
// algo.subscribe_book(SecurityKey(algo.definition().security_key()));
// }
// void LeanAlgo::do_launch_attached(
// IAlgoServices &algo, OrderKey k, ChildState st) {
// me->algo_services = &algo;
// me->child_key = k;
// algo.subscribe_book(SecurityKey(algo.definition().security_key()));
// on_child_change(k, st);
// }
// // updates ////////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// void LeanAlgo::do_update(const api::orderman::Update &update) {
// LOG(OrderMan, Diag, "LeanOrder order {} receiving update: {}",
// algo().order_key().value(), utils::pb::to_string(update));
if (update.has_custom() && !update.custom().has_lean()) {
algo().terminate_error("Invalid update");
return;
}
Update child_update;
Price price_to_pass = _get_price_to_pass(Price(update.price()));
child_update.set_price(price_to_pass.value());
child_update.set_quantity(update.quantity());
if (update.has_custom()) {
auto &lean_update = update.custom().lean();
*child_update.mutable_custom() = lean_update.child();
}
_udpate_out_price_qty(Price(update.price()), Quantity(update.quantity()));
algo().update_child(me->child_key, child_update);
switch (me->status) {
case LeanAlgo::Status::WAITING_FOR_MD:
break;
case LeanAlgo::Status::WORKING:
_analyze_and_act();
break;
case LeanAlgo::Status::DONE:
break;
}
}
// callbacks //////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void LeanAlgo::on_book(SecurityKey, const Book &book) {
me->curr_book = book;
switch (me->status) {
case LeanAlgo::Status::WAITING_FOR_MD:
if (_opp_inside_market().size() != 0) {
me->anchor_price = Price(_opp_inside_market().price());
me->observed_price = me->anchor_price;
me->status = LeanAlgo::Status::WORKING;
_analyze_and_act();
}
break;
case LeanAlgo::Status::WORKING:
_analyze_and_act();
break;
case LeanAlgo::Status::DONE:
break;
}
}
void LeanAlgo::on_child_change(OrderKey key, const ChildState &st) {
LOG(OrderMan, Info,
"LeanAlgo::on_child_change key:{} child_key:{} status:{} total_fills:{} "
"last_price:{}",
algo().order_key().value(), key, static_cast<int>(st.status),
st.total_fills, st.last_price);
algo().update_fill_state(st.total_fills, st.last_price);
switch (st.status) {
case ChildStatus::WORKING:
// if this update had fills and qty threshold is
// ratio, should it re-evaluate ?
return;
case ChildStatus::COMPLETED:
me->status = LeanAlgo::Status::DONE;
algo().complete();
return;
case ChildStatus::CANCELED:
me->status = LeanAlgo::Status::DONE;
algo().cancel("Unexpected child cancel");
return;
}
}
// accessor helpers ///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
Quantity LeanAlgo::_cross_threshold() const {
auto qty_measure = algo().definition().custom_def().lean().cross_threshold();
return SpreaderUtils::_quantity_threshold(
qty_measure, Quantity(algo().quantity()));
}
uint32_t LeanAlgo::_max_chase_ticks() const {
return algo().definition().custom_def().lean().max_chase_ticks();
}
api::orderman::LeanOrderDefinition::OnToward
LeanAlgo::_on_toward_behaviour() const {
return algo().definition().custom_def().lean().trade_toward_behavior();
}
bool LeanAlgo::_book_moved_toward() const {
return securities::is_better(
_opp_side(), Price(_opp_inside_market().price()), me->anchor_price);
}
bool LeanAlgo::_book_moved_away() const {
return securities::is_better(
_opp_side(), me->anchor_price, Price(_opp_inside_market().price()));
}
md::PriceSize LeanAlgo::_opp_inside_market() const {
return me->curr_book.side(_opp_side())[0];
}
api::Side LeanAlgo::_opp_side() const {
return securities::other_side(algo().definition().side());
}
bool LeanAlgo::_within_chase_band(Price price) const {
return securities::is_better(
algo().definition().side(), me->anchor_price, price) ||
static_cast<uint32_t>(std::abs(securities::price_diff_to_ticks(
price.value(), me->anchor_price.value(), me->tick_size))) <=
_max_chase_ticks();
}
Price LeanAlgo::_chase_limit() const {
double offset = me->tick_size * _max_chase_ticks();
return securities::improve_price(
algo().definition().side(), me->anchor_price, Price(offset));
}
LeanAlgo::OppMarketState LeanAlgo::_market_state() const {
md::PriceSize opp_inside = _opp_inside_market();
if (opp_inside.size() == 0)
return OppMarketState::NO_MARKET;
else if (_book_moved_away())
return OppMarketState::AWAY;
else if (_book_moved_toward())
return OppMarketState::TOWARD;
else
return OppMarketState::NO_DIRECTIONAL_CHANGE;
}
Price LeanAlgo::_get_price_to_pass(Price price) const {
// if it has crossed once, then the
// child order is always passed that price
if (!os::is_nan(me->cross_price.value()))
return me->cross_price;
return price;
}
// actions ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void LeanAlgo::_analyze_and_act() {
bts_assert(me->status == LeanAlgo::Status::WORKING);
auto market_change = _market_state();
switch (market_change) {
case OppMarketState::NO_MARKET:
_go_furthest_possible();
break;
case OppMarketState::NO_DIRECTIONAL_CHANGE:
_try_cross(me->observed_price);
break;
case OppMarketState::AWAY:
me->observed_price = Price(_opp_inside_market().price());
_go_furthest_possible();
break;
case OppMarketState::TOWARD:
_on_toward_me();
break;
}
}
// since this is more complicated than the
// other market states
void LeanAlgo::_on_toward_me() {
switch (_on_toward_behaviour()) {
case LeanOrderDefinition_OnToward_CANCEL:
LOG(OrderMan, Info,
"Canceling LeanAlgo {} because opposite moved towards order",
algo().order_key().value());
algo().cancel("Opposite inside market moved towards order");
break;
case LeanOrderDefinition_OnToward_DETACH:
LOG(OrderMan, Info,
"LeanAlgo {} is done because opposite moved towards order",
algo().order_key().value());
algo().detach_child(me->child_key);
algo().cancel("Opposite inside market moved towards order(detaching)");
break;
case LeanOrderDefinition_OnToward_REPRICE:
LOG(OrderMan, Info,
"LeanAlgo {} changing its target to {} because opposite moved "
"towards order",
algo().order_key().value(), _opp_inside_market().price());
me->observed_price = Price(_opp_inside_market().price());
_try_cross(me->observed_price);
break;
case LeanOrderDefinition_OnToward_NO_CHANGE:
LOG(OrderMan, Info, "LeanAlgo {} ignoring opposite move towards order",
algo().order_key().value());
_try_cross(me->observed_price);
break;
default:
bts_assert(false);
break;
}
}
// if the price is within the chase bands
// (which is only applied on AWAY prices),
// and the cum qty on the opposite side
// up to the price is less than
// the threshold, then it jumps to that price.
bool LeanAlgo::_try_cross(Price price) {
if (_within_chase_band(price)) {
Quantity cumm_qty;
SpreaderUtils::find_qty_upto_price_level(
me->curr_book, _opp_side(), price, me->tick_size, cumm_qty);
// if we are within the chase bands and
// the price happens to be better than
// the opposite inside market (ie for buy orders
// the price < top sell market)
// then cumm_qty will be 0, which
// means the price will always be modified
// to the arg
Quantity trigger_quantity = _cross_threshold();
if (cumm_qty.value() < trigger_quantity.value()) {
_modify_price_price_qty(price, algo().quantity());
return true;
}
}
return false;
}
// this function is called when NO_MARKET or AWAY
// it tries to place the order as far as it can;
// with the priority:
// 1. observed_price
// 2. chase_limit
// 3. opp inside-1 (for buys)
// 4. same side inside
void LeanAlgo::_go_furthest_possible() {
if (!_try_cross(me->observed_price)) {
// this will be false if the observed price
// is outside the chase OR qty threshold
// is not met
if (!_try_cross(_chase_limit())) {
// this will only be false
// if chase_limit is on the opposite side
// and the qty > threshold (if it was on the
// same side, cum_qty==0 and it should go at
// that price, see try_cross)
auto opp_inside = _opp_inside_market();
if (opp_inside.size() > 0) {
// goes to the opp side, improves a tick to get
// to the same market top, and crosses there
auto inside = securities::worsen_price(
algo().definition().side(), Price(opp_inside.price()),
Price(me->tick_size));
_try_cross(inside);
} else {
auto inside = me->curr_book.side(algo().definition().side())[0];
if (inside.size() > 0)
_try_cross(Price(inside.price()));
}
}
}
}
void LeanAlgo::_udpate_out_price_qty(Price price, Quantity qty) {
me->out_price = price;
me->out_qty = qty;
}
bool LeanAlgo::_modify_price_price_qty(Price price, Quantity qty) {
if (me->out_price.value() != price.value() ||
me->out_qty.value() != qty.value()) {
_udpate_out_price_qty(price, qty);
bool first_cross = os::is_nan(me->cross_price.value());
me->cross_price = price;
Update child_update;
child_update.set_price(price.value()),
child_update.set_quantity(qty.value());
LOG(OrderMan, Info,
"LeanAlgo::_try_cross updating child "
"key:{} child_key:{} price:{} qty:{}",
algo().order_key().value(), me->child_key, child_update.price(),
child_update.quantity());
algo().update_child(me->child_key, child_update);
if (first_cross)
algo().set_ladder_visibility(false);
return true;
}
return false;
}
} // namespace orderman
} // namespace bts
// if (update.has_custom() && !update.custom().has_lean()) {
// algo().terminate_error("Invalid update");
// return;
// }
// Update child_update;
// Price price_to_pass = _get_price_to_pass(Price(update.price()));
// child_update.set_price(price_to_pass.value());
// child_update.set_quantity(update.quantity());
// if (update.has_custom()) {
// auto &lean_update = update.custom().lean();
// *child_update.mutable_custom() = lean_update.child();
// }
// _udpate_out_price_qty(Price(update.price()), Quantity(update.quantity()));
// algo().update_child(me->child_key, child_update);
// switch (me->status) {
// case LeanAlgo::Status::WAITING_FOR_MD:
// break;
// case LeanAlgo::Status::WORKING:
// _analyze_and_act();
// break;
// case LeanAlgo::Status::DONE:
// break;
// }
// }
// // callbacks //////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// void LeanAlgo::on_book(SecurityKey, const Book &book) {
// me->curr_book = book;
// switch (me->status) {
// case LeanAlgo::Status::WAITING_FOR_MD:
// if (_opp_inside_market().size() != 0) {
// me->anchor_price = Price(_opp_inside_market().price());
// me->observed_price = me->anchor_price;
// me->status = LeanAlgo::Status::WORKING;
// _analyze_and_act();
// }
// break;
// case LeanAlgo::Status::WORKING:
// _analyze_and_act();
// break;
// case LeanAlgo::Status::DONE:
// break;
// }
// }
// void LeanAlgo::on_child_change(OrderKey key, const ChildState &st) {
// LOG(OrderMan, Info,
// "LeanAlgo::on_child_change key:{} child_key:{} status:{} total_fills:{} "
// "last_price:{}",
// algo().order_key().value(), key, static_cast<int>(st.status),
// st.total_fills, st.last_price);
// algo().update_fill_state(st.total_fills, st.last_price);
// switch (st.status) {
// case ChildStatus::WORKING:
// // if this update had fills and qty threshold is
// // ratio, should it re-evaluate ?
// return;
// case ChildStatus::COMPLETED:
// me->status = LeanAlgo::Status::DONE;
// algo().complete();
// return;
// case ChildStatus::CANCELED:
// me->status = LeanAlgo::Status::DONE;
// algo().cancel("Unexpected child cancel");
// return;
// }
// }
// // accessor helpers ///////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// Quantity LeanAlgo::_cross_threshold() const {
// auto qty_measure = algo().definition().custom_def().lean().cross_threshold();
// return SpreaderUtils::_quantity_threshold(
// qty_measure, Quantity(algo().quantity()));
// }
// uint32_t LeanAlgo::_max_chase_ticks() const {
// return algo().definition().custom_def().lean().max_chase_ticks();
// }
// api::orderman::LeanOrderDefinition::OnToward
// LeanAlgo::_on_toward_behaviour() const {
// return algo().definition().custom_def().lean().trade_toward_behavior();
// }
// bool LeanAlgo::_book_moved_toward() const {
// return securities::is_better(
// _opp_side(), Price(_opp_inside_market().price()), me->anchor_price);
// }
// bool LeanAlgo::_book_moved_away() const {
// return securities::is_better(
// _opp_side(), me->anchor_price, Price(_opp_inside_market().price()));
// }
// md::PriceSize LeanAlgo::_opp_inside_market() const {
// return me->curr_book.side(_opp_side())[0];
// }
// api::Side LeanAlgo::_opp_side() const {
// return securities::other_side(algo().definition().side());
// }
// bool LeanAlgo::_within_chase_band(Price price) const {
// return securities::is_better(
// algo().definition().side(), me->anchor_price, price) ||
// static_cast<uint32_t>(std::abs(securities::price_diff_to_ticks(
// price.value(), me->anchor_price.value(), me->tick_size))) <=
// _max_chase_ticks();
// }
// Price LeanAlgo::_chase_limit() const {
// double offset = me->tick_size * _max_chase_ticks();
// return securities::improve_price(
// algo().definition().side(), me->anchor_price, Price(offset));
// }
// LeanAlgo::OppMarketState LeanAlgo::_market_state() const {
// md::PriceSize opp_inside = _opp_inside_market();
// if (opp_inside.size() == 0)
// return OppMarketState::NO_MARKET;
// else if (_book_moved_away())
// return OppMarketState::AWAY;
// else if (_book_moved_toward())
// return OppMarketState::TOWARD;
// else
// return OppMarketState::NO_DIRECTIONAL_CHANGE;
// }
// Price LeanAlgo::_get_price_to_pass(Price price) const {
// // if it has crossed once, then the
// // child order is always passed that price
// if (!os::is_nan(me->cross_price.value()))
// return me->cross_price;
// return price;
// }
// // actions ////////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// void LeanAlgo::_analyze_and_act() {
// bts_assert(me->status == LeanAlgo::Status::WORKING);
// auto market_change = _market_state();
// switch (market_change) {
// case OppMarketState::NO_MARKET:
// _go_furthest_possible();
// break;
// case OppMarketState::NO_DIRECTIONAL_CHANGE:
// _try_cross(me->observed_price);
// break;
// case OppMarketState::AWAY:
// me->observed_price = Price(_opp_inside_market().price());
// _go_furthest_possible();
// break;
// case OppMarketState::TOWARD:
// _on_toward_me();
// break;
// }
// }
// // since this is more complicated than the
// // other market states
// void LeanAlgo::_on_toward_me() {
// switch (_on_toward_behaviour()) {
// case LeanOrderDefinition_OnToward_CANCEL:
// LOG(OrderMan, Info,
// "Canceling LeanAlgo {} because opposite moved towards order",
// algo().order_key().value());
// algo().cancel("Opposite inside market moved towards order");
// break;
// case LeanOrderDefinition_OnToward_DETACH:
// LOG(OrderMan, Info,
// "LeanAlgo {} is done because opposite moved towards order",
// algo().order_key().value());
// algo().detach_child(me->child_key);
// algo().cancel("Opposite inside market moved towards order(detaching)");
// break;
// case LeanOrderDefinition_OnToward_REPRICE:
// LOG(OrderMan, Info,
// "LeanAlgo {} changing its target to {} because opposite moved "
// "towards order",
// algo().order_key().value(), _opp_inside_market().price());
// me->observed_price = Price(_opp_inside_market().price());
// _try_cross(me->observed_price);
// break;
// case LeanOrderDefinition_OnToward_NO_CHANGE:
// LOG(OrderMan, Info, "LeanAlgo {} ignoring opposite move towards order",
// algo().order_key().value());
// _try_cross(me->observed_price);
// break;
// default:
// bts_assert(false);
// break;
// }
// }
// // if the price is within the chase bands
// // (which is only applied on AWAY prices),
// // and the cum qty on the opposite side
// // up to the price is less than
// // the threshold, then it jumps to that price.
// bool LeanAlgo::_try_cross(Price price) {
// if (_within_chase_band(price)) {
// Quantity cumm_qty;
// SpreaderUtils::find_qty_upto_price_level(
// me->curr_book, _opp_side(), price, me->tick_size, cumm_qty);
// // if we are within the chase bands and
// // the price happens to be better than
// // the opposite inside market (ie for buy orders
// // the price < top sell market)
// // then cumm_qty will be 0, which
// // means the price will always be modified
// // to the arg
// Quantity trigger_quantity = _cross_threshold();
// if (cumm_qty.value() < trigger_quantity.value()) {
// _modify_price_price_qty(price, algo().quantity());
// return true;
// }
// }
// return false;
// }
// // this function is called when NO_MARKET or AWAY
// // it tries to place the order as far as it can;
// // with the priority:
// // 1. observed_price
// // 2. chase_limit
// // 3. opp inside-1 (for buys)
// // 4. same side inside
// void LeanAlgo::_go_furthest_possible() {
// if (!_try_cross(me->observed_price)) {
// // this will be false if the observed price
// // is outside the chase OR qty threshold
// // is not met
// if (!_try_cross(_chase_limit())) {
// // this will only be false
// // if chase_limit is on the opposite side
// // and the qty > threshold (if it was on the
// // same side, cum_qty==0 and it should go at
// // that price, see try_cross)
// auto opp_inside = _opp_inside_market();
// if (opp_inside.size() > 0) {
// // goes to the opp side, improves a tick to get
// // to the same market top, and crosses there
// auto inside = securities::worsen_price(
// algo().definition().side(), Price(opp_inside.price()),
// Price(me->tick_size));
// _try_cross(inside);
// } else {
// auto inside = me->curr_book.side(algo().definition().side())[0];
// if (inside.size() > 0)
// _try_cross(Price(inside.price()));
// }
// }
// }
// }
// void LeanAlgo::_udpate_out_price_qty(Price price, Quantity qty) {
// me->out_price = price;
// me->out_qty = qty;
// }
// bool LeanAlgo::_modify_price_price_qty(Price price, Quantity qty) {
// if (me->out_price.value() != price.value() ||
// me->out_qty.value() != qty.value()) {
// _udpate_out_price_qty(price, qty);
// bool first_cross = os::is_nan(me->cross_price.value());
// me->cross_price = price;
// Update child_update;
// child_update.set_price(price.value()),
// child_update.set_quantity(qty.value());
// LOG(OrderMan, Info,
// "LeanAlgo::_try_cross updating child "
// "key:{} child_key:{} price:{} qty:{}",
// algo().order_key().value(), me->child_key, child_update.price(),
// child_update.quantity());
// algo().update_child(me->child_key, child_update);
// if (first_cross)
// algo().set_ladder_visibility(false);
// return true;
// }
// return false;
// }
// } // namespace orderman
// } // namespace bts
@@ -1,59 +1,56 @@
#pragma once
// #pragma once
#include "lib/domain_model/Security.pb.h"
#include "lib/md/PriceSize.h"
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/domain_model/Security.pb.h"
// #include "lib/md/PriceSize.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class LeanAlgo : public IAlgoOrder {
public:
LeanAlgo();
~LeanAlgo();
// class LeanAlgo : public IAlgoOrder {
// public:
// LeanAlgo();
// ~LeanAlgo();
public:
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_child_change(OrderKey, ChildState const &) override;
// public:
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(api::orderman::Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_child_change(OrderKey, ChildState const &) override;
private:
// this is the state of opposite inside market (oim)
// NO_MARKET: oim.qty==0
// NO_DIRECTIONAL_CHANGE: oim.price==anchor_price
// AWAY: anchor_price is_better_than oim.price
// TOWARD: oim.price is_better_than anchor_price
// since lean order is placed away from the market
// on the same size, AWAY means opp inside moves
// AWAY from the order, similiarly TOWARD
enum class OppMarketState { NO_MARKET, NO_DIRECTIONAL_CHANGE, AWAY, TOWARD };
struct PImpl;
enum class Status;
IAlgoServices &algo() const;
Quantity _cross_threshold() const;
uint32_t _max_chase_ticks() const;
api::orderman::LeanOrderDefinition::OnToward _on_toward_behaviour() const;
bool _book_moved_toward() const;
bool _book_moved_away() const;
md::PriceSize _opp_inside_market() const;
api::Side _opp_side() const;
bool _within_chase_band(Price price) const;
Price _chase_limit() const;
OppMarketState _market_state() const;
Price _get_price_to_pass(Price price) const;
void _analyze_and_act();
bool _try_cross(Price);
void _go_furthest_possible();
void _on_toward_me();
void _udpate_out_price_qty(Price price, Quantity qty);
bool _modify_price_price_qty(Price price, Quantity qty);
// private:
// // this is the state of opposite inside market (oim)
// // NO_MARKET: oim.qty==0
// // NO_DIRECTIONAL_CHANGE: oim.price==anchor_price
// // AWAY: anchor_price is_better_than oim.price
// // TOWARD: oim.price is_better_than anchor_price
// // since lean order is placed away from the market
// // on the same size, AWAY means opp inside moves
// // AWAY from the order, similiarly TOWARD
// enum class OppMarketState { NO_MARKET, NO_DIRECTIONAL_CHANGE, AWAY, TOWARD
// }; struct PImpl; enum class Status; IAlgoServices &algo() const; Quantity
// _cross_threshold() const; uint32_t _max_chase_ticks() const;
// api::orderman::LeanOrderDefinition::OnToward _on_toward_behaviour() const;
// bool _book_moved_toward() const;
// bool _book_moved_away() const;
// md::PriceSize _opp_inside_market() const;
// api::Side _opp_side() const;
// bool _within_chase_band(Price price) const;
// Price _chase_limit() const;
// OppMarketState _market_state() const;
// Price _get_price_to_pass(Price price) const;
// void _analyze_and_act();
// bool _try_cross(Price);
// void _go_furthest_possible();
// void _on_toward_me();
// void _udpate_out_price_qty(Price price, Quantity qty);
// bool _modify_price_price_qty(Price price, Quantity qty);
private:
std::unique_ptr<PImpl> me;
};
// private:
// std::unique_ptr<PImpl> me;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,192 +1,192 @@
#include "lib/orderman/orders/algo/algo_orders/OffsetAlgo.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
// #include "lib/orderman/orders/algo/algo_orders/OffsetAlgo.h"
// #include "lib/orderman/order_factory/IOrderFactory.h"
// #include "lib/orderman/orders/algo/IAlgoServices.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using namespace api::orderman;
using namespace std;
// using namespace api::orderman;
// using namespace std;
enum class OffsetAlgo::Status { WAITING_FOR_MD, DEPLOYED };
// enum class OffsetAlgo::Status { WAITING_FOR_MD, DEPLOYED };
struct OffsetAlgo::PImpl {
IAlgoServices *algo;
Status status = Status::WAITING_FOR_MD;
OrderKey child_key = null_order_key();
ChildTemplate *child_template = nullptr;
Book last_book;
uint32_t tick_offset_on_deploy;
double child_price = os::nan();
};
// struct OffsetAlgo::PImpl {
// IAlgoServices *algo;
// Status status = Status::WAITING_FOR_MD;
// OrderKey child_key = null_order_key();
// ChildTemplate *child_template = nullptr;
// Book last_book;
// uint32_t tick_offset_on_deploy;
// double child_price = os::nan();
// };
OffsetAlgo::OffsetAlgo() : me(new PImpl()) {}
OffsetAlgo::~OffsetAlgo() {}
IAlgoServices &OffsetAlgo::algo() const { return *me->algo; }
// OffsetAlgo::OffsetAlgo() : me(new PImpl()) {}
// OffsetAlgo::~OffsetAlgo() {}
// IAlgoServices &OffsetAlgo::algo() const { return *me->algo; }
OrderOpResult OffsetAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
auto &def = serv.definition();
bts_assert(def.has_custom_def());
bts_assert(def.custom_def().has_offset());
// Validate the offset part of the definition
auto &offset = def.custom_def().offset();
if (!offset.has_child() &&
OrderKey(def.attach_target_order_key()) == null_order_key()) {
return OrderOpResult{
ORDER_ERROR_INVALID_ORDER_DEFINITON,
"Offset order must have either a child order definition or an "
"order to attach to"};
}
me->tick_offset_on_deploy = offset.tick_offset_on_deploy();
if (!serv.is_attaching()) {
// if we are not attaching create a child template so we can
// quickly launch our child when the time comes.
auto child_def = def;
*child_def.mutable_custom_def() = def.custom_def().offset().child();
me->child_template = &(serv.make_child_template(child_def));
}
return {};
}
// OrderOpResult OffsetAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
// auto &def = serv.definition();
// bts_assert(def.has_custom_def());
// bts_assert(def.custom_def().has_offset());
// // Validate the offset part of the definition
// auto &offset = def.custom_def().offset();
// if (!offset.has_child() &&
// OrderKey(def.attach_target_order_key()) == null_order_key()) {
// return OrderOpResult{
// ORDER_ERROR_INVALID_ORDER_DEFINITON,
// "Offset order must have either a child order definition or an "
// "order to attach to"};
// }
// me->tick_offset_on_deploy = offset.tick_offset_on_deploy();
// if (!serv.is_attaching()) {
// // if we are not attaching create a child template so we can
// // quickly launch our child when the time comes.
// auto child_def = def;
// *child_def.mutable_custom_def() = def.custom_def().offset().child();
// me->child_template = &(serv.make_child_template(child_def));
// }
// return {};
// }
void OffsetAlgo::do_launch(IAlgoServices &algo) {
me->algo = &algo;
bts_assert(me->child_template);
algo.subscribe_book(SecurityKey(algo.security().id()));
}
// void OffsetAlgo::do_launch(IAlgoServices &algo) {
// me->algo = &algo;
// bts_assert(me->child_template);
// algo.subscribe_book(SecurityKey(algo.security().id()));
// }
void OffsetAlgo::do_launch_attached(
IAlgoServices &algo, OrderKey k, ChildState st) {
me->algo = &algo;
me->child_key = k;
me->child_price = st.price.value();
algo.subscribe_book(SecurityKey(algo.security().id()));
}
// void OffsetAlgo::do_launch_attached(
// IAlgoServices &algo, OrderKey k, ChildState st) {
// me->algo = &algo;
// me->child_key = k;
// me->child_price = st.price.value();
// algo.subscribe_book(SecurityKey(algo.security().id()));
// }
void OffsetAlgo::do_update(api::orderman::Update const &update) {
LOG(OrderMan, Diag, "OffsetAlgo order {} receiving update: {}",
algo().order_key().value(), utils::pb::to_string(update));
if (update.has_custom()) {
if (!update.custom().has_offset()) {
algo().terminate_error("Invalid update");
return;
}
me->tick_offset_on_deploy =
update.custom().offset().tick_offset_on_deploy();
}
switch (me->status) {
case Status::WAITING_FOR_MD:
if (update.has_custom()) {
if (algo().is_attaching()) {
// don't change the child's price
// (need md to determine initial deploy price)
bts_assert(!os::is_nan(me->child_price));
algo().update_child(
me->child_key, mk_child_update(update, me->child_price));
} else {
auto &def = algo().definition();
auto child_def = def;
*child_def.mutable_custom_def() = update.custom().offset().child();
me->child_template = &(algo().make_child_template(child_def));
}
}
break;
case Status::DEPLOYED:
algo().update_child(me->child_key, mk_child_update(update));
break;
}
}
// void OffsetAlgo::do_update(api::orderman::Update const &update) {
// LOG(OrderMan, Diag, "OffsetAlgo order {} receiving update: {}",
// algo().order_key().value(), utils::pb::to_string(update));
// if (update.has_custom()) {
// if (!update.custom().has_offset()) {
// algo().terminate_error("Invalid update");
// return;
// }
// me->tick_offset_on_deploy =
// update.custom().offset().tick_offset_on_deploy();
// }
// switch (me->status) {
// case Status::WAITING_FOR_MD:
// if (update.has_custom()) {
// if (algo().is_attaching()) {
// // don't change the child's price
// // (need md to determine initial deploy price)
// bts_assert(!os::is_nan(me->child_price));
// algo().update_child(
// me->child_key, mk_child_update(update, me->child_price));
// } else {
// auto &def = algo().definition();
// auto child_def = def;
// *child_def.mutable_custom_def() = update.custom().offset().child();
// me->child_template = &(algo().make_child_template(child_def));
// }
// }
// break;
// case Status::DEPLOYED:
// algo().update_child(me->child_key, mk_child_update(update));
// break;
// }
// }
void OffsetAlgo::on_book(SecurityKey, Book const &b) {
me->last_book = b;
// log top of book
LOG(OrderMan, Diag, "OffsetAlgo order {} on book. BID: {}@{} ASK: {}@{}",
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
b.side(api::SIDE_SELL)[0].price());
// attempt deploy
if (me->status == Status::WAITING_FOR_MD)
try_deploy();
}
// void OffsetAlgo::on_book(SecurityKey, Book const &b) {
// me->last_book = b;
// // log top of book
// LOG(OrderMan, Diag, "OffsetAlgo order {} on book. BID: {}@{} ASK: {}@{}",
// algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
// b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
// b.side(api::SIDE_SELL)[0].price());
// // attempt deploy
// if (me->status == Status::WAITING_FOR_MD)
// try_deploy();
// }
Update OffsetAlgo::mk_child_update(
const Update &source, const double &price_override) {
Update target;
if (source.has_custom()) {
bts_assert(source.custom().has_offset());
*target.mutable_custom() = source.custom().offset().child();
}
if (!os::is_nan(price_override))
target.set_price(price_override);
else
target.set_price(source.price());
target.set_quantity(source.quantity());
return target;
}
// Update OffsetAlgo::mk_child_update(
// const Update &source, const double &price_override) {
// Update target;
// if (source.has_custom()) {
// bts_assert(source.custom().has_offset());
// *target.mutable_custom() = source.custom().offset().child();
// }
// if (!os::is_nan(price_override))
// target.set_price(price_override);
// else
// target.set_price(source.price());
// target.set_quantity(source.quantity());
// return target;
// }
void OffsetAlgo::try_deploy() {
bts_assert(me->status == Status::WAITING_FOR_MD);
auto side = algo().definition().side();
auto &top_psz = me->last_book.side(side)[0];
if (!top_psz.size()) {
// log state (still waiting for md)
LOG(OrderMan, Diag,
"OffsetAlgo order {} still waiting for md. Order: {} {}@ {} ticks "
"away.",
algo().order_key().value(), algo().definition().side(),
algo().quantity().value(), me->tick_offset_on_deploy);
// void OffsetAlgo::try_deploy() {
// bts_assert(me->status == Status::WAITING_FOR_MD);
// auto side = algo().definition().side();
// auto &top_psz = me->last_book.side(side)[0];
// if (!top_psz.size()) {
// // log state (still waiting for md)
// LOG(OrderMan, Diag,
// "OffsetAlgo order {} still waiting for md. Order: {} {}@ {} ticks "
// "away.",
// algo().order_key().value(), algo().definition().side(),
// algo().quantity().value(), me->tick_offset_on_deploy);
} else {
const double &offset = securities::tick_size(algo().security()).value() *
me->tick_offset_on_deploy;
me->child_price =
securities::worsen_price(side, Price(top_psz.price()), Price(offset))
.value();
if (algo().is_attaching()) {
// log imminent update
LOG(OrderMan, Diag,
"OffsetAlgo order {} attaching child {}. Order: {} {}@ {} ({} "
"ticks "
"away).",
algo().order_key().value(), me->child_key.value(), side,
algo().quantity().value(), me->child_price,
me->tick_offset_on_deploy);
// update
Update child_update;
child_update.set_quantity(algo().quantity().value());
child_update.set_price(me->child_price);
algo().update_child(me->child_key, child_update);
} else {
// log imminent deploy
LOG(OrderMan, Diag,
"OffsetAlgo order {} deploying child. Order: {} {}@ {} ({} ticks "
"away).",
algo().order_key().value(), side, algo().quantity().value(),
me->child_price, me->tick_offset_on_deploy);
// deploy
me->child_key = algo().launch_child(
*me->child_template, Price(me->child_price), algo().quantity());
}
algo().set_ladder_visibility(false);
me->status = Status::DEPLOYED;
}
}
// } else {
// const double &offset = securities::tick_size(algo().security()).value() *
// me->tick_offset_on_deploy;
// me->child_price =
// securities::worsen_price(side, Price(top_psz.price()), Price(offset))
// .value();
// if (algo().is_attaching()) {
// // log imminent update
// LOG(OrderMan, Diag,
// "OffsetAlgo order {} attaching child {}. Order: {} {}@ {} ({} "
// "ticks "
// "away).",
// algo().order_key().value(), me->child_key.value(), side,
// algo().quantity().value(), me->child_price,
// me->tick_offset_on_deploy);
// // update
// Update child_update;
// child_update.set_quantity(algo().quantity().value());
// child_update.set_price(me->child_price);
// algo().update_child(me->child_key, child_update);
// } else {
// // log imminent deploy
// LOG(OrderMan, Diag,
// "OffsetAlgo order {} deploying child. Order: {} {}@ {} ({} ticks "
// "away).",
// algo().order_key().value(), side, algo().quantity().value(),
// me->child_price, me->tick_offset_on_deploy);
// // deploy
// me->child_key = algo().launch_child(
// *me->child_template, Price(me->child_price), algo().quantity());
// }
// algo().set_ladder_visibility(false);
// me->status = Status::DEPLOYED;
// }
// }
void OffsetAlgo::on_child_change(OrderKey, ChildState const &st) {
me->child_price = st.price.value();
algo().update_fill_state(st.total_fills, st.last_price);
switch (st.status) {
case ChildStatus::WORKING:
return;
case ChildStatus::COMPLETED:
algo().complete();
return;
case ChildStatus::CANCELED:
algo().cancel("Unexpected child cancel");
return;
}
}
} // namespace orderman
} // namespace bts
// void OffsetAlgo::on_child_change(OrderKey, ChildState const &st) {
// me->child_price = st.price.value();
// algo().update_fill_state(st.total_fills, st.last_price);
// switch (st.status) {
// case ChildStatus::WORKING:
// return;
// case ChildStatus::COMPLETED:
// algo().complete();
// return;
// case ChildStatus::CANCELED:
// algo().cancel("Unexpected child cancel");
// return;
// }
// }
// } // namespace orderman
// } // namespace bts
@@ -1,39 +1,40 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
using Update = bts::api::orderman::Update;
// using Update = bts::api::orderman::Update;
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
// Offset sends a child-order/algo N ticks away from the top of the book.
// If necessary, it will wait for the market to arrive before shooting the
// child.
// // Offset sends a child-order/algo N ticks away from the top of the book.
// // If necessary, it will wait for the market to arrive before shooting the
// // child.
class OffsetAlgo : public IAlgoOrder {
public:
OffsetAlgo();
~OffsetAlgo();
// class OffsetAlgo : public IAlgoOrder {
// public:
// OffsetAlgo();
// ~OffsetAlgo();
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_child_change(OrderKey, ChildState const &) override;
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_child_change(OrderKey, ChildState const &) override;
private:
enum class Status;
struct PImpl;
std::unique_ptr<PImpl> me;
// private:
// enum class Status;
// struct PImpl;
// std::unique_ptr<PImpl> me;
static Update mk_child_update(
const Update &source_update, const double &price_override = os::nan());
// static Update mk_child_update(
// const Update &source_update, const double &price_override =
// os::nan());
IAlgoServices &algo() const;
void try_deploy();
};
// IAlgoServices &algo() const;
// void try_deploy();
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,388 +1,394 @@
#include "lib/orderman/orders/algo/algo_orders/ProRataAlgo.h"
// #include "lib/orderman/orders/algo/algo_orders/ProRataAlgo.h"
#include "lib/domain_model/Security.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orders/algo/DynamicHelpers.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
// #include "lib/domain_model/Security.h"
// #include "lib/orderman/order_factory/IOrderFactory.h"
// #include "lib/orderman/orders/algo/DynamicHelpers.h"
// #include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/utils/PBUtils.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/utils/PBUtils.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using namespace api::orderman;
// using namespace api::orderman;
enum class ProRataAlgo::Status {
WAITING_FOR_MD,
WORKING,
DONE_WORKING,
GETTING_OUT
}; // this is for the child
const char *ProRataAlgo::status_name(Status st) {
switch (st) {
case Status::WAITING_FOR_MD:
return "WAITING_FOR_MD";
case Status::WORKING:
return "WORKING";
case Status::DONE_WORKING:
return "DONE_WORKING";
case Status::GETTING_OUT:
return "GETTING_OUT";
}
return nullptr;
}
struct ProRataAlgo::PImpl {
PImpl() : is_overfilled(false) {}
IAlgoServices *algo;
OrderKey child_key = null_order_key();
// enum class ProRataAlgo::Status {
// WAITING_FOR_MD,
// WORKING,
// DONE_WORKING,
// GETTING_OUT
// }; // this is for the child
// const char *ProRataAlgo::status_name(Status st) {
// switch (st) {
// case Status::WAITING_FOR_MD:
// return "WAITING_FOR_MD";
// case Status::WORKING:
// return "WORKING";
// case Status::DONE_WORKING:
// return "DONE_WORKING";
// case Status::GETTING_OUT:
// return "GETTING_OUT";
// }
// return nullptr;
// }
// struct ProRataAlgo::PImpl {
// PImpl() : is_overfilled(false) {}
// IAlgoServices *algo;
// OrderKey child_key = null_order_key();
Price hedge_price_diff;
Price chase_diff;
Quantity fill_limit;
Quantity stay_in_threshold;
bool scratch_on_overfill;
bool stay_in;
// Price hedge_price_diff;
// Price chase_diff;
// Quantity fill_limit;
// Quantity stay_in_threshold;
// bool scratch_on_overfill;
// bool stay_in;
ChildTemplate *child_template = nullptr;
ChildTemplate *hedge_template = nullptr;
ChildTemplate *overfill_hedge_template = nullptr;
// ChildTemplate *child_template = nullptr;
// ChildTemplate *hedge_template = nullptr;
// ChildTemplate *overfill_hedge_template = nullptr;
Quantity hedged_fills = Quantity(0);
Status status = Status::WAITING_FOR_MD;
std::vector<OrderKey> hedges;
std::map<OrderKey, Quantity> overfill_hedges;
// Quantity hedged_fills = Quantity(0);
// Status status = Status::WAITING_FOR_MD;
// std::vector<OrderKey> hedges;
// std::map<OrderKey, Quantity> overfill_hedges;
Book last_book;
Book book_at_inception;
bool is_overfilled;
};
// Book last_book;
// Book book_at_inception;
// bool is_overfilled;
// };
ProRataAlgo::ProRataAlgo() : me(new PImpl()) {}
ProRataAlgo::~ProRataAlgo() {}
IAlgoServices &ProRataAlgo::algo() const { return *me->algo; }
// ProRataAlgo::ProRataAlgo() : me(new PImpl()) {}
// ProRataAlgo::~ProRataAlgo() {}
// IAlgoServices &ProRataAlgo::algo() const { return *me->algo; }
OrderOpResult ProRataAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
const api::orderman::OrderDefinition &def = serv.definition();
bts_assert(def.has_custom_def());
bts_assert(def.custom_def().has_prorata());
// OrderOpResult ProRataAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv)
// {
// const api::orderman::OrderDefinition &def = serv.definition();
// bts_assert(def.has_custom_def());
// bts_assert(def.custom_def().has_prorata());
// Validate the prorata part of the definition
auto &prorata = def.custom_def().prorata();
if (!prorata.has_child() &&
OrderKey(def.attach_target_order_key()) == null_order_key()) {
return OrderOpResult{
ORDER_ERROR_INVALID_ORDER_DEFINITON,
"ProRata order must have either a child order definition or an order "
"to attach to"};
}
// // Validate the prorata part of the definition
// auto &prorata = def.custom_def().prorata();
// if (!prorata.has_child() &&
// OrderKey(def.attach_target_order_key()) == null_order_key()) {
// return OrderOpResult{
// ORDER_ERROR_INVALID_ORDER_DEFINITON,
// "ProRata order must have either a child order definition or an
// order " "to attach to"};
// }
if (!prorata.has_hedge())
return OrderOpResult{
ORDER_ERROR_INVALID_ORDER_DEFINITON,
"ProRata order must have a hedge order definition"};
if (!serv.is_attaching()) {
// if we are not attaching create a child template so we can
// quickly launch our child when the time comes.
auto child_def = def;
*child_def.mutable_custom_def() = def.custom_def().prorata().child();
me->child_template = &(serv.make_child_template(child_def));
}
// if (!prorata.has_hedge())
// return OrderOpResult{
// ORDER_ERROR_INVALID_ORDER_DEFINITON,
// "ProRata order must have a hedge order definition"};
// if (!serv.is_attaching()) {
// // if we are not attaching create a child template so we can
// // quickly launch our child when the time comes.
// auto child_def = def;
// *child_def.mutable_custom_def() = def.custom_def().prorata().child();
// me->child_template = &(serv.make_child_template(child_def));
// }
init_hedge_template(serv, prorata.hedge(), false);
init_hedge_template(serv, prorata.overfill_hedge(), true);
// init_hedge_template(serv, prorata.hedge(), false);
// init_hedge_template(serv, prorata.overfill_hedge(), true);
return {};
}
// return {};
// }
void ProRataAlgo::do_launch(IAlgoServices &algo) {
me->algo = &algo;
set_parameters(algo, algo.definition().custom_def().prorata());
LOG(OrderMan, Diag, "Starting ProRataAlgo {}, {} {}@{}. Will cover {} ticks",
algo.order_key().value(),
securities::side_name(algo.definition().side()), algo.quantity().value(),
algo.price().value(),
algo.definition().custom_def().prorata().hedge_ticks());
// void ProRataAlgo::do_launch(IAlgoServices &algo) {
// me->algo = &algo;
// set_parameters(algo, algo.definition().custom_def().prorata());
// LOG(OrderMan, Diag, "Starting ProRataAlgo {}, {} {}@{}. Will cover {}
// ticks",
// algo.order_key().value(),
// securities::side_name(algo.definition().side()),
// algo.quantity().value(), algo.price().value(),
// algo.definition().custom_def().prorata().hedge_ticks());
LOG(OrderMan, Diag, "ProRataAlgo {} launching child",
algo.order_key().value());
me->child_key =
algo.launch_child(*me->child_template, algo.price(), algo.quantity());
algo.subscribe_book(SecurityKey(algo.security().id()));
}
// LOG(OrderMan, Diag, "ProRataAlgo {} launching child",
// algo.order_key().value());
// me->child_key =
// algo.launch_child(*me->child_template, algo.price(), algo.quantity());
// algo.subscribe_book(SecurityKey(algo.security().id()));
// }
void ProRataAlgo::do_launch_attached(
IAlgoServices &algo, OrderKey k, ChildState st) {
me->algo = &algo;
set_parameters(algo, algo.definition().custom_def().prorata());
me->child_key = k;
me->hedged_fills = st.total_fills;
algo.subscribe_book(SecurityKey(algo.security().id()));
on_child_change(k, st);
}
// void ProRataAlgo::do_launch_attached(
// IAlgoServices &algo, OrderKey k, ChildState st) {
// me->algo = &algo;
// set_parameters(algo, algo.definition().custom_def().prorata());
// me->child_key = k;
// me->hedged_fills = st.total_fills;
// algo.subscribe_book(SecurityKey(algo.security().id()));
// on_child_change(k, st);
// }
void ProRataAlgo::on_book(SecurityKey, const Book &b) {
me->last_book = b;
// void ProRataAlgo::on_book(SecurityKey, const Book &b) {
// me->last_book = b;
LOG(OrderMan, Diag, "ProRataAlgo order {} on book. BID: {}@{} ASK: {}@{}",
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
b.side(api::SIDE_SELL)[0].price());
// LOG(OrderMan, Diag, "ProRataAlgo order {} on book. BID: {}@{} ASK: {}@{}",
// algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
// b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
// b.side(api::SIDE_SELL)[0].price());
switch (me->status) {
case Status::WAITING_FOR_MD: {
auto other_side = securities::other_side(algo().definition().side());
auto &other_side_top = me->last_book.side(other_side)[0];
// switch (me->status) {
// case Status::WAITING_FOR_MD: {
// auto other_side = securities::other_side(algo().definition().side());
// auto &other_side_top = me->last_book.side(other_side)[0];
if (other_side_top.size() > 0) {
me->status = Status::WORKING;
me->book_at_inception = b;
LOG(OrderMan, Diag,
"ProRataAlgo order {} receiving first book: {} Target: {}",
algo().order_key().value(), me->fill_limit.value(),
other_side_top.price());
}
} break;
case Status::WORKING:
check_overfill();
break;
case Status::DONE_WORKING:
check_overfill();
break;
case Status::GETTING_OUT:
break;
}
}
// if (other_side_top.size() > 0) {
// me->status = Status::WORKING;
// me->book_at_inception = b;
// LOG(OrderMan, Diag,
// "ProRataAlgo order {} receiving first book: {} Target: {}",
// algo().order_key().value(), me->fill_limit.value(),
// other_side_top.price());
// }
// } break;
// case Status::WORKING:
// check_overfill();
// break;
// case Status::DONE_WORKING:
// check_overfill();
// break;
// case Status::GETTING_OUT:
// break;
// }
// }
void ProRataAlgo::check_overfill() {
if (me->is_overfilled && me->status != Status::GETTING_OUT) {
if (me->stay_in) {
auto my_side = algo().definition().side();
auto other_side = securities::other_side(my_side);
// void ProRataAlgo::check_overfill() {
// if (me->is_overfilled && me->status != Status::GETTING_OUT) {
// if (me->stay_in) {
// auto my_side = algo().definition().side();
// auto other_side = securities::other_side(my_side);
auto &my_side_psz = me->last_book.side(my_side)[0];
auto &other_side_psz = me->last_book.side(other_side)[0];
bool market_moved = !securities::is_equal_or_better(
other_side, Price(other_side_psz.price()),
Price(me->book_at_inception.side(other_side)[0].price()));
bool on_market = securities::is_same(
Price(my_side_psz.price()), Price(algo().price()));
// auto &my_side_psz = me->last_book.side(my_side)[0];
// auto &other_side_psz = me->last_book.side(other_side)[0];
// bool market_moved = !securities::is_equal_or_better(
// other_side, Price(other_side_psz.price()),
// Price(me->book_at_inception.side(other_side)[0].price()));
// bool on_market = securities::is_same(
// Price(my_side_psz.price()), Price(algo().price()));
if (market_moved ||
(on_market && my_side_psz.size() <= me->stay_in_threshold.value()))
get_out();
} else
get_out();
}
}
// if (market_moved ||
// (on_market && my_side_psz.size() <=
// me->stay_in_threshold.value()))
// get_out();
// } else
// get_out();
// }
// }
void ProRataAlgo::scratch(std::pair<const OrderKey, Quantity> kk) {
auto cross_side = algo().definition().side();
Price scratch_price;
Quantity scratch_quan;
SpreaderUtils::traverse_depth_for_quantity(
me->last_book, cross_side, kk.second, scratch_price, scratch_quan);
// Quantity scratch_quan =
// Price(me->book_at_inception.side(other_side)[0].price())
scratch_price =
securities::worsen_price(cross_side, scratch_price, me->chase_diff);
// void ProRataAlgo::scratch(std::pair<const OrderKey, Quantity> kk) {
// auto cross_side = algo().definition().side();
// Price scratch_price;
// Quantity scratch_quan;
// SpreaderUtils::traverse_depth_for_quantity(
// me->last_book, cross_side, kk.second, scratch_price, scratch_quan);
// // Quantity scratch_quan =
// // Price(me->book_at_inception.side(other_side)[0].price())
// scratch_price =
// securities::worsen_price(cross_side, scratch_price, me->chase_diff);
Update update;
update.set_price(scratch_price.value());
update.set_quantity(kk.second.value());
algo().update_child(kk.first, update);
}
// Update update;
// update.set_price(scratch_price.value());
// update.set_quantity(kk.second.value());
// algo().update_child(kk.first, update);
// }
void ProRataAlgo::get_out() {
if (me->status != Status::GETTING_OUT) {
me->status = Status::GETTING_OUT;
algo().cancel_child(me->child_key);
if (me->scratch_on_overfill) {
for (auto &h : me->overfill_hedges)
scratch(h);
}
check_terminate();
}
}
// void ProRataAlgo::get_out() {
// if (me->status != Status::GETTING_OUT) {
// me->status = Status::GETTING_OUT;
// algo().cancel_child(me->child_key);
// if (me->scratch_on_overfill) {
// for (auto &h : me->overfill_hedges)
// scratch(h);
// }
// check_terminate();
// }
// }
void ProRataAlgo::standard_hedge(Price hedge_price, Quantity hedge_qty) {
auto hedge =
algo().launch_child(*me->hedge_template, hedge_price, hedge_qty);
me->hedges.push_back(hedge);
LOG(OrderMan, Diag,
"ProRataAlgo {}, launched new hedge in response to child fill. "
"Hedge: {} {}@{}",
algo().order_key().value(),
securities::side_name(algo().definition().side()), hedge_qty.value(),
hedge_price.value());
}
// void ProRataAlgo::standard_hedge(Price hedge_price, Quantity hedge_qty) {
// auto hedge =
// algo().launch_child(*me->hedge_template, hedge_price, hedge_qty);
// me->hedges.push_back(hedge);
// LOG(OrderMan, Diag,
// "ProRataAlgo {}, launched new hedge in response to child fill. "
// "Hedge: {} {}@{}",
// algo().order_key().value(),
// securities::side_name(algo().definition().side()), hedge_qty.value(),
// hedge_price.value());
// }
void ProRataAlgo::overfill_hedge(Price hedge_price, Quantity hedge_qty) {
auto hedge = algo().launch_child(
*me->overfill_hedge_template, hedge_price, hedge_qty);
me->overfill_hedges[hedge] = hedge_qty;
LOG(OrderMan, Diag,
"ProRataAlgo {}, launched new OVERFILL hedge in response to child fill. "
"Hedge: {} {}@{}",
algo().order_key().value(),
securities::side_name(algo().definition().side()), hedge_qty.value(),
hedge_price.value());
me->is_overfilled = true;
check_overfill();
}
// void ProRataAlgo::overfill_hedge(Price hedge_price, Quantity hedge_qty) {
// auto hedge = algo().launch_child(
// *me->overfill_hedge_template, hedge_price, hedge_qty);
// me->overfill_hedges[hedge] = hedge_qty;
// LOG(OrderMan, Diag,
// "ProRataAlgo {}, launched new OVERFILL hedge in response to child
// fill. " "Hedge: {} {}@{}", algo().order_key().value(),
// securities::side_name(algo().definition().side()), hedge_qty.value(),
// hedge_price.value());
// me->is_overfilled = true;
// check_overfill();
// }
void ProRataAlgo::on_child_change(OrderKey k, const ChildState &st) {
if (k == me->child_key) {
if (st.total_fills.value() > me->hedged_fills.value() &&
me->status == Status::WORKING) {
auto hedge_qty =
Quantity(st.total_fills.value() - me->hedged_fills.value());
auto hedge_price = securities::improve_price(
algo().definition().side(), st.last_price, me->hedge_price_diff);
if (me->hedged_fills.value() + hedge_qty.value() <=
me->fill_limit.value())
standard_hedge(hedge_price, hedge_qty);
else {
auto over = me->hedged_fills.value() + hedge_qty.value() -
me->fill_limit.value();
if (over < hedge_qty.value()) {
standard_hedge(hedge_price, Quantity(hedge_qty.value() - over));
overfill_hedge(hedge_price, Quantity(over));
} else
overfill_hedge(hedge_price, hedge_qty);
}
me->hedged_fills =
Quantity(hedge_qty.value() + me->hedged_fills.value());
}
algo().update_fill_state(st.total_fills, st.last_price);
switch (st.status) {
case ChildStatus::WORKING:
break;
case ChildStatus::COMPLETED:
LOG(OrderMan, Info,
"ProRataAlgo {}. Child is completed, waiting for hedges ",
algo().order_key().value());
me->status = Status::DONE_WORKING;
break;
case ChildStatus::CANCELED:
LOG(OrderMan, Info,
"ProRataAlgo {}. Child was canceled, waiting on hedges",
algo().order_key().value());
me->status = Status::DONE_WORKING;
break;
}
} else // it's a hedge
{
switch (st.status) {
case ChildStatus::WORKING:
break;
case ChildStatus::CANCELED:
case ChildStatus::COMPLETED:
if (auto it = std::find(me->hedges.begin(), me->hedges.end(), k);
it != me->hedges.end())
me->hedges.erase(it);
if (me->overfill_hedges.find(k) != me->overfill_hedges.end())
me->overfill_hedges.erase(k);
break;
}
}
check_terminate();
}
// void ProRataAlgo::on_child_change(OrderKey k, const ChildState &st) {
// if (k == me->child_key) {
// if (st.total_fills.value() > me->hedged_fills.value() &&
// me->status == Status::WORKING) {
// auto hedge_qty =
// Quantity(st.total_fills.value() - me->hedged_fills.value());
// auto hedge_price = securities::improve_price(
// algo().definition().side(), st.last_price,
// me->hedge_price_diff);
// if (me->hedged_fills.value() + hedge_qty.value() <=
// me->fill_limit.value())
// standard_hedge(hedge_price, hedge_qty);
// else {
// auto over = me->hedged_fills.value() + hedge_qty.value() -
// me->fill_limit.value();
// if (over < hedge_qty.value()) {
// standard_hedge(hedge_price, Quantity(hedge_qty.value() -
// over)); overfill_hedge(hedge_price, Quantity(over));
// } else
// overfill_hedge(hedge_price, hedge_qty);
// }
// me->hedged_fills =
// Quantity(hedge_qty.value() + me->hedged_fills.value());
// }
// algo().update_fill_state(st.total_fills, st.last_price);
// switch (st.status) {
// case ChildStatus::WORKING:
// break;
// case ChildStatus::COMPLETED:
// LOG(OrderMan, Info,
// "ProRataAlgo {}. Child is completed, waiting for hedges ",
// algo().order_key().value());
// me->status = Status::DONE_WORKING;
// break;
// case ChildStatus::CANCELED:
// LOG(OrderMan, Info,
// "ProRataAlgo {}. Child was canceled, waiting on hedges",
// algo().order_key().value());
// me->status = Status::DONE_WORKING;
// break;
// }
// } else // it's a hedge
// {
// switch (st.status) {
// case ChildStatus::WORKING:
// break;
// case ChildStatus::CANCELED:
// case ChildStatus::COMPLETED:
// if (auto it = std::find(me->hedges.begin(), me->hedges.end(), k);
// it != me->hedges.end())
// me->hedges.erase(it);
// if (me->overfill_hedges.find(k) != me->overfill_hedges.end())
// me->overfill_hedges.erase(k);
// break;
// }
// }
// check_terminate();
// }
void ProRataAlgo::do_update(const api::orderman::Update &update) {
if (me->status != Status::DONE_WORKING) {
LOG(OrderMan, Diag, "ProRataAlgo order {} receiving update: {}",
algo().order_key().value(), utils::pb::to_string(update));
// void ProRataAlgo::do_update(const api::orderman::Update &update) {
// if (me->status != Status::DONE_WORKING) {
// LOG(OrderMan, Diag, "ProRataAlgo order {} receiving update: {}",
// algo().order_key().value(), utils::pb::to_string(update));
if (update.has_custom() && !update.custom().has_prorata()) {
algo().terminate_error("Invalid update");
return;
}
// if (update.has_custom() && !update.custom().has_prorata()) {
// algo().terminate_error("Invalid update");
// return;
// }
Update child_update;
child_update.set_price(update.price());
child_update.set_quantity(update.quantity());
// Update child_update;
// child_update.set_price(update.price());
// child_update.set_quantity(update.quantity());
// process custom part of the udpate if any
if (update.has_custom()) {
auto &prorata_update = update.custom().prorata();
set_parameters(algo(), prorata_update);
// // process custom part of the udpate if any
// if (update.has_custom()) {
// auto &prorata_update = update.custom().prorata();
// set_parameters(algo(), prorata_update);
if (prorata_update.has_child())
*child_update.mutable_custom() = prorata_update.child();
if (prorata_update.has_hedge())
init_hedge_template(algo(), prorata_update.hedge(), false);
if (prorata_update.has_overfill_hedge())
init_hedge_template(algo(), prorata_update.overfill_hedge(), true);
}
algo().update_child(me->child_key, child_update);
} else {
LOG(OrderMan, Info,
"ProRataAlgo {}. Received update after child has completed. Ignoring "
"it.",
algo().order_key().value());
}
}
// if (prorata_update.has_child())
// *child_update.mutable_custom() = prorata_update.child();
// if (prorata_update.has_hedge())
// init_hedge_template(algo(), prorata_update.hedge(), false);
// if (prorata_update.has_overfill_hedge())
// init_hedge_template(algo(), prorata_update.overfill_hedge(),
// true);
// }
// algo().update_child(me->child_key, child_update);
// } else {
// LOG(OrderMan, Info,
// "ProRataAlgo {}. Received update after child has completed.
// Ignoring " "it.", algo().order_key().value());
// }
// }
void orderman::ProRataAlgo::init_hedge_template(
IBeforeLaunchAlgoServices &algo,
const api::orderman::CustomOrderDefinition &hedge_cdef,
bool overfill) {
api::orderman::OrderDefinition hedge_def;
hedge_def.CopyFrom(algo.definition());
// void orderman::ProRataAlgo::init_hedge_template(
// IBeforeLaunchAlgoServices &algo,
// const api::orderman::CustomOrderDefinition &hedge_cdef,
// bool overfill) {
// api::orderman::OrderDefinition hedge_def;
// hedge_def.CopyFrom(algo.definition());
hedge_def.set_side(securities::other_side(algo.definition().side()));
hedge_def.set_sub_type(ORDER_SUB_TYPE_HEDGING_LEG);
hedge_def.clear_attach_target_order_key();
*hedge_def.mutable_custom_def() = hedge_cdef;
if (!overfill)
me->hedge_template = &(algo.make_child_template(hedge_def));
else
me->overfill_hedge_template = &(algo.make_child_template(hedge_def));
}
// hedge_def.set_side(securities::other_side(algo.definition().side()));
// hedge_def.set_sub_type(ORDER_SUB_TYPE_HEDGING_LEG);
// hedge_def.clear_attach_target_order_key();
// *hedge_def.mutable_custom_def() = hedge_cdef;
// if (!overfill)
// me->hedge_template = &(algo.make_child_template(hedge_def));
// else
// me->overfill_hedge_template = &(algo.make_child_template(hedge_def));
// }
void ProRataAlgo::set_parameters(
IBeforeLaunchAlgoServices &serv,
const api::orderman::ProRataOrderDefinition &prorata) {
me->hedge_price_diff = Price(
securities::tick_size(serv.security()).value() * prorata.hedge_ticks());
me->chase_diff = Price(
securities::tick_size(serv.security()).value() * prorata.chase_ticks());
// void ProRataAlgo::set_parameters(
// IBeforeLaunchAlgoServices &serv,
// const api::orderman::ProRataOrderDefinition &prorata) {
// me->hedge_price_diff = Price(
// securities::tick_size(serv.security()).value() *
// prorata.hedge_ticks());
// me->chase_diff = Price(
// securities::tick_size(serv.security()).value() *
// prorata.chase_ticks());
if (!prorata.has_fill_limit() ||
prorata.fill_limit().quantity_case() ==
bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET) {
algo().terminate_error("Invalid fill limit quantity, terminating");
return;
}
me->fill_limit = SpreaderUtils::_quantity_threshold(
prorata.fill_limit(), algo().quantity());
// if (!prorata.has_fill_limit() ||
// prorata.fill_limit().quantity_case() ==
// bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET) {
// algo().terminate_error("Invalid fill limit quantity, terminating");
// return;
// }
// me->fill_limit = SpreaderUtils::_quantity_threshold(
// prorata.fill_limit(), algo().quantity());
if (!prorata.has_stay_in_threshold() ||
prorata.stay_in_threshold().quantity_case() ==
bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET) {
algo().terminate_error("Invalid stay_in_threshold quantity, terminating");
return;
}
me->stay_in_threshold = SpreaderUtils::_quantity_threshold(
prorata.stay_in_threshold(), algo().quantity());
me->scratch_on_overfill = prorata.scratch_on_overfill();
me->stay_in = prorata.stay_in();
}
// if (!prorata.has_stay_in_threshold() ||
// prorata.stay_in_threshold().quantity_case() ==
// bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET) {
// algo().terminate_error("Invalid stay_in_threshold quantity,
// terminating"); return;
// }
// me->stay_in_threshold = SpreaderUtils::_quantity_threshold(
// prorata.stay_in_threshold(), algo().quantity());
// me->scratch_on_overfill = prorata.scratch_on_overfill();
// me->stay_in = prorata.stay_in();
// }
void ProRataAlgo::check_terminate() {
LOG(OrderMan, Diag,
"ProRataAlgo {} check_terminate. State: {} Hedge count: {}",
algo().order_key(), log::CharPtrWrapper{status_name(me->status)},
me->hedges.size());
// void ProRataAlgo::check_terminate() {
// LOG(OrderMan, Diag,
// "ProRataAlgo {} check_terminate. State: {} Hedge count: {}",
// algo().order_key(), log::CharPtrWrapper{status_name(me->status)},
// me->hedges.size());
if (me->status == Status::DONE_WORKING && me->hedges.empty() &&
me->overfill_hedges.empty()) {
LOG(OrderMan, Info, "ProRataAlgo {}. Last hedge completed, terminating",
algo().order_key().value());
algo().complete();
}
}
// if (me->status == Status::DONE_WORKING && me->hedges.empty() &&
// me->overfill_hedges.empty()) {
// LOG(OrderMan, Info, "ProRataAlgo {}. Last hedge completed,
// terminating",
// algo().order_key().value());
// algo().complete();
// }
// }
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,47 +1,47 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
// ProRata initializes the child order as normal. Fills trigger one type of
// hedge, overfills (fills > limit) trigger another type of hedge. Overfills
// also optionally trigger overfill_hedge scratching and cancelling children
// dependent on book state
// // ProRata initializes the child order as normal. Fills trigger one type of
// // hedge, overfills (fills > limit) trigger another type of hedge. Overfills
// // also optionally trigger overfill_hedge scratching and cancelling children
// // dependent on book state
class ProRataAlgo : public IAlgoOrder {
public:
ProRataAlgo();
~ProRataAlgo();
// class ProRataAlgo : public IAlgoOrder {
// public:
// ProRataAlgo();
// ~ProRataAlgo();
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(const api::orderman::Update &) override;
void on_book(SecurityKey, const Book &) override;
void on_child_change(OrderKey, const ChildState &) override;
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(const api::orderman::Update &) override;
// void on_book(SecurityKey, const Book &) override;
// void on_child_change(OrderKey, const ChildState &) override;
private:
struct PImpl;
enum class Status;
std::unique_ptr<PImpl> me;
IAlgoServices &algo() const;
void set_parameters(
IBeforeLaunchAlgoServices &,
const api::orderman::ProRataOrderDefinition &);
void init_hedge_template(
IBeforeLaunchAlgoServices &,
const api::orderman::CustomOrderDefinition &,
bool);
void scratch(std::pair<const OrderKey, Quantity> kk);
void get_out();
void standard_hedge(Price hedge_price, Quantity hedge_qty);
void overfill_hedge(Price hedge_price, Quantity hedge_qty);
void check_overfill();
void check_terminate();
const char *status_name(Status st);
};
// private:
// struct PImpl;
// enum class Status;
// std::unique_ptr<PImpl> me;
// IAlgoServices &algo() const;
// void set_parameters(
// IBeforeLaunchAlgoServices &,
// const api::orderman::ProRataOrderDefinition &);
// void init_hedge_template(
// IBeforeLaunchAlgoServices &,
// const api::orderman::CustomOrderDefinition &,
// bool);
// void scratch(std::pair<const OrderKey, Quantity> kk);
// void get_out();
// void standard_hedge(Price hedge_price, Quantity hedge_qty);
// void overfill_hedge(Price hedge_price, Quantity hedge_qty);
// void check_overfill();
// void check_terminate();
// const char *status_name(Status st);
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
File diff suppressed because it is too large. Load diff
@@ -1,74 +1,77 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class ChildTemplate;
struct LegData;
// class ChildTemplate;
// struct LegData;
class PyramidAlgo : public IAlgoOrder {
public:
PyramidAlgo();
~PyramidAlgo();
struct PyramidAlgoParams {
// first one is the top
SecurityKey top_spread;
Quantity top_delete;
std::vector<SecurityKey> cover_spreads;
std::vector<Quantity> cover_payups;
};
// class PyramidAlgo : public IAlgoOrder {
// public:
// PyramidAlgo();
// ~PyramidAlgo();
// struct PyramidAlgoParams {
// // first one is the top
// SecurityKey top_spread;
// Quantity top_delete;
// std::vector<SecurityKey> cover_spreads;
// std::vector<Quantity> cover_payups;
// };
public:
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_child_change(OrderKey, ChildState const &) override;
IAlgoOrder::OnCancelResponse on_cancel() override;
// public:
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(api::orderman::Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_child_change(OrderKey, ChildState const &) override;
// IAlgoOrder::OnCancelResponse on_cancel() override;
private:
enum class Status;
enum class ExitPath;
// private:
// enum class Status;
// enum class ExitPath;
struct PImpl;
std::unique_ptr<PImpl> me;
// struct PImpl;
// std::unique_ptr<PImpl> me;
private:
IAlgoServices &_algo() const;
// converts parameters from the CustomOrderDefinition::dynamic
// to PyramidAlgoParams
static PyramidAlgoParams
_convert_params(const bts::api::orderman::CustomOrderDefinition &);
static std::tuple<PyramidAlgoParams, bool, std::string> _get_creation_params(
const bts::api::orderman::CustomOrderDefinition &,
IBeforeLaunchAlgoServices &);
static std::tuple<PyramidAlgoParams, bool, std::string> _get_update_params(
const api::orderman::Update &, const PyramidAlgoParams &curr_params);
ChildTemplate *_create_top_order_template(IBeforeLaunchAlgoServices &) const;
ChildTemplate *
_create_hedge_order_template(IBeforeLaunchAlgoServices &, size_t) const;
// acts on md, cognizant of the state
void _analyze_md_and_act();
void _switch_state(Status new_status);
void _send_hedges(Quantity filled_qty);
void _modify_top_spread_order();
// returns true is the price, qty changes
bool _copy_update(
const PyramidAlgoParams &algo_params, Price price, Quantity qty);
void _check_for_completion();
// cxl's the working top leg order (if any), detaches
// all hedges. if is_error then terminates algo, is
// is_cxl then cxl's algo, otherwise completes it
void _cxl_and_end(
ExitPath exit_path, const std::string &err_txt, bool detach_top = false);
bool _target_reached() const;
// true is all legs have recvd md
bool _md_recvd() const;
bool _working_state_met() const;
};
// private:
// IAlgoServices &_algo() const;
// // converts parameters from the CustomOrderDefinition::dynamic
// // to PyramidAlgoParams
// static PyramidAlgoParams
// _convert_params(const bts::api::orderman::CustomOrderDefinition &);
// static std::tuple<PyramidAlgoParams, bool, std::string>
// _get_creation_params(
// const bts::api::orderman::CustomOrderDefinition &,
// IBeforeLaunchAlgoServices &);
// static std::tuple<PyramidAlgoParams, bool, std::string>
// _get_update_params(
// const api::orderman::Update &, const PyramidAlgoParams &curr_params);
// ChildTemplate *_create_top_order_template(IBeforeLaunchAlgoServices &)
// const; ChildTemplate *
// _create_hedge_order_template(IBeforeLaunchAlgoServices &, size_t) const;
// // acts on md, cognizant of the state
// void _analyze_md_and_act();
// void _switch_state(Status new_status);
// void _send_hedges(Quantity filled_qty);
// void _modify_top_spread_order();
// // returns true is the price, qty changes
// bool _copy_update(
// const PyramidAlgoParams &algo_params, Price price, Quantity qty);
// void _check_for_completion();
// // cxl's the working top leg order (if any), detaches
// // all hedges. if is_error then terminates algo, is
// // is_cxl then cxl's algo, otherwise completes it
// void _cxl_and_end(
// ExitPath exit_path, const std::string &err_txt, bool detach_top =
// false);
// bool _target_reached() const;
// // true is all legs have recvd md
// bool _md_recvd() const;
// bool _working_state_met() const;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -23,14 +23,15 @@ namespace bts {
namespace orderman {
namespace dorman {
template <class OK, class ERR> using Result = std::variant<OK, ERR>;
template <class OK, class ERR>
using Result = std::variant<OK, ERR>;
Result<AlgoParams, OrderOpResult> bad_definition(const char *msg) {
return {OrderOpResult{ORDER_ERROR_INVALID_ORDER_DEFINITON, msg}};
}
// Validate DynamicOrderDefinition and construct parameter object
Result<AlgoParams, OrderOpResult>
to_dorman_params(const api::orderman::DynamicOrderDefinition &def) {
Result<AlgoParams, OrderOpResult> to_dorman_params(
const api::orderman::DynamicOrderDefinition &def) {
const auto &params = def.order_params();
AlgoParams res;
@@ -136,7 +137,7 @@ extern "C" void init_dorman_plugin(bts::SparkServices &srv) {
join_desc, []() { return std::make_unique<DormanAlgo>(); });
}
extern "C" void init_pyramid_plugin(bts::SparkServices &srv) {
extern "C" void init_pyramid_plugin(bts::SparkServices &) {
DynamicOrderDescriptor join_desc;
join_desc.set_dynamic_order_type("Pyramid");
@@ -159,11 +160,12 @@ extern "C" void init_pyramid_plugin(bts::SparkServices &srv) {
*join_desc.add_param_desc() = param_uint32("Spread 5 Payup");
*join_desc.add_param_desc() = param_order("Spread 5 Order");
srv.register_dynamic_algo_order(
join_desc, []() { return std::make_unique<orderman::PyramidAlgo>(); });
// srv.register_dynamic_algo_order(
// join_desc, []() { return std::make_unique<orderman::PyramidAlgo>();
// });
}
extern "C" void init_tascounter_plugin(bts::SparkServices &srv) {
extern "C" void init_tascounter_plugin(bts::SparkServices &) {
DynamicOrderDescriptor join_desc;
join_desc.set_dynamic_order_type("TASCounter");
@@ -199,12 +201,12 @@ extern "C" void init_tascounter_plugin(bts::SparkServices &srv) {
*join_desc.add_param_desc() = param_uint32("Settlement Start Seconds");
*join_desc.add_param_desc() = param_uint32("Settlement Period (seconds)");
srv.register_dynamic_algo_order(join_desc, []() {
return std::make_unique<orderman::TASCounterAlgo>();
});
// srv.register_dynamic_algo_order(join_desc, []() {
// return std::make_unique<orderman::TASCounterAlgo>();
// });
}
extern "C" void init_tas_plugin(bts::SparkServices &srv) {
extern "C" void init_tas_plugin(bts::SparkServices &) {
DynamicOrderDescriptor tas_desc;
tas_desc.set_dynamic_order_type("TAS");
@@ -216,8 +218,8 @@ extern "C" void init_tas_plugin(bts::SparkServices &srv) {
*tas_desc.add_param_desc() = param_uint32("Order " + idx + " Quantity");
*tas_desc.add_param_desc() = param_bool("Order " + idx + " Buy?");
}
srv.register_dynamic_algo_order(
tas_desc, []() { return std::make_unique<orderman::TASAlgo>(); });
// srv.register_dynamic_algo_order(
// tas_desc, []() { return std::make_unique<orderman::TASAlgo>(); });
}
extern "C" void init_limit_cover(bts::SparkServices &srv) {
@@ -251,8 +253,8 @@ extern "C" void init_on_signal_plugin(bts::SparkServices &srv) {
on_signal, []() { return std::make_unique<orderman::OnSignalAlgo>(); });
}
extern "C" void init_sniper_plugin(bts::SparkServices &srv) {
DynamicOrderDescriptor sniper;
extern "C" void init_sniper_plugin(bts::SparkServices &) {
DynamicOrderDescriptor sniper;
sniper.set_dynamic_order_type("Sniper");
// contracts
*sniper.add_param_desc() = param_string("File");
@@ -274,10 +276,10 @@ extern "C" void init_sniper_plugin(bts::SparkServices &srv) {
*sniper.add_param_desc() = param_uint32("Min Cumm Qty");
*sniper.add_param_desc() = param_bool("Shoot At Implieds");
// misc
*sniper.add_param_desc() = param_bool("Use Trade Summary");
*sniper.add_param_desc() = param_bool("Use Trade Summary");
srv.register_dynamic_algo_order(
sniper, []() { return std::make_unique<orderman::SniperAlgo>(); });
// srv.register_dynamic_algo_order(
// sniper, []() { return std::make_unique<orderman::SniperAlgo>(); });
}
extern "C" void init_spark_plugin(bts::SparkServices &srv) {
@@ -1,225 +1,225 @@
#include "lib/orderman/orders/algo/algo_orders/ScratchAlgo.h"
#include "lib/orderman/orders/algo/DynamicHelpers.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
// #include "lib/orderman/orders/algo/algo_orders/ScratchAlgo.h"
// #include "lib/orderman/orders/algo/DynamicHelpers.h"
// #include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
// #include "lib/orderman/order_factory/IOrderFactory.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using namespace api::orderman;
// using namespace api::orderman;
enum class ScratchAlgo::Status {
WAITING_FOR_MD,
WAITING_TO_SCRATCH,
SCRATCHED
};
// enum class ScratchAlgo::Status {
// WAITING_FOR_MD,
// WAITING_TO_SCRATCH,
// SCRATCHED
// };
struct ScratchAlgo::PImpl {
IAlgoServices *algo;
OrderDefinition definition;
// struct ScratchAlgo::PImpl {
// IAlgoServices *algo;
// OrderDefinition definition;
Quantity scratch_threshold;
Price chase_diff;
Status status = Status::WAITING_FOR_MD;
OrderKey child_key = null_order_key();
ChildTemplate *child_template = nullptr;
Book last_book;
Book book_at_inception;
Price scratch_price = Price(os::nan());
};
// Quantity scratch_threshold;
// Price chase_diff;
// Status status = Status::WAITING_FOR_MD;
// OrderKey child_key = null_order_key();
// ChildTemplate *child_template = nullptr;
// Book last_book;
// Book book_at_inception;
// Price scratch_price = Price(os::nan());
// };
ScratchAlgo::ScratchAlgo() : me(new PImpl()) {}
ScratchAlgo::~ScratchAlgo() {}
IAlgoServices &ScratchAlgo::algo() const { return *me->algo; }
// ScratchAlgo::ScratchAlgo() : me(new PImpl()) {}
// ScratchAlgo::~ScratchAlgo() {}
// IAlgoServices &ScratchAlgo::algo() const { return *me->algo; }
OrderOpResult ScratchAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
const auto &def = serv.definition();
bts_assert(def.has_custom_def());
bts_assert(def.custom_def().has_scratch());
// Validate the scratch part of the definition
auto &scratch = def.custom_def().scratch();
if (!scratch.has_child() &&
OrderKey(def.attach_target_order_key()) == null_order_key()) {
return OrderOpResult{
ORDER_ERROR_INVALID_ORDER_DEFINITON,
"Scratch order must have either a child order definition or an order "
"to attach to"};
}
if (!serv.is_attaching()) {
// if we are not attaching create a child template so we can
// quickly launch our child when the time comes.
auto child_def = def;
*child_def.mutable_custom_def() = def.custom_def().scratch().child();
me->child_template = &(serv.make_child_template(child_def));
}
return {};
}
// OrderOpResult ScratchAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
// const auto &def = serv.definition();
// bts_assert(def.has_custom_def());
// bts_assert(def.custom_def().has_scratch());
// // Validate the scratch part of the definition
// auto &scratch = def.custom_def().scratch();
// if (!scratch.has_child() &&
// OrderKey(def.attach_target_order_key()) == null_order_key()) {
// return OrderOpResult{
// ORDER_ERROR_INVALID_ORDER_DEFINITON,
// "Scratch order must have either a child order definition or an order "
// "to attach to"};
// }
// if (!serv.is_attaching()) {
// // if we are not attaching create a child template so we can
// // quickly launch our child when the time comes.
// auto child_def = def;
// *child_def.mutable_custom_def() = def.custom_def().scratch().child();
// me->child_template = &(serv.make_child_template(child_def));
// }
// return {};
// }
void ScratchAlgo::do_launch(IAlgoServices &algo) {
me->algo = &algo;
update_scratch_threshold();
bts_assert(me->child_template);
me->child_key =
algo.launch_child(*me->child_template, algo.price(), algo.quantity());
algo.subscribe_book(SecurityKey(algo.security().id()));
}
// void ScratchAlgo::do_launch(IAlgoServices &algo) {
// me->algo = &algo;
// update_scratch_threshold();
// bts_assert(me->child_template);
// me->child_key =
// algo.launch_child(*me->child_template, algo.price(), algo.quantity());
// algo.subscribe_book(SecurityKey(algo.security().id()));
// }
void ScratchAlgo::do_launch_attached(
IAlgoServices &algo, OrderKey k, ChildState st) {
me->algo = &algo;
update_scratch_threshold();
me->child_key = k;
algo.subscribe_book(SecurityKey(algo.security().id()));
on_child_change(k, st);
}
// void ScratchAlgo::do_launch_attached(
// IAlgoServices &algo, OrderKey k, ChildState st) {
// me->algo = &algo;
// update_scratch_threshold();
// me->child_key = k;
// algo.subscribe_book(SecurityKey(algo.security().id()));
// on_child_change(k, st);
// }
void ScratchAlgo::do_update(api::orderman::Update const &update) {
LOG(OrderMan, Diag, "ScratchOrder order {} receiving update: {}",
algo().order_key().value(), utils::pb::to_string(update));
// void ScratchAlgo::do_update(api::orderman::Update const &update) {
// LOG(OrderMan, Diag, "ScratchOrder order {} receiving update: {}",
// algo().order_key().value(), utils::pb::to_string(update));
if (update.has_custom() && !update.custom().has_scratch()) {
algo().terminate_error("Invalid update");
return;
}
// if (update.has_custom() && !update.custom().has_scratch()) {
// algo().terminate_error("Invalid update");
// return;
// }
Update child_update;
Price price_to_pass = get_price_to_pass(Price(update.price()));
child_update.set_price(price_to_pass.value());
child_update.set_quantity(update.quantity());
if (update.has_custom()) {
update_scratch_threshold();
auto &scratch_update = update.custom().scratch();
*child_update.mutable_custom() = scratch_update.child();
}
algo().update_child(me->child_key, child_update);
// Update child_update;
// Price price_to_pass = get_price_to_pass(Price(update.price()));
// child_update.set_price(price_to_pass.value());
// child_update.set_quantity(update.quantity());
// if (update.has_custom()) {
// update_scratch_threshold();
// auto &scratch_update = update.custom().scratch();
// *child_update.mutable_custom() = scratch_update.child();
// }
// algo().update_child(me->child_key, child_update);
// If we were not yet scratched it is possible we now can, since we
// could have changed the scratch_threshold
if (me->status == Status::WAITING_TO_SCRATCH)
try_to_scratch();
}
// // If we were not yet scratched it is possible we now can, since we
// // could have changed the scratch_threshold
// if (me->status == Status::WAITING_TO_SCRATCH)
// try_to_scratch();
// }
void ScratchAlgo::on_book(SecurityKey, Book const &b) {
me->last_book = b;
// void ScratchAlgo::on_book(SecurityKey, Book const &b) {
// me->last_book = b;
LOG(OrderMan, Diag, "ScratchOrder order {} on book. BID: {}@{} ASK: {}@{}",
algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
b.side(api::SIDE_SELL)[0].price());
// LOG(OrderMan, Diag, "ScratchOrder order {} on book. BID: {}@{} ASK: {}@{}",
// algo().order_key().value(), b.side(api::SIDE_BUY)[0].size(),
// b.side(api::SIDE_BUY)[0].price(), b.side(api::SIDE_SELL)[0].size(),
// b.side(api::SIDE_SELL)[0].price());
switch (me->status) {
case Status::WAITING_FOR_MD: {
auto other_side = securities::other_side(algo().definition().side());
auto &other_side_top = me->last_book.side(other_side)[0];
// switch (me->status) {
// case Status::WAITING_FOR_MD: {
// auto other_side = securities::other_side(algo().definition().side());
// auto &other_side_top = me->last_book.side(other_side)[0];
if (other_side_top.size() > 0) {
me->status = Status::WAITING_TO_SCRATCH;
me->book_at_inception = b;
LOG(OrderMan, Diag,
"ScratchOrder order {} receiving first book: {} Target: {}",
algo().order_key().value(), me->scratch_threshold.value(),
other_side_top.price());
try_to_scratch();
}
} break;
case Status::SCRATCHED:
break;
case Status::WAITING_TO_SCRATCH:
try_to_scratch();
break;
}
}
// if (other_side_top.size() > 0) {
// me->status = Status::WAITING_TO_SCRATCH;
// me->book_at_inception = b;
// LOG(OrderMan, Diag,
// "ScratchOrder order {} receiving first book: {} Target: {}",
// algo().order_key().value(), me->scratch_threshold.value(),
// other_side_top.price());
// try_to_scratch();
// }
// } break;
// case Status::SCRATCHED:
// break;
// case Status::WAITING_TO_SCRATCH:
// try_to_scratch();
// break;
// }
// }
void ScratchAlgo::try_to_scratch() {
bts_assert(me->status == Status::WAITING_TO_SCRATCH);
auto my_side = algo().definition().side();
auto other_side = securities::other_side(my_side);
// void ScratchAlgo::try_to_scratch() {
// bts_assert(me->status == Status::WAITING_TO_SCRATCH);
// auto my_side = algo().definition().side();
// auto other_side = securities::other_side(my_side);
Quantity my_quan;
auto &my_side_psz = me->last_book.side(my_side)[0];
auto &other_side_psz = me->last_book.side(other_side)[0];
auto other_side_price = Price(other_side_psz.price());
auto inception_other_side_price =
Price(me->book_at_inception.side(other_side)[0].price());
// Quantity my_quan;
// auto &my_side_psz = me->last_book.side(my_side)[0];
// auto &other_side_psz = me->last_book.side(other_side)[0];
// auto other_side_price = Price(other_side_psz.price());
// auto inception_other_side_price =
// Price(me->book_at_inception.side(other_side)[0].price());
bool market_moved = !securities::is_equal_or_better(
other_side, other_side_price, inception_other_side_price);
bool on_market =
securities::is_same(Price(my_side_psz.price()), Price(algo().price()));
if (market_moved ||
(on_market && my_side_psz.size() <= me->scratch_threshold.value())) {
Price scratch_price = securities::worsen_price(
other_side, inception_other_side_price, me->chase_diff);
LOG(OrderMan, Diag, "ScratchOrder order {} scratching: {}@{}",
algo().order_key().value(), algo().quantity().value(), scratch_price);
// bool market_moved = !securities::is_equal_or_better(
// other_side, other_side_price, inception_other_side_price);
// bool on_market =
// securities::is_same(Price(my_side_psz.price()), Price(algo().price()));
// if (market_moved ||
// (on_market && my_side_psz.size() <= me->scratch_threshold.value())) {
// Price scratch_price = securities::worsen_price(
// other_side, inception_other_side_price, me->chase_diff);
// LOG(OrderMan, Diag, "ScratchOrder order {} scratching: {}@{}",
// algo().order_key().value(), algo().quantity().value(), scratch_price);
// Time to scratch
Update child_update;
me->scratch_price = scratch_price;
child_update.set_price(scratch_price.value());
// we still want to try to scratch all of it
child_update.set_quantity(algo().quantity().value());
algo().update_child(me->child_key, child_update);
me->status = Status::SCRATCHED;
} else {
LOG(OrderMan, Diag,
"ScratchOrder order {} still waiting to scratch. Order: {}@{} "
"My side: {}@{}"
"Thresh : {}@{}",
algo().order_key().value(), algo().quantity().value(),
algo().price().value(), my_side_psz.size(), my_side_psz.price(),
me->scratch_threshold.value(), my_quan);
}
}
// // Time to scratch
// Update child_update;
// me->scratch_price = scratch_price;
// child_update.set_price(scratch_price.value());
// // we still want to try to scratch all of it
// child_update.set_quantity(algo().quantity().value());
// algo().update_child(me->child_key, child_update);
// me->status = Status::SCRATCHED;
// } else {
// LOG(OrderMan, Diag,
// "ScratchOrder order {} still waiting to scratch. Order: {}@{} "
// "My side: {}@{}"
// "Thresh : {}@{}",
// algo().order_key().value(), algo().quantity().value(),
// algo().price().value(), my_side_psz.size(), my_side_psz.price(),
// me->scratch_threshold.value(), my_quan);
// }
// }
Price ScratchAlgo::get_price_to_pass(Price price) const {
switch (me->status) {
case Status::WAITING_FOR_MD:
case Status::WAITING_TO_SCRATCH:
return price;
case Status::SCRATCHED:
return me->scratch_price;
default:
return Price{os::nan()};
}
}
// Price ScratchAlgo::get_price_to_pass(Price price) const {
// switch (me->status) {
// case Status::WAITING_FOR_MD:
// case Status::WAITING_TO_SCRATCH:
// return price;
// case Status::SCRATCHED:
// return me->scratch_price;
// default:
// return Price{os::nan()};
// }
// }
void ScratchAlgo::update_scratch_threshold() {
if (algo()
.definition()
.custom_def()
.scratch()
.scratch_threshold()
.quantity_case() ==
bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET) {
algo().terminate_error("Invalid quantity, terminating");
return;
}
me->scratch_threshold = SpreaderUtils::_quantity_threshold(
algo().definition().custom_def().scratch().scratch_threshold(),
algo().quantity());
me->chase_diff = Price(
securities::tick_size(algo().security()).value() *
algo().definition().custom_def().scratch().chase_ticks());
}
// void ScratchAlgo::update_scratch_threshold() {
// if (algo()
// .definition()
// .custom_def()
// .scratch()
// .scratch_threshold()
// .quantity_case() ==
// bts::spark::QuantityMeasure::QuantityCase::QUANTITY_NOT_SET) {
// algo().terminate_error("Invalid quantity, terminating");
// return;
// }
// me->scratch_threshold = SpreaderUtils::_quantity_threshold(
// algo().definition().custom_def().scratch().scratch_threshold(),
// algo().quantity());
// me->chase_diff = Price(
// securities::tick_size(algo().security()).value() *
// algo().definition().custom_def().scratch().chase_ticks());
// }
void ScratchAlgo::on_child_change(OrderKey, ChildState const &st) {
algo().update_fill_state(st.total_fills, st.last_price);
switch (st.status) {
case ChildStatus::WORKING:
return;
case ChildStatus::COMPLETED:
algo().complete();
return;
case ChildStatus::CANCELED:
algo().cancel("Unexpected child cancel");
return;
}
}
// void ScratchAlgo::on_child_change(OrderKey, ChildState const &st) {
// algo().update_fill_state(st.total_fills, st.last_price);
// switch (st.status) {
// case ChildStatus::WORKING:
// return;
// case ChildStatus::COMPLETED:
// algo().complete();
// return;
// case ChildStatus::CANCELED:
// algo().cancel("Unexpected child cancel");
// return;
// }
// }
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,36 +1,38 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
// Scratch monitors the cumulative size on the same side of the market of it's
// child if that size falls below the threshold it will cross the market, paying
// through enough levels to do all of it's size
// // Scratch monitors the cumulative size on the same side of the market of
// it's
// // child if that size falls below the threshold it will cross the market,
// paying
// // through enough levels to do all of it's size
class ScratchAlgo : public IAlgoOrder {
public:
ScratchAlgo();
~ScratchAlgo();
// class ScratchAlgo : public IAlgoOrder {
// public:
// ScratchAlgo();
// ~ScratchAlgo();
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_child_change(OrderKey, ChildState const &) override;
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(api::orderman::Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_child_change(OrderKey, ChildState const &) override;
private:
enum class Status;
struct PImpl;
std::unique_ptr<PImpl> me;
IAlgoServices &algo() const;
// private:
// enum class Status;
// struct PImpl;
// std::unique_ptr<PImpl> me;
// IAlgoServices &algo() const;
void update_scratch_threshold();
void try_to_scratch();
Price get_price_to_pass(Price price) const;
};
// void update_scratch_threshold();
// void try_to_scratch();
// Price get_price_to_pass(Price price) const;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
File diff suppressed because it is too large. Load diff
@@ -1,63 +1,63 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace api {
namespace orderman {
class CustomOrderDefinition;
}
} // namespace api
} // namespace bts
// namespace bts {
// namespace api {
// namespace orderman {
// class CustomOrderDefinition;
// }
// } // namespace api
// } // namespace bts
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class ChildTemplate;
class IBeforeLaunchAlgoServices;
struct SniperPolygon;
struct SecData;
// class ChildTemplate;
// class IBeforeLaunchAlgoServices;
// struct SniperPolygon;
// struct SecData;
class SniperAlgo : public IAlgoOrder {
public:
SniperAlgo();
~SniperAlgo();
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_trade_summary_signal(
SecurityKey,
api::Side /* aggressor */,
Price /* last_price */,
Quantity /* last_quantity */,
uint /* n_levels */) override;
void on_child_change(OrderKey, ChildState const &) override;
void on_status(SecurityKey, co2::TradingStatus) override;
void on_timer() override;
enum class FireImpetus;
// class SniperAlgo : public IAlgoOrder {
// public:
// SniperAlgo();
// ~SniperAlgo();
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(api::orderman::Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_trade_summary_signal(
// SecurityKey,
// api::Side /* aggressor */,
// Price /* last_price */,
// Quantity /* last_quantity */,
// uint /* n_levels */) override;
// void on_child_change(OrderKey, ChildState const &) override;
// void on_status(SecurityKey, co2::TradingStatus) override;
// void on_timer() override;
// enum class FireImpetus;
private:
struct PImpl;
std::unique_ptr<PImpl> me;
// private:
// struct PImpl;
// std::unique_ptr<PImpl> me;
private:
IAlgoServices &_algo() const;
bool _onetime_setup(
IBeforeLaunchAlgoServices &serv,
const bts::api::orderman::CustomOrderDefinition &cod);
bool _global_constraints_passed() const;
bool _is_fire_opp(const SniperPolygon&, api::Side) const;
bool _is_better_opp (const SniperPolygon* curr_best,
const SniperPolygon* new_eval,
api::Side side) const;
std::tuple<api::Side,SniperPolygon*> _find_best_polygon_to_fire(SecData&);
std::tuple<api::Side, SniperPolygon*> _find_best_polygon_to_fire();
void _compute_and_analyze(SecData &sec_data, FireImpetus);
void _fire_polygon_order(SniperPolygon& p, api::Side side, FireImpetus);
void _cxl_and_end(bool is_error, const std::string &err_msg);
};
// private:
// IAlgoServices &_algo() const;
// bool _onetime_setup(
// IBeforeLaunchAlgoServices &serv,
// const bts::api::orderman::CustomOrderDefinition &cod);
// bool _global_constraints_passed() const;
// bool _is_fire_opp(const SniperPolygon&, api::Side) const;
// bool _is_better_opp (const SniperPolygon* curr_best,
// const SniperPolygon* new_eval,
// api::Side side) const;
// std::tuple<api::Side,SniperPolygon*> _find_best_polygon_to_fire(SecData&);
// std::tuple<api::Side, SniperPolygon*> _find_best_polygon_to_fire();
// void _compute_and_analyze(SecData &sec_data, FireImpetus);
// void _fire_polygon_order(SniperPolygon& p, api::Side side, FireImpetus);
// void _cxl_and_end(bool is_error, const std::string &err_msg);
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
File diff suppressed because it is too large. Load diff
@@ -1,201 +1,206 @@
#include "lib/orderman/orders/algo/algo_orders/TASAlgo.h"
// #include "lib/orderman/orders/algo/algo_orders/TASAlgo.h"
#include "lib/domain_model/Security.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orderman_utils/Book.h"
#include "lib/orderman/orderman_utils/orderman_utils.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/datetime.h"
#include <stdint.h>
// #include "lib/domain_model/Security.h"
// #include "lib/orderman/order_factory/IOrderFactory.h"
// #include "lib/orderman/orderman_utils/Book.h"
// #include "lib/orderman/orderman_utils/orderman_utils.h"
// #include "lib/orderman/orders/algo/IAlgoServices.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
// #include "lib/utils/datetime.h"
// #include <stdint.h>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using namespace api::orderman;
// using namespace api::orderman;
enum class TASAlgo::Status { WAITING_FOR_SIGNAL, WORKING, DONE };
// enum class TASAlgo::Status { WAITING_FOR_SIGNAL, WORKING, DONE };
struct TASLegData {
api::Side side; // trading side
Price price;
Quantity quantity;
ChildTemplate *order_template = nullptr;
OrderKey order_key;
ChildState order_state;
TASLegData(
api::Side side,
Price price,
Quantity quantity,
ChildTemplate *order_template)
: side(side), price(price), quantity(quantity),
order_template(order_template), order_key(null_order_key()),
order_state() {
order_state.status = ChildStatus::WORKING;
}
};
// struct TASLegData {
// api::Side side; // trading side
// Price price;
// Quantity quantity;
// ChildTemplate *order_template = nullptr;
// OrderKey order_key;
// ChildState order_state;
// TASLegData(
// api::Side side,
// Price price,
// Quantity quantity,
// ChildTemplate *order_template)
// : side(side), price(price), quantity(quantity),
// order_template(order_template), order_key(null_order_key()),
// order_state() {
// order_state.status = ChildStatus::WORKING;
// }
// };
struct TASAlgo::PImpl {
Status state = Status::WAITING_FOR_SIGNAL;
IAlgoServices *algo_services = nullptr;
SecurityKey sec_key = SecurityKey(0);
TASAlgo::TASAlgoParams algo_params;
std::vector<TASLegData> legs;
bts::datetime::PTime start_time = bts::datetime::local_now();
PImpl() {}
TASLegData *find_leg_from_ord_key(OrderKey ord_key) {
for (auto &l : legs)
if (l.order_key == ord_key)
return &l;
return nullptr;
}
};
// struct TASAlgo::PImpl {
// Status state = Status::WAITING_FOR_SIGNAL;
// IAlgoServices *algo_services = nullptr;
// SecurityKey sec_key = SecurityKey(0);
// TASAlgo::TASAlgoParams algo_params;
// std::vector<TASLegData> legs;
// bts::datetime::PTime start_time = bts::datetime::local_now();
// PImpl() {}
// TASLegData *find_leg_from_ord_key(OrderKey ord_key) {
// for (auto &l : legs)
// if (l.order_key == ord_key)
// return &l;
// return nullptr;
// }
// };
TASAlgo::TASAlgo() : me(new PImpl()) {}
TASAlgo::~TASAlgo() {}
// initialization /////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
OrderOpResult TASAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
auto &def = serv.definition();
if (def.has_custom_def() && def.custom_def().has_dynamic()) {
const auto &sec = serv.security();
me->sec_key = SecurityKey(sec.id());
me->algo_params = _convert_params(def.custom_def());
if (me->algo_params.child_orders.size() > 0) {
for (const auto &t : me->algo_params.child_orders) {
Price price = std::get<0>(t);
Quantity quantity = std::get<1>(t);
api::Side side = std::get<2>(t);
auto *ordt =
_create_order_template(serv, SecurityKey(sec.id()), side);
me->legs.emplace_back(side, price, quantity, ordt);
}
return {};
}
}
return {
OrderError::ORDER_ERROR_INVALID_PARAMETER,
std::string("Parameters are not well formed")};
}
void TASAlgo::do_launch(IAlgoServices &algo_services) {
me->algo_services = &algo_services;
_algo().subscribe_status(me->sec_key);
//_algo().subscribe_timer();
}
void TASAlgo::do_launch_attached(
IAlgoServices &algo_services, OrderKey, ChildState) {
me->algo_services = &algo_services;
_cxl_and_end(true, "TAS Algo can not be attached to working orders");
}
// updates ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void TASAlgo::do_update(const api::orderman::Update &) {
LOG(OrderMan, Warn,
"TASAlgo::do_update order:{} state:{} update not allowed ",
_algo().order_key().value(), static_cast<int>(me->state));
}
// callbacks //////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void TASAlgo::on_book(SecurityKey, const Book &) {}
void TASAlgo::on_child_change(OrderKey key, ChildState const &st) {
LOG(OrderMan, Info,
"TASAlgo::on_child_change key:{} child_key:{} state:{} status:{} "
"total_fills:{} last_price:{}",
_algo().order_key(), key, static_cast<int>(me->state),
static_cast<int>(st.status), st.total_fills, st.last_price);
TASLegData *ld_ptr = me->find_leg_from_ord_key(key);
if (ld_ptr != nullptr)
ld_ptr->order_state = st;
for (const auto &l : me->legs)
if (l.order_state.status == ChildStatus::WORKING)
return;
_cxl_and_end(false, "");
}
void TASAlgo::on_status(SecurityKey key, co2::TradingStatus status) {
if (key == me->sec_key &&
status == co2::TradingStatus::TRADINGSTATUS_PRE_OPEN) {
switch (me->state) {
case TASAlgo::Status::WAITING_FOR_SIGNAL:
for (auto &l : me->legs) {
l.order_key =
_algo().launch_child(*l.order_template, l.price, l.quantity);
LOG(OrderMan, Info,
"TASAlgo::on_status launching order key:{} "
"child_key:{} price:{} qty:{}",
_algo().order_key(), l.order_key, l.price, l.quantity);
}
_switch_state(TASAlgo::Status::WORKING);
break;
case TASAlgo::Status::WORKING:
case TASAlgo::Status::DONE:
break;
}
}
}
void TASAlgo::on_timer() {
if (bts::datetime::msecs_since(me->start_time) > 10000)
on_status(me->sec_key, co2::TradingStatus::TRADINGSTATUS_PRE_OPEN);
}
IAlgoServices &TASAlgo::_algo() const { return *me->algo_services; }
///////////////////////////////////////////////////////////////////////////////
// stateless conversion ///////////////////////////////////////////////////////
TASAlgo::TASAlgoParams
TASAlgo::_convert_params(const bts::api::orderman::CustomOrderDefinition &cod) {
const DynamicOrderDefinition &def = cod.dynamic();
const auto &params = def.order_params();
TASAlgo::TASAlgoParams rv;
for (size_t i = 0; i < 1024; i++) {
auto idx = std::to_string(i + 1);
auto price = get_if<double>(params, "Order " + idx + " Price");
auto qty = get_if<uint32_t>(params, "Order " + idx + " Quantity");
auto is_buy = get_if<bool>(params, "Order " + idx + " Buy?");
if (price.has_value() && qty.has_value() && qty.value() != 0) {
api::Side side =
is_buy.has_value()
? (is_buy.value() ? api::Side::SIDE_BUY : api::Side::SIDE_SELL)
: api::Side::SIDE_SELL;
rv.child_orders.emplace_back(
Price(price.value()), Quantity(qty.value()), side);
}
}
return rv;
}
ChildTemplate *TASAlgo::_create_order_template(
IBeforeLaunchAlgoServices &serv, SecurityKey key, api::Side side) {
OrderDefinition order_def = serv.definition();
order_def.set_security_key(key.value());
order_def.set_side(side);
order_def.mutable_custom_def()->mutable_limit()->MergeFrom(
api::orderman::LimitOrderDefinition());
return &(serv.make_child_template(order_def));
}
void TASAlgo::_switch_state(TASAlgo::Status new_state) {
switch (new_state) {
case (TASAlgo::Status::WAITING_FOR_SIGNAL):
bts_assert(false);
break;
case (TASAlgo::Status::WORKING):
bts_assert(me->state == TASAlgo::Status::WAITING_FOR_SIGNAL);
break;
case (TASAlgo::Status::DONE):
bts_assert(me->state != TASAlgo::Status::DONE);
break;
}
LOG(OrderMan, Info, "TASAlgo::_switch_state key:{} {}->{} ",
_algo().order_key(), static_cast<int>(me->state),
static_cast<int>(new_state));
me->state = new_state;
}
// TASAlgo::TASAlgo() : me(new PImpl()) {}
// TASAlgo::~TASAlgo() {}
// // initialization
// /////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// OrderOpResult TASAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
// auto &def = serv.definition();
// if (def.has_custom_def() && def.custom_def().has_dynamic()) {
// const auto &sec = serv.security();
// me->sec_key = SecurityKey(sec.id());
// me->algo_params = _convert_params(def.custom_def());
// if (me->algo_params.child_orders.size() > 0) {
// for (const auto &t : me->algo_params.child_orders) {
// Price price = std::get<0>(t);
// Quantity quantity = std::get<1>(t);
// api::Side side = std::get<2>(t);
// auto *ordt =
// _create_order_template(serv, SecurityKey(sec.id()), side);
// me->legs.emplace_back(side, price, quantity, ordt);
// }
// return {};
// }
// }
// return {
// OrderError::ORDER_ERROR_INVALID_PARAMETER,
// std::string("Parameters are not well formed")};
// }
// void TASAlgo::do_launch(IAlgoServices &algo_services) {
// me->algo_services = &algo_services;
// _algo().subscribe_status(me->sec_key);
// //_algo().subscribe_timer();
// }
// void TASAlgo::do_launch_attached(
// IAlgoServices &algo_services, OrderKey, ChildState) {
// me->algo_services = &algo_services;
// _cxl_and_end(true, "TAS Algo can not be attached to working orders");
// }
// // updates
// ////////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// void TASAlgo::do_update(const api::orderman::Update &) {
// LOG(OrderMan, Warn,
// "TASAlgo::do_update order:{} state:{} update not allowed ",
// _algo().order_key().value(), static_cast<int>(me->state));
// }
// // callbacks
// //////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// void TASAlgo::on_book(SecurityKey, const Book &) {}
// void TASAlgo::on_child_change(OrderKey key, ChildState const &st) {
// LOG(OrderMan, Info,
// "TASAlgo::on_child_change key:{} child_key:{} state:{} status:{} "
// "total_fills:{} last_price:{}",
// _algo().order_key(), key, static_cast<int>(me->state),
// static_cast<int>(st.status), st.total_fills, st.last_price);
// TASLegData *ld_ptr = me->find_leg_from_ord_key(key);
// if (ld_ptr != nullptr)
// ld_ptr->order_state = st;
// for (const auto &l : me->legs)
// if (l.order_state.status == ChildStatus::WORKING)
// return;
// _cxl_and_end(false, "");
// }
// void TASAlgo::on_status(SecurityKey key, co2::TradingStatus status) {
// if (key == me->sec_key &&
// status == co2::TradingStatus::TRADINGSTATUS_PRE_OPEN) {
// switch (me->state) {
// case TASAlgo::Status::WAITING_FOR_SIGNAL:
// for (auto &l : me->legs) {
// l.order_key =
// _algo().launch_child(*l.order_template, l.price, l.quantity);
// LOG(OrderMan, Info,
// "TASAlgo::on_status launching order key:{} "
// "child_key:{} price:{} qty:{}",
// _algo().order_key(), l.order_key, l.price, l.quantity);
// }
// _switch_state(TASAlgo::Status::WORKING);
// break;
// case TASAlgo::Status::WORKING:
// case TASAlgo::Status::DONE:
// break;
// }
// }
// }
// void TASAlgo::on_timer() {
// if (bts::datetime::msecs_since(me->start_time) > 10000)
// on_status(me->sec_key, co2::TradingStatus::TRADINGSTATUS_PRE_OPEN);
// }
// IAlgoServices &TASAlgo::_algo() const { return *me->algo_services; }
// ///////////////////////////////////////////////////////////////////////////////
// // stateless conversion
// ///////////////////////////////////////////////////////
// TASAlgo::TASAlgoParams
// TASAlgo::_convert_params(const bts::api::orderman::CustomOrderDefinition
// &cod) {
// const DynamicOrderDefinition &def = cod.dynamic();
// const auto &params = def.order_params();
// TASAlgo::TASAlgoParams rv;
// for (size_t i = 0; i < 1024; i++) {
// auto idx = std::to_string(i + 1);
// auto price = get_if<double>(params, "Order " + idx + " Price");
// auto qty = get_if<uint32_t>(params, "Order " + idx + " Quantity");
// auto is_buy = get_if<bool>(params, "Order " + idx + " Buy?");
// if (price.has_value() && qty.has_value() && qty.value() != 0) {
// api::Side side =
// is_buy.has_value()
// ? (is_buy.value() ? api::Side::SIDE_BUY :
// api::Side::SIDE_SELL) : api::Side::SIDE_SELL;
// rv.child_orders.emplace_back(
// Price(price.value()), Quantity(qty.value()), side);
// }
// }
// return rv;
// }
// ChildTemplate *TASAlgo::_create_order_template(
// IBeforeLaunchAlgoServices &serv, SecurityKey key, api::Side side) {
// OrderDefinition order_def = serv.definition();
// order_def.set_security_key(key.value());
// order_def.set_side(side);
// order_def.mutable_custom_def()->mutable_limit()->MergeFrom(
// api::orderman::LimitOrderDefinition());
// return &(serv.make_child_template(order_def));
// }
// void TASAlgo::_switch_state(TASAlgo::Status new_state) {
// switch (new_state) {
// case (TASAlgo::Status::WAITING_FOR_SIGNAL):
// bts_assert(false);
// break;
// case (TASAlgo::Status::WORKING):
// bts_assert(me->state == TASAlgo::Status::WAITING_FOR_SIGNAL);
// break;
// case (TASAlgo::Status::DONE):
// bts_assert(me->state != TASAlgo::Status::DONE);
// break;
// }
// LOG(OrderMan, Info, "TASAlgo::_switch_state key:{} {}->{} ",
// _algo().order_key(), static_cast<int>(me->state),
// static_cast<int>(new_state));
// me->state = new_state;
// }
void TASAlgo::_cxl_and_end(bool is_error, const std::string &err_msg) {
if (is_error)
_algo().terminate_error(err_msg);
else
_algo().complete();
_switch_state(TASAlgo::Status::DONE);
}
// void TASAlgo::_cxl_and_end(bool is_error, const std::string &err_msg) {
// if (is_error)
// _algo().terminate_error(err_msg);
// else
// _algo().complete();
// _switch_state(TASAlgo::Status::DONE);
// }
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,46 +1,47 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class ChildTemplate;
struct LegData;
// class ChildTemplate;
// struct LegData;
class TASAlgo : public IAlgoOrder {
public:
TASAlgo();
~TASAlgo();
struct TASAlgoParams {
std::vector<std::tuple<Price, Quantity, api::Side>> child_orders;
};
// class TASAlgo : public IAlgoOrder {
// public:
// TASAlgo();
// ~TASAlgo();
// struct TASAlgoParams {
// std::vector<std::tuple<Price, Quantity, api::Side>> child_orders;
// };
public:
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_child_change(OrderKey, ChildState const &) override;
void on_status(SecurityKey, co2::TradingStatus) override;
void on_timer() override;
// public:
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(api::orderman::Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_child_change(OrderKey, ChildState const &) override;
// void on_status(SecurityKey, co2::TradingStatus) override;
// void on_timer() override;
private:
enum class Status;
struct PImpl;
std::unique_ptr<PImpl> me;
// private:
// enum class Status;
// struct PImpl;
// std::unique_ptr<PImpl> me;
private:
IAlgoServices &_algo() const;
static TASAlgoParams
_convert_params(const bts::api::orderman::CustomOrderDefinition &);
static ChildTemplate *
_create_order_template(IBeforeLaunchAlgoServices &, SecurityKey, api::Side);
// acts on md, cognizant of the state
void _switch_state(Status new_status);
void _cxl_and_end(bool is_error, const std::string &err_msg);
};
// private:
// IAlgoServices &_algo() const;
// static TASAlgoParams
// _convert_params(const bts::api::orderman::CustomOrderDefinition &);
// static ChildTemplate *
// _create_order_template(IBeforeLaunchAlgoServices &, SecurityKey,
// api::Side);
// // acts on md, cognizant of the state
// void _switch_state(Status new_status);
// void _cxl_and_end(bool is_error, const std::string &err_msg);
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,468 +1,488 @@
#include "lib/orderman/orders/algo/algo_orders/TASCounterAlgo.h"
#include "lib/domain_model/Security.h"
#include "lib/orderman/actors/SODPosition.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orderman_utils/Book.h"
#include "lib/orderman/orderman_utils/orderman_utils.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/datetime.h"
#include <google/protobuf/timestamp.pb.h>
#include <google/protobuf/util/time_util.h>
#include <stdint.h>
// #include "lib/orderman/orders/algo/algo_orders/TASCounterAlgo.h"
// #include "lib/domain_model/Security.h"
// #include "lib/orderman/actors/SODPosition.h"
// #include "lib/orderman/order_factory/IOrderFactory.h"
// #include "lib/orderman/orderman_utils/Book.h"
// #include "lib/orderman/orderman_utils/orderman_utils.h"
// #include "lib/orderman/orders/algo/IAlgoServices.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
// #include "lib/utils/datetime.h"
// #include <google/protobuf/timestamp.pb.h>
// #include <google/protobuf/util/time_util.h>
// #include <stdint.h>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using namespace api::orderman;
using namespace google::protobuf;
// using namespace api::orderman;
// using namespace google::protobuf;
enum class TASCounterAlgo::Status { WORKING, DONE };
// enum class TASCounterAlgo::Status { WORKING, DONE };
// book and trade data for each of the 6 contracts as well as the TAS contracts
// (the vwap of the TAS contracts is not used, but for uniformity is kept in the
// the same structure)
struct ContractMarketData {
uint32_t tot_vol = 0;
double running_sum = 0;
Book book;
double estimated_settle = 0.0;
void on_trade(const TickerUpdateEvent::Trade &t) {
tot_vol += t.quantity.value();
running_sum += t.price.value() * t.quantity.value();
}
const double vwap() {
return tot_vol == 0 ? os::nan() : running_sum / tot_vol;
}
};
// the input for the t1 front, second and third orders, as well as the template
// and working order key are kept here
struct ContractAutoOrder {
// inputs
uint32_t n_secs = 0;
uint32_t qty = 0;
bool enabled = false;
// dont know yet which side will be sent
ChildTemplate *buy_ord = nullptr;
ChildTemplate *sell_ord = nullptr;
// computed input
// the is AlgoData::settle_stop - n_secs
Timestamp algo_start;
// output
std::optional<OrderKey> tas_order = std::nullopt;
};
// // book and trade data for each of the 6 contracts as well as the TAS
// contracts
// // (the vwap of the TAS contracts is not used, but for uniformity is kept in
// the
// // the same structure)
// struct ContractMarketData {
// uint32_t tot_vol = 0;
// double running_sum = 0;
// Book book;
// double estimated_settle = 0.0;
// void on_trade(const TickerUpdateEvent::Trade &t) {
// tot_vol += t.quantity.value();
// running_sum += t.price.value() * t.quantity.value();
// }
// const double vwap() {
// return tot_vol == 0 ? os::nan() : running_sum / tot_vol;
// }
// };
// // the input for the t1 front, second and third orders, as well as the
// template
// // and working order key are kept here
// struct ContractAutoOrder {
// // inputs
// uint32_t n_secs = 0;
// uint32_t qty = 0;
// bool enabled = false;
// // dont know yet which side will be sent
// ChildTemplate *buy_ord = nullptr;
// ChildTemplate *sell_ord = nullptr;
// // computed input
// // the is AlgoData::settle_stop - n_secs
// Timestamp algo_start;
// // output
// std::optional<OrderKey> tas_order = std::nullopt;
// };
struct AlgoData {
double tick_size;
Timestamp settle_start;
Timestamp settle_stop;
ContractMarketData t1_front, t1_second, t1_third, t2_front, t2_second,
t3_front;
ContractMarketData t1_front_tas, t1_second_tas, t1_third_tas;
ContractAutoOrder t1_front_order, t1_second_order, t1_third_order;
DynamicOrderState out_state;
};
// struct AlgoData {
// double tick_size;
// Timestamp settle_start;
// Timestamp settle_stop;
// ContractMarketData t1_front, t1_second, t1_third, t2_front, t2_second,
// t3_front;
// ContractMarketData t1_front_tas, t1_second_tas, t1_third_tas;
// ContractAutoOrder t1_front_order, t1_second_order, t1_third_order;
// DynamicOrderState out_state;
// };
struct TASCounterAlgo::PImpl {
// reporting
uint16_t reporting_period_msecs = 500;
bts::datetime::PTime last_report_time = bts::datetime::now();
// immutable state
IAlgoServices *algo_services = nullptr;
// mutable state
AlgoData data;
// to avoid lookups on md and ticker callbacks
std::map<SecurityKey, ContractMarketData *> md_map;
Status state = Status::WORKING;
};
// struct TASCounterAlgo::PImpl {
// // reporting
// uint16_t reporting_period_msecs = 500;
// bts::datetime::PTime last_report_time = bts::datetime::now();
// // immutable state
// IAlgoServices *algo_services = nullptr;
// // mutable state
// AlgoData data;
// // to avoid lookups on md and ticker callbacks
// std::map<SecurityKey, ContractMarketData *> md_map;
// Status state = Status::WORKING;
// };
// fwd decls
void _copy_output(AlgoData &);
bool _copy_settle_times(
AlgoData &, const Map<std::string, DynamicOrderParam> &);
void _copy_order_data(AlgoData &, const Map<std::string, DynamicOrderParam> &);
bool _setup_data(
IBeforeLaunchAlgoServices &,
AlgoData &,
std::map<SecurityKey, ContractMarketData *> &);
double round_to_nearest(double price, double tick_size);
long long t1_minus_t2_usecs(const Timestamp &, const Timestamp &);
long long t_minus_now_usecs(const google::protobuf::Timestamp &);
bool trade_within_time_win(
const Timestamp &, const Timestamp &, const TickerUpdateEvent::Trade &t);
bool now_within_time_win(const Timestamp &, const Timestamp &);
// // fwd decls
// void _copy_output(AlgoData &);
// bool _copy_settle_times(
// AlgoData &, const Map<std::string, DynamicOrderParam> &);
// void _copy_order_data(AlgoData &, const Map<std::string, DynamicOrderParam>
// &); bool _setup_data(
// IBeforeLaunchAlgoServices &,
// AlgoData &,
// std::map<SecurityKey, ContractMarketData *> &);
// double round_to_nearest(double price, double tick_size);
// long long t1_minus_t2_usecs(const Timestamp &, const Timestamp &);
// long long t_minus_now_usecs(const google::protobuf::Timestamp &);
// bool trade_within_time_win(
// const Timestamp &, const Timestamp &, const TickerUpdateEvent::Trade &t);
// bool now_within_time_win(const Timestamp &, const Timestamp &);
TASCounterAlgo::TASCounterAlgo() : me(new PImpl()) {}
TASCounterAlgo::~TASCounterAlgo() {}
// initialization /////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
OrderOpResult
TASCounterAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
if (_setup_data(serv, me->data, me->md_map))
return {};
return {
OrderError::ORDER_ERROR_INVALID_PARAMETER,
std::string("Parameters are not well formed")};
}
void TASCounterAlgo::do_launch(IAlgoServices &algo_services) {
me->algo_services = &algo_services;
algo_services.subscribe_timer();
for (const auto &kp : me->md_map) {
algo_services.subscribe_book(kp.first);
algo_services.subscribe_ticker(kp.first);
}
}
void TASCounterAlgo::do_launch_attached(
IAlgoServices &algo_services, OrderKey, ChildState) {
// not allowed
me->algo_services = &algo_services;
}
// updates ////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void TASCounterAlgo::do_update(const api::orderman::Update &u) {
if (me->state == Status::DONE)
return;
const auto &p = u.custom().dynamic().order_params();
// only order data change is allowed
_copy_order_data(me->data, p);
_check_and_send_order();
}
// callbacks //////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
void TASCounterAlgo::on_book(SecurityKey k, const Book &b) {
if (me->state == Status::DONE)
return;
const auto &it = me->md_map.find(k);
if (it != me->md_map.end()) {
it->second->book = b;
_check_and_send_order();
}
}
void TASCounterAlgo::on_ticker(SecurityKey k, const TradeVector &v) {
if (me->state == Status::DONE)
return;
const auto &it = me->md_map.find(k);
if (it != me->md_map.end()) {
auto contract_md = it->second;
for (const auto &t : v)
if (_within_settle()) {
contract_md->on_trade(t);
}
_check_and_send_order();
}
}
// TASCounterAlgo::TASCounterAlgo() : me(new PImpl()) {}
// TASCounterAlgo::~TASCounterAlgo() {}
// // initialization
// /////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// OrderOpResult
// TASCounterAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
// if (_setup_data(serv, me->data, me->md_map))
// return {};
// return {
// OrderError::ORDER_ERROR_INVALID_PARAMETER,
// std::string("Parameters are not well formed")};
// }
// void TASCounterAlgo::do_launch(IAlgoServices &algo_services) {
// me->algo_services = &algo_services;
// algo_services.subscribe_timer();
// for (const auto &kp : me->md_map) {
// algo_services.subscribe_book(kp.first);
// algo_services.subscribe_ticker(kp.first);
// }
// }
// void TASCounterAlgo::do_launch_attached(
// IAlgoServices &algo_services, OrderKey, ChildState) {
// // not allowed
// me->algo_services = &algo_services;
// }
// // updates
// ////////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// void TASCounterAlgo::do_update(const api::orderman::Update &u) {
// if (me->state == Status::DONE)
// return;
// const auto &p = u.custom().dynamic().order_params();
// // only order data change is allowed
// _copy_order_data(me->data, p);
// _check_and_send_order();
// }
// // callbacks
// //////////////////////////////////////////////////////////////////
// ///////////////////////////////////////////////////////////////////////////////
// void TASCounterAlgo::on_book(SecurityKey k, const Book &b) {
// if (me->state == Status::DONE)
// return;
// const auto &it = me->md_map.find(k);
// if (it != me->md_map.end()) {
// it->second->book = b;
// _check_and_send_order();
// }
// }
// void TASCounterAlgo::on_ticker(SecurityKey k, const TradeVector &v) {
// if (me->state == Status::DONE)
// return;
// const auto &it = me->md_map.find(k);
// if (it != me->md_map.end()) {
// auto contract_md = it->second;
// for (const auto &t : v)
// if (_within_settle()) {
// contract_md->on_trade(t);
// }
// _check_and_send_order();
// }
// }
// this only gets called on an explicit algo cancel
// from the outside
TASCounterAlgo::OnCancelResponse TASCounterAlgo::on_cancel() {
return IAlgoOrder::OnCancelResponse::HONOR;
}
// // this only gets called on an explicit algo cancel
// // from the outside
// TASCounterAlgo::OnCancelResponse TASCounterAlgo::on_cancel() {
// return IAlgoOrder::OnCancelResponse::HONOR;
// }
void TASCounterAlgo::on_timer() {
if (me->state == Status::DONE)
return;
_check_and_send_order();
if (bts::datetime::msecs_since(me->last_report_time) >=
me->reporting_period_msecs) {
me->last_report_time = bts::datetime::now();
_report_state();
}
// check for end and exit
if (t1_minus_t2_usecs(
util::TimeUtil::GetCurrentTime(), me->data.settle_stop) > 0) {
std::string msg = "session over";
_cxl_and_end(false, msg);
}
}
// void TASCounterAlgo::on_timer() {
// if (me->state == Status::DONE)
// return;
// _check_and_send_order();
// if (bts::datetime::msecs_since(me->last_report_time) >=
// me->reporting_period_msecs) {
// me->last_report_time = bts::datetime::now();
// _report_state();
// }
// // check for end and exit
// if (t1_minus_t2_usecs(
// util::TimeUtil::GetCurrentTime(), me->data.settle_stop) > 0) {
// std::string msg = "session over";
// _cxl_and_end(false, msg);
// }
// }
IAlgoServices &TASCounterAlgo::_algo() const { return *me->algo_services; }
// IAlgoServices &TASCounterAlgo::_algo() const { return *me->algo_services; }
void TASCounterAlgo::_switch_state(TASCounterAlgo::Status new_state) {
switch (new_state) {
case (TASCounterAlgo::Status::WORKING):
bts_assert(false);
break;
case (TASCounterAlgo::Status::DONE):
bts_assert(me->state == TASCounterAlgo::Status::WORKING);
break;
}
LOG(OrderMan, Info, "TASCounterAlgo::_switch_state key:{} {}->{} ",
_algo().order_key(), static_cast<int>(me->state),
static_cast<int>(new_state));
me->state = new_state;
}
// void TASCounterAlgo::_switch_state(TASCounterAlgo::Status new_state) {
// switch (new_state) {
// case (TASCounterAlgo::Status::WORKING):
// bts_assert(false);
// break;
// case (TASCounterAlgo::Status::DONE):
// bts_assert(me->state == TASCounterAlgo::Status::WORKING);
// break;
// }
// LOG(OrderMan, Info, "TASCounterAlgo::_switch_state key:{} {}->{} ",
// _algo().order_key(), static_cast<int>(me->state),
// static_cast<int>(new_state));
// me->state = new_state;
// }
void TASCounterAlgo::_cxl_and_end(bool is_error, const std::string &err_msg) {
if (me->state == Status::DONE)
return;
if (is_error)
_algo().terminate_error(err_msg);
else
_algo().complete();
_switch_state(TASCounterAlgo::Status::DONE);
}
// void TASCounterAlgo::_cxl_and_end(bool is_error, const std::string &err_msg)
// {
// if (me->state == Status::DONE)
// return;
// if (is_error)
// _algo().terminate_error(err_msg);
// else
// _algo().complete();
// _switch_state(TASCounterAlgo::Status::DONE);
// }
void TASCounterAlgo::_compute_settles() {
AlgoData &d = me->data;
d.t1_front.estimated_settle = d.t1_front.vwap();
d.t1_second.estimated_settle = d.t2_front.vwap() - d.t1_front.vwap();
d.t1_third.estimated_settle =
d.t3_front.vwap() - d.t1_front.vwap() - d.t1_second.vwap();
// round to closest value
d.t1_front.estimated_settle =
round_to_nearest(d.t1_front.estimated_settle, me->data.tick_size);
d.t1_second.estimated_settle =
round_to_nearest(d.t1_second.estimated_settle, me->data.tick_size);
d.t1_third.estimated_settle =
round_to_nearest(d.t1_third.estimated_settle, me->data.tick_size);
}
// void TASCounterAlgo::_compute_settles() {
// AlgoData &d = me->data;
// d.t1_front.estimated_settle = d.t1_front.vwap();
// d.t1_second.estimated_settle = d.t2_front.vwap() - d.t1_front.vwap();
// d.t1_third.estimated_settle =
// d.t3_front.vwap() - d.t1_front.vwap() - d.t1_second.vwap();
// // round to closest value
// d.t1_front.estimated_settle =
// round_to_nearest(d.t1_front.estimated_settle, me->data.tick_size);
// d.t1_second.estimated_settle =
// round_to_nearest(d.t1_second.estimated_settle, me->data.tick_size);
// d.t1_third.estimated_settle =
// round_to_nearest(d.t1_third.estimated_settle, me->data.tick_size);
// }
// the decision to send an order can be caused by
// 1. change in settings (=> time window, enabling orders etc)
// 2. book update (=> TAS book can result in order)
// 3. ticker (=> new estimated settlement)
// 4. time passing (=> in the window now)
void TASCounterAlgo::_check_and_send_order() {
_compute_settles();
auto should_send_tas_order =
[](const ContractMarketData &c, const ContractAutoOrder &o,
const ContractMarketData &tas_c, bool is_buy, double &out_price,
uint32_t &out_qty) -> bool {
// buying the tas at the offer or selling it at the bid
const auto &tas_opp_top =
is_buy ? tas_c.book.ask()[0] : tas_c.book.bid()[0];
const auto &c_same_top = is_buy ? c.book.bid()[0] : c.book.ask()[0];
if (tas_opp_top.size() > 0 && c_same_top.size() > 0) {
// delivery price as defined by where the TAS order is traded
double tas_delv_price = c.estimated_settle + tas_opp_top.price();
// if the delv price for the buy order is <= contract bid or
// or the delv price for the sell order is >= contract offer
// then we are buying at the bid or selling at the offer
double c_price = c_same_top.price();
if ((is_buy && tas_delv_price <= c_price) ||
(!is_buy && tas_delv_price >= c_price)) {
out_qty = std::min(o.qty, tas_opp_top.size());
out_price = tas_opp_top.price();
return true;
}
}
return false;
};
// // the decision to send an order can be caused by
// // 1. change in settings (=> time window, enabling orders etc)
// // 2. book update (=> TAS book can result in order)
// // 3. ticker (=> new estimated settlement)
// // 4. time passing (=> in the window now)
// void TASCounterAlgo::_check_and_send_order() {
// _compute_settles();
// auto should_send_tas_order =
// [](const ContractMarketData &c, const ContractAutoOrder &o,
// const ContractMarketData &tas_c, bool is_buy, double &out_price,
// uint32_t &out_qty) -> bool {
// // buying the tas at the offer or selling it at the bid
// const auto &tas_opp_top =
// is_buy ? tas_c.book.ask()[0] : tas_c.book.bid()[0];
// const auto &c_same_top = is_buy ? c.book.bid()[0] : c.book.ask()[0];
// if (tas_opp_top.size() > 0 && c_same_top.size() > 0) {
// // delivery price as defined by where the TAS order is traded
// double tas_delv_price = c.estimated_settle + tas_opp_top.price();
// // if the delv price for the buy order is <= contract bid or
// // or the delv price for the sell order is >= contract offer
// // then we are buying at the bid or selling at the offer
// double c_price = c_same_top.price();
// if ((is_buy && tas_delv_price <= c_price) ||
// (!is_buy && tas_delv_price >= c_price)) {
// out_qty = std::min(o.qty, tas_opp_top.size());
// out_price = tas_opp_top.price();
// return true;
// }
// }
// return false;
// };
auto _send_order_tn = [&](const ContractMarketData &c, ContractAutoOrder &o,
const ContractMarketData &tas_c) -> void {
if (o.enabled && o.n_secs > 0 && o.qty > 0 && !o.tas_order.has_value() &&
now_within_time_win(o.algo_start, me->data.settle_stop)) {
double price;
uint32_t qty;
if (should_send_tas_order(c, o, tas_c, true, price, qty)) {
o.tas_order =
_algo().launch_child(*o.buy_ord, Price(price), Quantity(qty));
} else if (should_send_tas_order(c, o, tas_c, false, price, qty)) {
o.tas_order =
_algo().launch_child(*o.sell_ord, Price(price), Quantity(qty));
}
}
};
_send_order_tn(
me->data.t1_front, me->data.t1_front_order, me->data.t1_front_tas);
_send_order_tn(
me->data.t1_second, me->data.t1_second_order, me->data.t1_second_tas);
_send_order_tn(
me->data.t1_third, me->data.t1_third_order, me->data.t1_third_tas);
}
// auto _send_order_tn = [&](const ContractMarketData &c, ContractAutoOrder
// &o,
// const ContractMarketData &tas_c) -> void {
// if (o.enabled && o.n_secs > 0 && o.qty > 0 && !o.tas_order.has_value()
// &&
// now_within_time_win(o.algo_start, me->data.settle_stop)) {
// double price;
// uint32_t qty;
// if (should_send_tas_order(c, o, tas_c, true, price, qty)) {
// o.tas_order =
// _algo().launch_child(*o.buy_ord, Price(price),
// Quantity(qty));
// } else if (should_send_tas_order(c, o, tas_c, false, price, qty)) {
// o.tas_order =
// _algo().launch_child(*o.sell_ord, Price(price),
// Quantity(qty));
// }
// }
// };
// _send_order_tn(
// me->data.t1_front, me->data.t1_front_order, me->data.t1_front_tas);
// _send_order_tn(
// me->data.t1_second, me->data.t1_second_order, me->data.t1_second_tas);
// _send_order_tn(
// me->data.t1_third, me->data.t1_third_order, me->data.t1_third_tas);
// }
void TASCounterAlgo::_report_state() {
_compute_settles();
_copy_output(me->data);
_algo().update_custom_state(me->data.out_state);
}
// void TASCounterAlgo::_report_state() {
// _compute_settles();
// _copy_output(me->data);
// _algo().update_custom_state(me->data.out_state);
// }
bool TASCounterAlgo::_within_settle() const {
return now_within_time_win(me->data.settle_start, me->data.settle_stop);
}
///////////////////////////////////////////////////////////////////////////////
// stateless conversion ///////////////////////////////////////////////////////
// bool TASCounterAlgo::_within_settle() const {
// return now_within_time_win(me->data.settle_start, me->data.settle_stop);
// }
// ///////////////////////////////////////////////////////////////////////////////
// // stateless conversion
// ///////////////////////////////////////////////////////
// stateless helpers
ChildTemplate *_create_order_template(
IBeforeLaunchAlgoServices &serv, SecurityKey key, api::Side side) {
OrderDefinition order_def = serv.definition();
order_def.set_security_key(key.value());
order_def.set_side(side);
order_def.mutable_custom_def()->mutable_limit()->MergeFrom(
api::orderman::LimitOrderDefinition());
return &(serv.make_child_template(order_def));
}
// copies output into the dynamic state for publishing
void _copy_output(AlgoData &d) {
auto &m = *(d.out_state.mutable_custom_state());
// we can safely assume its within int32
auto secs_to_settle =
static_cast<int>(t_minus_now_usecs(d.settle_start) / 1000000);
// if its in settle (-ve value), its set to 0
secs_to_settle = std::max(secs_to_settle, 0);
m["Seconds To Settle"].set_as_int32(secs_to_settle);
m["VWAP T1-Front"].set_as_double(d.t1_front.vwap());
m["VWAP T1-Second"].set_as_double(d.t1_second.vwap());
m["VWAP T1-Third"].set_as_double(d.t1_third.vwap());
m["VWAP T2-Front"].set_as_double(d.t2_front.vwap());
m["VWAP T2-Second"].set_as_double(d.t2_second.vwap());
m["VWAP T3-Front"].set_as_double(d.t3_front.vwap());
m["T1-Front Projected Settle"].set_as_double(d.t1_front.estimated_settle);
m["T1-Second Projected Settle"].set_as_double(d.t1_second.estimated_settle);
m["T1-Third Projected Settle"].set_as_double(d.t1_third.estimated_settle);
}
// copies the settle time/period part of the input data
bool _copy_settle_times(
AlgoData &d, const Map<std::string, DynamicOrderParam> &params) {
auto hh = get_if<uint32_t>(params, "Settlement Start Hour");
auto mm = get_if<uint32_t>(params, "Settlement Start Minutes");
auto ss = get_if<uint32_t>(params, "Settlement Start Seconds");
auto settle_period_secs =
get_if<uint32_t>(params, "Settlement Period (seconds)");
if (hh.has_value() && mm.has_value() && ss.has_value() &&
settle_period_secs.has_value() && hh.value() <= 23 && mm.value() <= 59 &&
ss.value() <= 59) {
d.settle_start = SODPosition::today_local_time_to_utc_timestamp(
hh.value(), mm.value(), ss.value(), true, false);
d.settle_stop =
SODPosition::add_seconds(d.settle_start, settle_period_secs.value());
auto settle_start_txt = util::TimeUtil::ToString(d.settle_start);
auto settle_stop_txt = util::TimeUtil::ToString(d.settle_stop);
LOG(OrderMan, Info,
"TASCounterAlgo::_copy_settle_times "
"settle_start:{} settle_stop:{} ",
settle_start_txt, settle_stop_txt);
return true;
}
return false;
}
// copies the order execution part of the input data
void _copy_order_data(
AlgoData &out_d, const Map<std::string, DynamicOrderParam> &params) {
auto copy_o = [&](ContractAutoOrder &out_order, std::string time_name,
std::string qty_name, std::string enabled_name) -> bool {
out_order.enabled = false;
auto time = get_if<uint32_t>(params, time_name);
auto qty = get_if<uint32_t>(params, qty_name);
auto enabled = get_if<bool>(params, enabled_name);
if (time.has_value() && qty.has_value() && enabled.has_value()) {
out_order.n_secs = time.value();
out_order.qty = qty.value();
out_order.enabled = enabled.value();
out_order.algo_start =
SODPosition::add_seconds(out_d.settle_stop, -1 * out_order.n_secs);
return true;
}
return false;
};
copy_o(
out_d.t1_front_order, "T1 Front Time", "T1 Front Qty",
"T1 Front Order Enabled");
copy_o(
out_d.t1_second_order, "T1 Second Time", "T1 Second Qty",
"T1 Second Order Enabled");
copy_o(
out_d.t1_third_order, "T1 Third Time", "T1 Third Qty",
"T1 Third Order Enabled");
}
// copies all of the input params (in the algo) into proper structures
bool _setup_data(
IBeforeLaunchAlgoServices &serv,
AlgoData &out_d,
std::map<SecurityKey, ContractMarketData *> &out_md_map) {
auto &def = serv.definition();
// [ captures ] (parameters) -> returnTypesDeclaration { lambdaStatements; }
auto init_contract_bk = [&out_md_map](
auto &out_contract_md, const auto &params,
auto param_name) -> void {
auto k = get_if<uint32_t>(params, param_name);
if (k.has_value()) {
out_contract_md.book = Book(SecurityKey(k.value()));
out_md_map[out_contract_md.book.security_key()] = &out_contract_md;
}
};
if (def.has_custom_def() && def.custom_def().has_dynamic()) {
out_d.tick_size = serv.security().contract_spec().low_tick();
const auto &p = def.custom_def().dynamic().order_params();
// settlement time
if (!_copy_settle_times(out_d, p))
return false;
// main contracts
init_contract_bk(out_d.t1_front, p, "T1 Front");
init_contract_bk(out_d.t1_second, p, "T1 Second");
init_contract_bk(out_d.t1_third, p, "T1 Third");
init_contract_bk(out_d.t2_front, p, "T2 Front");
init_contract_bk(out_d.t2_second, p, "T2 Second");
init_contract_bk(out_d.t3_front, p, "T3 Front");
// TAS contracts
init_contract_bk(out_d.t1_front_tas, p, "T1 Front TAS");
init_contract_bk(out_d.t1_second_tas, p, "T1 Second TAS");
init_contract_bk(out_d.t1_third_tas, p, "T1 Third TAS");
// auto orders
_copy_order_data(out_d, p);
// order template
out_d.t1_front_order.buy_ord = _create_order_template(
serv, out_d.t1_front_tas.book.security_key(), api::Side::SIDE_BUY);
out_d.t1_front_order.sell_ord = _create_order_template(
serv, out_d.t1_front_tas.book.security_key(), api::Side::SIDE_SELL);
out_d.t1_second_order.buy_ord = _create_order_template(
serv, out_d.t1_second_tas.book.security_key(), api::Side::SIDE_BUY);
out_d.t1_second_order.sell_ord = _create_order_template(
serv, out_d.t1_second_tas.book.security_key(), api::Side::SIDE_SELL);
out_d.t1_third_order.buy_ord = _create_order_template(
serv, out_d.t1_third_tas.book.security_key(), api::Side::SIDE_BUY);
out_d.t1_third_order.sell_ord = _create_order_template(
serv, out_d.t1_third_tas.book.security_key(), api::Side::SIDE_SELL);
return true;
}
return false;
}
double round_to_nearest(double price, double tick_size) {
if (os::is_nan(price))
return price;
auto buy_price = bts::securities::raw_round_to_quote_price(
price, tick_size, api::Side::SIDE_BUY);
auto sell_price = buy_price + tick_size;
return std::abs(price - buy_price) < std::abs(sell_price - price)
? buy_price
: sell_price;
}
// time helpers
// converts std::chrono::system_clock::time_point to google timestamp
google::protobuf::Timestamp to_pf_ts(const bts::TimeStamp &ts) {
auto ts_us = std::chrono::time_point_cast<std::chrono::microseconds>(ts);
auto ts_us_val = ts_us.time_since_epoch();
auto usecs = ts_us_val.count();
return google::protobuf::util::TimeUtil::MicrosecondsToTimestamp(usecs);
}
long long t1_minus_t2_usecs(const Timestamp &t1, const Timestamp &t2) {
return util::TimeUtil::TimestampToMicroseconds(t1) -
util::TimeUtil::TimestampToMicroseconds(t2);
}
long long t_minus_now_usecs(const Timestamp &t) {
auto now = util::TimeUtil::GetCurrentTime();
return t1_minus_t2_usecs(t, now);
}
bool within_time_win(
const Timestamp &beg, const Timestamp &end, const Timestamp &ts) {
return t1_minus_t2_usecs(ts, beg) >= 0 && t1_minus_t2_usecs(ts, end) <= 0;
}
// if current time is within time window
bool now_within_time_win(const Timestamp &beg, const Timestamp &end) {
return within_time_win(beg, end, util::TimeUtil::GetCurrentTime());
}
bool trade_within_time_win(
const Timestamp &beg,
const Timestamp &end,
const TickerUpdateEvent::Trade &t) {
auto ts = to_pf_ts(t.ts);
return within_time_win(beg, end, ts);
}
// // stateless helpers
// ChildTemplate *_create_order_template(
// IBeforeLaunchAlgoServices &serv, SecurityKey key, api::Side side) {
// OrderDefinition order_def = serv.definition();
// order_def.set_security_key(key.value());
// order_def.set_side(side);
// order_def.mutable_custom_def()->mutable_limit()->MergeFrom(
// api::orderman::LimitOrderDefinition());
// return &(serv.make_child_template(order_def));
// }
// // copies output into the dynamic state for publishing
// void _copy_output(AlgoData &d) {
// auto &m = *(d.out_state.mutable_custom_state());
// // we can safely assume its within int32
// auto secs_to_settle =
// static_cast<int>(t_minus_now_usecs(d.settle_start) / 1000000);
// // if its in settle (-ve value), its set to 0
// secs_to_settle = std::max(secs_to_settle, 0);
// m["Seconds To Settle"].set_as_int32(secs_to_settle);
// m["VWAP T1-Front"].set_as_double(d.t1_front.vwap());
// m["VWAP T1-Second"].set_as_double(d.t1_second.vwap());
// m["VWAP T1-Third"].set_as_double(d.t1_third.vwap());
// m["VWAP T2-Front"].set_as_double(d.t2_front.vwap());
// m["VWAP T2-Second"].set_as_double(d.t2_second.vwap());
// m["VWAP T3-Front"].set_as_double(d.t3_front.vwap());
// m["T1-Front Projected Settle"].set_as_double(d.t1_front.estimated_settle);
// m["T1-Second Projected
// Settle"].set_as_double(d.t1_second.estimated_settle); m["T1-Third
// Projected Settle"].set_as_double(d.t1_third.estimated_settle);
// }
// // copies the settle time/period part of the input data
// bool _copy_settle_times(
// AlgoData &d, const Map<std::string, DynamicOrderParam> &params) {
// auto hh = get_if<uint32_t>(params, "Settlement Start Hour");
// auto mm = get_if<uint32_t>(params, "Settlement Start Minutes");
// auto ss = get_if<uint32_t>(params, "Settlement Start Seconds");
// auto settle_period_secs =
// get_if<uint32_t>(params, "Settlement Period (seconds)");
// if (hh.has_value() && mm.has_value() && ss.has_value() &&
// settle_period_secs.has_value() && hh.value() <= 23 && mm.value() <= 59
// && ss.value() <= 59) {
// d.settle_start = SODPosition::today_local_time_to_utc_timestamp(
// hh.value(), mm.value(), ss.value(), true, false);
// d.settle_stop =
// SODPosition::add_seconds(d.settle_start,
// settle_period_secs.value());
// auto settle_start_txt = util::TimeUtil::ToString(d.settle_start);
// auto settle_stop_txt = util::TimeUtil::ToString(d.settle_stop);
// LOG(OrderMan, Info,
// "TASCounterAlgo::_copy_settle_times "
// "settle_start:{} settle_stop:{} ",
// settle_start_txt, settle_stop_txt);
// return true;
// }
// return false;
// }
// // copies the order execution part of the input data
// void _copy_order_data(
// AlgoData &out_d, const Map<std::string, DynamicOrderParam> &params) {
// auto copy_o = [&](ContractAutoOrder &out_order, std::string time_name,
// std::string qty_name, std::string enabled_name) -> bool
// {
// out_order.enabled = false;
// auto time = get_if<uint32_t>(params, time_name);
// auto qty = get_if<uint32_t>(params, qty_name);
// auto enabled = get_if<bool>(params, enabled_name);
// if (time.has_value() && qty.has_value() && enabled.has_value()) {
// out_order.n_secs = time.value();
// out_order.qty = qty.value();
// out_order.enabled = enabled.value();
// out_order.algo_start =
// SODPosition::add_seconds(out_d.settle_stop, -1 *
// out_order.n_secs);
// return true;
// }
// return false;
// };
// copy_o(
// out_d.t1_front_order, "T1 Front Time", "T1 Front Qty",
// "T1 Front Order Enabled");
// copy_o(
// out_d.t1_second_order, "T1 Second Time", "T1 Second Qty",
// "T1 Second Order Enabled");
// copy_o(
// out_d.t1_third_order, "T1 Third Time", "T1 Third Qty",
// "T1 Third Order Enabled");
// }
// // copies all of the input params (in the algo) into proper structures
// bool _setup_data(
// IBeforeLaunchAlgoServices &serv,
// AlgoData &out_d,
// std::map<SecurityKey, ContractMarketData *> &out_md_map) {
// auto &def = serv.definition();
// // [ captures ] (parameters) -> returnTypesDeclaration { lambdaStatements;
// } auto init_contract_bk = [&out_md_map](
// auto &out_contract_md, const auto &params,
// auto param_name) -> void {
// auto k = get_if<uint32_t>(params, param_name);
// if (k.has_value()) {
// out_contract_md.book = Book(SecurityKey(k.value()));
// out_md_map[out_contract_md.book.security_key()] = &out_contract_md;
// }
// };
// if (def.has_custom_def() && def.custom_def().has_dynamic()) {
// out_d.tick_size = serv.security().contract_spec().low_tick();
// const auto &p = def.custom_def().dynamic().order_params();
// // settlement time
// if (!_copy_settle_times(out_d, p))
// return false;
// // main contracts
// init_contract_bk(out_d.t1_front, p, "T1 Front");
// init_contract_bk(out_d.t1_second, p, "T1 Second");
// init_contract_bk(out_d.t1_third, p, "T1 Third");
// init_contract_bk(out_d.t2_front, p, "T2 Front");
// init_contract_bk(out_d.t2_second, p, "T2 Second");
// init_contract_bk(out_d.t3_front, p, "T3 Front");
// // TAS contracts
// init_contract_bk(out_d.t1_front_tas, p, "T1 Front TAS");
// init_contract_bk(out_d.t1_second_tas, p, "T1 Second TAS");
// init_contract_bk(out_d.t1_third_tas, p, "T1 Third TAS");
// // auto orders
// _copy_order_data(out_d, p);
// // order template
// out_d.t1_front_order.buy_ord = _create_order_template(
// serv, out_d.t1_front_tas.book.security_key(), api::Side::SIDE_BUY);
// out_d.t1_front_order.sell_ord = _create_order_template(
// serv, out_d.t1_front_tas.book.security_key(),
// api::Side::SIDE_SELL);
// out_d.t1_second_order.buy_ord = _create_order_template(
// serv, out_d.t1_second_tas.book.security_key(),
// api::Side::SIDE_BUY);
// out_d.t1_second_order.sell_ord = _create_order_template(
// serv, out_d.t1_second_tas.book.security_key(),
// api::Side::SIDE_SELL);
// out_d.t1_third_order.buy_ord = _create_order_template(
// serv, out_d.t1_third_tas.book.security_key(), api::Side::SIDE_BUY);
// out_d.t1_third_order.sell_ord = _create_order_template(
// serv, out_d.t1_third_tas.book.security_key(),
// api::Side::SIDE_SELL);
// return true;
// }
// return false;
// }
// double round_to_nearest(double price, double tick_size) {
// if (os::is_nan(price))
// return price;
// auto buy_price = bts::securities::raw_round_to_quote_price(
// price, tick_size, api::Side::SIDE_BUY);
// auto sell_price = buy_price + tick_size;
// return std::abs(price - buy_price) < std::abs(sell_price - price)
// ? buy_price
// : sell_price;
// }
// // time helpers
// // converts std::chrono::system_clock::time_point to google timestamp
// google::protobuf::Timestamp to_pf_ts(const bts::TimeStamp &ts) {
// auto ts_us = std::chrono::time_point_cast<std::chrono::microseconds>(ts);
// auto ts_us_val = ts_us.time_since_epoch();
// auto usecs = ts_us_val.count();
// return google::protobuf::util::TimeUtil::MicrosecondsToTimestamp(usecs);
// }
// long long t1_minus_t2_usecs(const Timestamp &t1, const Timestamp &t2) {
// return util::TimeUtil::TimestampToMicroseconds(t1) -
// util::TimeUtil::TimestampToMicroseconds(t2);
// }
// long long t_minus_now_usecs(const Timestamp &t) {
// auto now = util::TimeUtil::GetCurrentTime();
// return t1_minus_t2_usecs(t, now);
// }
// bool within_time_win(
// const Timestamp &beg, const Timestamp &end, const Timestamp &ts) {
// return t1_minus_t2_usecs(ts, beg) >= 0 && t1_minus_t2_usecs(ts, end) <= 0;
// }
// // if current time is within time window
// bool now_within_time_win(const Timestamp &beg, const Timestamp &end) {
// return within_time_win(beg, end, util::TimeUtil::GetCurrentTime());
// }
// bool trade_within_time_win(
// const Timestamp &beg,
// const Timestamp &end,
// const TickerUpdateEvent::Trade &t) {
// auto ts = to_pf_ts(t.ts);
// return within_time_win(beg, end, ts);
// }
} // namespace orderman
// } // namespace orderman
} // namespace bts
// } // namespace bts
@@ -1,43 +1,43 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class ChildTemplate;
// class ChildTemplate;
class TASCounterAlgo : public IAlgoOrder {
public:
TASCounterAlgo();
~TASCounterAlgo();
struct TASCounterAlgoParams {};
// class TASCounterAlgo : public IAlgoOrder {
// public:
// TASCounterAlgo();
// ~TASCounterAlgo();
// struct TASCounterAlgoParams {};
public:
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_ticker(SecurityKey, TradeVector const &) override;
IAlgoOrder::OnCancelResponse on_cancel() override;
void on_timer() override;
// public:
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(api::orderman::Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_ticker(SecurityKey, TradeVector const &) override;
// IAlgoOrder::OnCancelResponse on_cancel() override;
// void on_timer() override;
private:
enum class Status;
// private:
// enum class Status;
struct PImpl;
std::unique_ptr<PImpl> me;
// struct PImpl;
// std::unique_ptr<PImpl> me;
private:
IAlgoServices &_algo() const;
void _switch_state(Status new_status);
void _cxl_and_end(bool is_error, const std::string &err_msg);
void _check_and_send_order();
void _compute_settles();
void _report_state();
bool _within_settle() const;
};
// private:
// IAlgoServices &_algo() const;
// void _switch_state(Status new_status);
// void _cxl_and_end(bool is_error, const std::string &err_msg);
// void _check_and_send_order();
// void _compute_settles();
// void _report_state();
// bool _within_settle() const;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,205 +1,204 @@
#include "lib/orderman/orders/algo/algo_orders/TopAlgo.h"
// #include "lib/orderman/orders/algo/algo_orders/TopAlgo.h"
#include "lib/domain_model/Security.h"
#include "lib/logging/logging.h"
#include "lib/orderman/order_factory/IOrderFactory.h"
#include "lib/orderman/orderman_utils/Book.h"
#include "lib/orderman/orders/algo/IAlgoServices.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/utils/PBUtils.h"
// #include "lib/domain_model/Security.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/order_factory/IOrderFactory.h"
// #include "lib/orderman/orderman_utils/Book.h"
// #include "lib/orderman/orders/algo/IAlgoServices.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/utils/PBUtils.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
using namespace api::orderman;
// using namespace api::orderman;
struct TopAlgo::PImpl {
IAlgoServices *algo;
spark::QuantityMeasure top_threshold_measure;
uint32_t ticks_off = 0;
OrderKey child_key = null_order_key();
ChildTemplate *child_template = nullptr;
Book last_book;
};
// struct TopAlgo::PImpl {
// IAlgoServices *algo;
// spark::QuantityMeasure top_threshold_measure;
// uint32_t ticks_off = 0;
// OrderKey child_key = null_order_key();
// ChildTemplate *child_template = nullptr;
// Book last_book;
// };
TopAlgo::TopAlgo() : me(new PImpl()) {}
TopAlgo::~TopAlgo() {}
IAlgoServices &TopAlgo::algo() const { return *me->algo; }
// TopAlgo::TopAlgo() : me(new PImpl()) {}
// TopAlgo::~TopAlgo() {}
// IAlgoServices &TopAlgo::algo() const { return *me->algo; }
OrderOpResult TopAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
auto &def = serv.definition();
bts_assert(def.has_custom_def());
bts_assert(def.custom_def().has_top());
// OrderOpResult TopAlgo::do_before_launch(IBeforeLaunchAlgoServices &serv) {
// auto &def = serv.definition();
// bts_assert(def.has_custom_def());
// bts_assert(def.custom_def().has_top());
// Validate the top part of the definition
auto &top = def.custom_def().top();
if (!top.has_child() &&
OrderKey(def.attach_target_order_key()) == null_order_key()) {
return OrderOpResult{
ORDER_ERROR_INVALID_ORDER_DEFINITON,
"Top order must have either a child order definition or an "
"order to attach to"};
}
// // Validate the top part of the definition
// auto &top = def.custom_def().top();
// if (!top.has_child() &&
// OrderKey(def.attach_target_order_key()) == null_order_key()) {
// return OrderOpResult{
// ORDER_ERROR_INVALID_ORDER_DEFINITON,
// "Top order must have either a child order definition or an "
// "order to attach to"};
// }
me->top_threshold_measure = top.top_threshold();
me->ticks_off = top.ticks_off();
if (!serv.is_attaching()) {
// if we are not attaching create a child template so we can
// quickly launch our child when the time comes.
auto child_def = def;
*child_def.mutable_custom_def() = def.custom_def().top().child();
me->child_template = &(serv.make_child_template(child_def));
}
// me->top_threshold_measure = top.top_threshold();
// me->ticks_off = top.ticks_off();
// if (!serv.is_attaching()) {
// // if we are not attaching create a child template so we can
// // quickly launch our child when the time comes.
// auto child_def = def;
// *child_def.mutable_custom_def() = def.custom_def().top().child();
// me->child_template = &(serv.make_child_template(child_def));
// }
return {};
}
// return {};
// }
void TopAlgo::do_launch(IAlgoServices &algo) {
me->algo = &algo;
algo.subscribe_book(SecurityKey(algo.security().id()));
bts_assert(me->child_template);
me->child_key =
algo.launch_child(*me->child_template, algo.price(), algo.quantity());
}
// void TopAlgo::do_launch(IAlgoServices &algo) {
// me->algo = &algo;
// algo.subscribe_book(SecurityKey(algo.security().id()));
// bts_assert(me->child_template);
// me->child_key =
// algo.launch_child(*me->child_template, algo.price(), algo.quantity());
// }
void TopAlgo::do_launch_attached(
IAlgoServices &algo, OrderKey k, ChildState st) {
me->algo = &algo;
me->child_key = k;
algo.subscribe_book(SecurityKey(algo.security().id()));
LOG(OrderMan, Diag,
"Top order attached to order {}. Child currently has {} fills",
k.value(), st.total_fills);
on_child_change(k, st);
}
// void TopAlgo::do_launch_attached(
// IAlgoServices &algo, OrderKey k, ChildState st) {
// me->algo = &algo;
// me->child_key = k;
// algo.subscribe_book(SecurityKey(algo.security().id()));
// LOG(OrderMan, Diag,
// "Top order attached to order {}. Child currently has {} fills",
// k.value(), st.total_fills);
// on_child_change(k, st);
// }
void TopAlgo::do_update(api::orderman::Update const &update) {
LOG(OrderMan, Diag, "Top order {} receiving update: {}",
algo().order_key().value(), utils::pb::to_string(update));
// void TopAlgo::do_update(api::orderman::Update const &update) {
// LOG(OrderMan, Diag, "Top order {} receiving update: {}",
// algo().order_key().value(), utils::pb::to_string(update));
if (update.has_custom() && !update.custom().has_top()) {
algo().terminate_error("Invalid update");
return;
}
// if (update.has_custom() && !update.custom().has_top()) {
// algo().terminate_error("Invalid update");
// return;
// }
Update child_update;
child_update.set_price(update.price());
child_update.set_quantity(update.quantity());
// Update child_update;
// child_update.set_price(update.price());
// child_update.set_quantity(update.quantity());
if (update.has_custom()) {
// process custom part of the udpate if any
auto &top_update = update.custom().top();
*child_update.mutable_custom() = top_update.child();
me->top_threshold_measure = top_update.top_threshold();
me->ticks_off = top_update.ticks_off();
}
algo().update_child(me->child_key, child_update);
}
// if (update.has_custom()) {
// // process custom part of the udpate if any
// auto &top_update = update.custom().top();
// *child_update.mutable_custom() = top_update.child();
// me->top_threshold_measure = top_update.top_threshold();
// me->ticks_off = top_update.ticks_off();
// }
// algo().update_child(me->child_key, child_update);
// }
void TopAlgo::on_book(SecurityKey, Book const &b) {
me->last_book = b;
ensure_order_meets_constraints();
}
// void TopAlgo::on_book(SecurityKey, Book const &b) {
// me->last_book = b;
// ensure_order_meets_constraints();
// }
void TopAlgo::on_child_change(OrderKey, ChildState const &st) {
algo().update_fill_state(st.total_fills, st.last_price);
// void TopAlgo::on_child_change(OrderKey, ChildState const &st) {
// algo().update_fill_state(st.total_fills, st.last_price);
switch (st.status) {
case ChildStatus::WORKING:
return;
case ChildStatus::COMPLETED:
algo().complete();
return;
case ChildStatus::CANCELED:
algo().cancel("Unexpected child cancel");
return;
}
}
// switch (st.status) {
// case ChildStatus::WORKING:
// return;
// case ChildStatus::COMPLETED:
// algo().complete();
// return;
// case ChildStatus::CANCELED:
// algo().cancel("Unexpected child cancel");
// return;
// }
// }
bool TopAlgo::ensure_order_meets_constraints() {
LOG(OrderMan, Diag,
"Top order {} received book update. Current top of book: {}@{}",
algo().order_key().value(), top_of_book_quantity().value(),
top_of_book_price().value());
// bool TopAlgo::ensure_order_meets_constraints() {
// LOG(OrderMan, Diag,
// "Top order {} received book update. Current top of book: {}@{}",
// algo().order_key().value(), top_of_book_quantity().value(),
// top_of_book_price().value());
if (better_than_top()) {
LOG(OrderMan, Info,
"Top order {} became better than the top of the book, canceling",
algo().order_key().value());
algo().cancel("Order would become BBO");
return false;
}
// if (better_than_top()) {
// LOG(OrderMan, Info,
// "Top order {} became better than the top of the book, canceling",
// algo().order_key().value());
// algo().cancel("Order would become BBO");
// return false;
// }
if (at_top_of_book()) {
auto total_min_quantity =
algo().quantity().value() + min_quantity_at_top().value();
LOG(OrderMan, Diag,
"Top order {} is now at the top of the book, min quantity for book "
"is: {}"
" book quantity is: {}",
algo().order_key().value(), total_min_quantity,
top_of_book_quantity().value());
if (top_of_book_quantity() < Quantity(total_min_quantity)) {
LOG(OrderMan, Info,
"Top order {} is canceling because min quantiy is not met",
algo().order_key().value());
algo().cancel("Top of book is below threshold");
return false;
}
} else
LOG(OrderMan, Diag,
"Top order {} is not at the top of the book. Order: {} Book: {}",
algo().order_key().value(), algo().price().value(),
top_of_book_price().value());
// if (at_top_of_book()) {
// auto total_min_quantity =
// algo().quantity().value() + min_quantity_at_top().value();
// LOG(OrderMan, Diag,
// "Top order {} is now at the top of the book, min quantity for book
// " "is: {}" " book quantity is: {}", algo().order_key().value(),
// total_min_quantity, top_of_book_quantity().value());
// if (top_of_book_quantity() < Quantity(total_min_quantity)) {
// LOG(OrderMan, Info,
// "Top order {} is canceling because min quantiy is not met",
// algo().order_key().value());
// algo().cancel("Top of book is below threshold");
// return false;
// }
// } else
// LOG(OrderMan, Diag,
// "Top order {} is not at the top of the book. Order: {} Book: {}",
// algo().order_key().value(), algo().price().value(),
// top_of_book_price().value());
if (me->ticks_off > 0) {
auto tick_diff = securities::price_diff_to_ticks(
algo().price().value(), top_of_book_price().value(),
securities::tick_size(algo().security()).value());
if (std::abs(tick_diff) <= (int)me->ticks_off) {
LOG(OrderMan, Info,
"Top order {} price {} is less than {} ticks away from top ({}). "
"Canceling",
algo().order_key().value(), algo().price().value(),
me->ticks_off + 1, top_of_book_price().value());
algo().cancel("Order is too close to top of the book");
return false;
} else
LOG(OrderMan, Diag,
"Top order {} price {} is {} ticks away from top. Top: {} Ticks "
"off: {}",
algo().order_key().value(), algo().price().value(),
std::abs(tick_diff), top_of_book_price().value(), me->ticks_off);
}
// if (me->ticks_off > 0) {
// auto tick_diff = securities::price_diff_to_ticks(
// algo().price().value(), top_of_book_price().value(),
// securities::tick_size(algo().security()).value());
// if (std::abs(tick_diff) <= (int)me->ticks_off) {
// LOG(OrderMan, Info,
// "Top order {} price {} is less than {} ticks away from top ({}).
// " "Canceling", algo().order_key().value(),
// algo().price().value(), me->ticks_off + 1,
// top_of_book_price().value());
// algo().cancel("Order is too close to top of the book");
// return false;
// } else
// LOG(OrderMan, Diag,
// "Top order {} price {} is {} ticks away from top. Top: {} Ticks
// " "off: {}", algo().order_key().value(), algo().price().value(),
// std::abs(tick_diff), top_of_book_price().value(),
// me->ticks_off);
// }
return true;
}
// return true;
// }
bool TopAlgo::at_top_of_book() const {
return securities::is_same(algo().price(), top_of_book_price());
}
// bool TopAlgo::at_top_of_book() const {
// return securities::is_same(algo().price(), top_of_book_price());
// }
bool TopAlgo::better_than_top() const {
auto my_side = algo().definition().side();
return securities::is_better(my_side, algo().price(), top_of_book_price());
}
// bool TopAlgo::better_than_top() const {
// auto my_side = algo().definition().side();
// return securities::is_better(my_side, algo().price(),
// top_of_book_price());
// }
Quantity TopAlgo::min_quantity_at_top() const {
return SpreaderUtils::_quantity_threshold(
me->top_threshold_measure, algo().quantity());
}
// Quantity TopAlgo::min_quantity_at_top() const {
// return SpreaderUtils::_quantity_threshold(
// me->top_threshold_measure, algo().quantity());
// }
Price TopAlgo::top_of_book_price() const {
auto my_side = algo().definition().side();
auto &my_side_psz = me->last_book.side(my_side)[0];
return Price(my_side_psz.price());
}
// Price TopAlgo::top_of_book_price() const {
// auto my_side = algo().definition().side();
// auto &my_side_psz = me->last_book.side(my_side)[0];
// return Price(my_side_psz.price());
// }
Quantity TopAlgo::top_of_book_quantity() const {
auto my_side = algo().definition().side();
auto &my_side_psz = me->last_book.side(my_side)[0];
return Quantity(my_side_psz.size());
}
// Quantity TopAlgo::top_of_book_quantity() const {
// auto my_side = algo().definition().side();
// auto &my_side_psz = me->last_book.side(my_side)[0];
// return Quantity(my_side_psz.size());
// }
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,38 +1,38 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/algo/IAlgoOrder.h"
// #include "lib/orderman/orders/algo/IAlgoOrder.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
// If Top becomes the top of book, it cancels if there is less than
// some threshold of size joined with it.
// // If Top becomes the top of book, it cancels if there is less than
// // some threshold of size joined with it.
class TopAlgo : public IAlgoOrder {
public:
TopAlgo();
~TopAlgo();
// class TopAlgo : public IAlgoOrder {
// public:
// TopAlgo();
// ~TopAlgo();
OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
void do_launch(IAlgoServices &) override;
void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
void do_update(api::orderman::Update const &) override;
void on_book(SecurityKey, Book const &) override;
void on_child_change(OrderKey, ChildState const &) override;
// OrderOpResult do_before_launch(IBeforeLaunchAlgoServices &) override;
// void do_launch(IAlgoServices &) override;
// void do_launch_attached(IAlgoServices &, OrderKey, ChildState) override;
// void do_update(api::orderman::Update const &) override;
// void on_book(SecurityKey, Book const &) override;
// void on_child_change(OrderKey, ChildState const &) override;
private:
struct PImpl;
std::unique_ptr<PImpl> me;
// private:
// struct PImpl;
// std::unique_ptr<PImpl> me;
bool at_top_of_book() const;
bool better_than_top() const;
Quantity min_quantity_at_top() const;
bool ensure_order_meets_constraints();
bool has_running_child() const;
Price top_of_book_price() const;
Quantity top_of_book_quantity() const;
IAlgoServices &algo() const;
};
// bool at_top_of_book() const;
// bool better_than_top() const;
// Quantity min_quantity_at_top() const;
// bool ensure_order_meets_constraints();
// bool has_running_child() const;
// Price top_of_book_price() const;
// Quantity top_of_book_quantity() const;
// IAlgoServices &algo() const;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,59 +1,58 @@
#pragma once
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/orderman/orders/queue_holder/QueueHolderOrderState.h"
#include "lib/utils/bts_chrono.h"
// #pragma once
// #include "lib/orderman/orderman_utils/orderman_common.h"
// #include "lib/orderman/orders/queue_holder/QueueHolderOrderState.h"
// #include "lib/utils/bts_chrono.h"
namespace bts {
namespace trace {
struct CorID;
}
namespace api {
namespace orderman {
class OrderDefinition;
class OrderRecord;
} // namespace orderman
} // namespace api
namespace orderman {
class PreActivationOrderServices;
class OrderServices;
} // namespace orderman
namespace orderman {
class OrderStateUpdateEvent;
}
namespace orderman {
class SpreaderOrder;
class Book;
// namespace bts {
// namespace trace {
// struct CorID;
// }
// namespace api {
// namespace orderman {
// class OrderDefinition;
// class OrderRecord;
// } // namespace orderman
// } // namespace api
// namespace orderman {
// class PreActivationOrderServices;
// class OrderServices;
// } // namespace orderman
// namespace orderman {
// class OrderStateUpdateEvent;
// }
// namespace orderman {
// class Book;
// All the QueueHolderOrder state machine workers
// implement this
class IQueueHolderOrderStateWorker {
public:
virtual QueueHolderOrderState state() const = 0;
// 0. when the state machine transits to a new state
virtual void on_transition() = 0;
// 1. called once before the other calls. state should be
// constructed here
virtual void on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) = 0;
// 2. when constructed on recovery (with partial state)
virtual void on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) = 0;
// 3. this is the end of the construction cycle. should happen
// after on_init_new and before all other calls. market data subs
// and quoter order creation happens here
virtual void on_activate(Price, Quantity, OrderServices &) = 0;
// 4. new a price, qty change
virtual void on_update(Price, Quantity) = 0;
// 5. called when the user cancels an order
virtual void on_cancel() = 0;
// 6. order callback
virtual void on_child_order_update(const OrderStateUpdateEvent &) = 0;
// 7. on market data update (support late joiners)
virtual void on_book(const Book &, const trace::CorID &) = 0;
// 8. time event called by the infrastructure
virtual void on_time(const TimeStamp &) = 0;
};
} // namespace orderman
} // namespace bts
// // All the QueueHolderOrder state machine workers
// // implement this
// class IQueueHolderOrderStateWorker {
// public:
// virtual QueueHolderOrderState state() const = 0;
// // 0. when the state machine transits to a new state
// virtual void on_transition() = 0;
// // 1. called once before the other calls. state should be
// // constructed here
// virtual void on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) = 0;
// // 2. when constructed on recovery (with partial state)
// virtual void on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) = 0;
// // 3. this is the end of the construction cycle. should happen
// // after on_init_new and before all other calls. market data subs
// // and quoter order creation happens here
// virtual void on_activate(Price, Quantity, OrderServices &) = 0;
// // 4. new a price, qty change
// virtual void on_update(Price, Quantity) = 0;
// // 5. called when the user cancels an order
// virtual void on_cancel() = 0;
// // 6. order callback
// virtual void on_child_order_update(const OrderStateUpdateEvent &) = 0;
// // 7. on market data update (support late joiners)
// virtual void on_book(const Book &, const trace::CorID &) = 0;
// // 8. time event called by the infrastructure
// virtual void on_time(const TimeStamp &) = 0;
// };
// } // namespace orderman
// } // namespace bts
@@ -1,328 +1,329 @@
#pragma once
// #pragma once
#include "lib/domain_model/Security.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.h"
#include "lib/os/os.h"
// #include "lib/domain_model/Security.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.h"
// #include "lib/os/os.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
// submit, modifies and cancels all orders
// that constitute the QHO.
// i. places, modifies and cancels orders
// ii. helper functons to get the accumulated
// state of the orders
// all the order interactions are done explicitely
// through function calls
class QHOOrderManager {
private:
bool _inited;
bool _orders_placed_once;
OrderBuilder *_order_builder;
api::Side _side;
double _tick_size;
uint16_t _n_eff_orders;
uint32_t _show_qty;
// the order of this does is not asc/desc wrt
// price (modify of price will make it unordered)
std::vector<SpreaderChildOrderState> _orders;
Price _target_price;
Quantity _target_qty;
// // submit, modifies and cancels all orders
// // that constitute the QHO.
// // i. places, modifies and cancels orders
// // ii. helper functons to get the accumulated
// // state of the orders
// // all the order interactions are done explicitely
// // through function calls
// class QHOOrderManager {
// private:
// bool _inited;
// bool _orders_placed_once;
// OrderBuilder *_order_builder;
// api::Side _side;
// double _tick_size;
// uint16_t _n_eff_orders;
// uint32_t _show_qty;
// // the order of this does is not asc/desc wrt
// // price (modify of price will make it unordered)
// std::vector<SpreaderChildOrderState> _orders;
// Price _target_price;
// Quantity _target_qty;
public:
QHOOrderManager()
: _inited(false), _orders_placed_once(false), _order_builder(nullptr),
_side(api::Side::SIDE_BUY), _tick_size(os::nan()), _n_eff_orders(1),
_show_qty(0), _orders(), _target_price(os::nan()), _target_qty(0) {}
// public interface //////////////////////////////
//////////////////////////////////////////////////
// public:
// QHOOrderManager()
// : _inited(false), _orders_placed_once(false), _order_builder(nullptr),
// _side(api::Side::SIDE_BUY), _tick_size(os::nan()), _n_eff_orders(1),
// _show_qty(0), _orders(), _target_price(os::nan()), _target_qty(0) {}
// // public interface //////////////////////////////
// //////////////////////////////////////////////////
// We could have done away with init and
// moved it in the cons. The problem is that
// the QueueHolderOrder does not have the data
// at its construction time to construct
// QHOOrderManager. (It could alternatively
// have used a pointer to QueueHolderOrder
// but for contigous memory allocations it
// did not)
void init(
OrderBuilder &order_builder,
SecurityKey key,
api::Side side,
double tick_size,
uint16_t n_orders,
uint32_t show_qty,
Price price,
Quantity qty) {
_inited = true;
_order_builder = &order_builder;
_side = side;
_tick_size = tick_size;
// 1 trailing order means 2 total orders and so forth
_n_eff_orders = n_orders + 1;
_orders.reserve(_n_eff_orders);
_show_qty = show_qty;
for (uint16_t i = 0; i < _n_eff_orders; i++)
_orders.emplace_back(key, true, side, true, nullptr);
_target_price = price;
_target_qty = qty;
}
// this can be called only once
void place_order() {
bts_assert(_inited);
bts_assert(!_orders_placed_once);
_orders_placed_once = true;
Quantity target_qty = _remaining();
bts_assert(target_qty.value() > 0);
double price_i = _target_price.value(),
move_inc = _top_down_tick_inc(_tick_size, _side);
for (SpreaderChildOrderState &o : _orders) {
bts_assert(o.out_quantity().value() == 0);
ChildOrder child_order = _order_builder->build();
o.re_init(child_order);
_place_order(o, Price(price_i), target_qty);
price_i += move_inc;
}
}
// sends orders (for the remaining qty)
// for all terminal orders
void reenter_filled_orders() {
bts_assert(_inited);
bts_assert(_orders_placed_once);
Quantity target_qty = _remaining();
bts_assert(target_qty.value() > 0);
for (SpreaderChildOrderState &o : _orders) {
if (o.terminal_status()) {
Price price = o.out_price(); // re-init resets the price
ChildOrder child_order = _order_builder->build();
o.re_init(child_order);
_place_order(o, price, target_qty);
} else
_prune_qty(o);
}
}
// can only modifiy if all acked and working
void modify_order(Price price, Quantity qty) {
bts_assert(_inited);
bts_assert(_orders_placed_once);
Price prev_price = _target_price;
Quantity prev_qty = _target_qty;
// all acked and working
bts_assert(all_orders_acked() && !has_terminal_order());
_target_price = price;
_target_qty = qty;
bts_assert(_remaining().value() > 0);
_modify_order(prev_price, prev_qty);
}
// cancels all the orders (and is smart
// enough to about working orders only)
void cancel() {
bts_assert(_inited);
bts_assert(_orders_placed_once);
for (auto &o : _orders) {
// is_cancelled() is checked so as not to keep
// sending cancel requests
if (!o.is_cancelled() && o.acknowledged() && !o.terminal_status())
o.cancel_order();
}
}
// finds the orders and subsumes the update.
// returns false if it cant locate the order key
bool on_order_update(const OrderStateUpdateEvent &ev) {
bts_assert(_inited);
bts_assert(_orders_placed_once);
for (auto &o : _orders)
if (o.order_key() == ev.key) {
o.on_order_update(ev);
return true;
}
return false;
}
// decreases qty in case of fills.
// can only modifiy if all acked and working
void prune_quantities() {
bts_assert(_inited);
bts_assert(_orders_placed_once);
bts_assert(all_orders_acked() && !has_terminal_order());
for (auto &o : _orders)
_prune_qty(o);
}
///////////////////////////////////////////////
inline Quantity filled_qty() const {
uint32_t rv = 0;
for (const auto &o : _orders)
rv += o.total_fills().value();
return Quantity(rv);
}
inline void filled_price(double &filled_price, uint32_t &filled_qty) const {
double tot_price = 0.0;
uint32_t tot_qty = 0;
for (const auto &o : _orders) {
Price so_price;
Quantity so_qty;
o.total_fills(so_qty, so_price);
if (so_qty.value() > 0) {
tot_qty += so_qty.value();
tot_price += so_price.value() * so_qty.value();
}
}
if (tot_qty > 0)
tot_price /= tot_qty;
filled_price = tot_price;
filled_qty = tot_qty;
}
inline bool last_update_has_fills() const {
for (const auto &o : _orders)
if (o.last_update_filled_quantity().value() > 0)
return true;
return false;
}
inline bool has_cancelled_order() const {
for (const auto &o : _orders)
if (o.cancelled())
return true;
return false;
}
inline bool has_completed_order() const {
for (const auto &o : _orders)
if (o.completed())
return true;
return false;
}
inline bool has_errored_order() const {
for (const auto &o : _orders)
if (o.errored())
return true;
return false;
}
inline bool has_terminal_order() const {
for (const auto &o : _orders)
if (o.terminal_status())
return true;
return false;
}
inline bool all_orders_terminal() const {
for (const auto &o : _orders)
if (!o.terminal_status())
return false;
return true;
}
inline bool all_orders_acked() const {
for (const auto &o : _orders)
if (!o.acknowledged())
return false;
return true;
}
// // We could have done away with init and
// // moved it in the cons. The problem is that
// // the QueueHolderOrder does not have the data
// // at its construction time to construct
// // QHOOrderManager. (It could alternatively
// // have used a pointer to QueueHolderOrder
// // but for contigous memory allocations it
// // did not)
// void init(
// OrderBuilder &order_builder,
// SecurityKey key,
// api::Side side,
// double tick_size,
// uint16_t n_orders,
// uint32_t show_qty,
// Price price,
// Quantity qty) {
// _inited = true;
// _order_builder = &order_builder;
// _side = side;
// _tick_size = tick_size;
// // 1 trailing order means 2 total orders and so forth
// _n_eff_orders = n_orders + 1;
// _orders.reserve(_n_eff_orders);
// _show_qty = show_qty;
// for (uint16_t i = 0; i < _n_eff_orders; i++)
// _orders.emplace_back(key, true, side, true);
// _target_price = price;
// _target_qty = qty;
// }
// // this can be called only once
// void place_order() {
// bts_assert(_inited);
// bts_assert(!_orders_placed_once);
// _orders_placed_once = true;
// Quantity target_qty = _remaining();
// bts_assert(target_qty.value() > 0);
// double price_i = _target_price.value(),
// move_inc = _top_down_tick_inc(_tick_size, _side);
// for (SpreaderChildOrderState &o : _orders) {
// bts_assert(o.out_quantity().value() == 0);
// // TODO
// // ChildOrder child_order = _order_builder->build();
// // o.re_init(child_order);
// _place_order(o, Price(price_i), target_qty);
// price_i += move_inc;
// }
// }
// // sends orders (for the remaining qty)
// // for all terminal orders
// void reenter_filled_orders() {
// bts_assert(_inited);
// bts_assert(_orders_placed_once);
// Quantity target_qty = _remaining();
// bts_assert(target_qty.value() > 0);
// for (SpreaderChildOrderState &o : _orders) {
// if (o.terminal_status()) {
// Price price = o.out_price(); // re-init resets the price
// // ChildOrder child_order = _order_builder->build();
// // o.re_init(child_order);
// _place_order(o, price, target_qty);
// } else
// _prune_qty(o);
// }
// }
// // can only modifiy if all acked and working
// void modify_order(Price price, Quantity qty) {
// bts_assert(_inited);
// bts_assert(_orders_placed_once);
// Price prev_price = _target_price;
// Quantity prev_qty = _target_qty;
// // all acked and working
// bts_assert(all_orders_acked() && !has_terminal_order());
// _target_price = price;
// _target_qty = qty;
// bts_assert(_remaining().value() > 0);
// _modify_order(prev_price, prev_qty);
// }
// // cancels all the orders (and is smart
// // enough to about working orders only)
// void cancel() {
// bts_assert(_inited);
// bts_assert(_orders_placed_once);
// for (auto &o : _orders) {
// // is_cancelled() is checked so as not to keep
// // sending cancel requests
// if (!o.is_cancelled() && o.acknowledged() && !o.terminal_status())
// o.cancel_order();
// }
// }
// // finds the orders and subsumes the update.
// // returns false if it cant locate the order key
// bool on_order_update(const OrderStateUpdateEvent &ev) {
// bts_assert(_inited);
// bts_assert(_orders_placed_once);
// for (auto &o : _orders)
// if (o.order_key() == ev.key) {
// o.on_order_update(ev);
// return true;
// }
// return false;
// }
// // decreases qty in case of fills.
// // can only modifiy if all acked and working
// void prune_quantities() {
// bts_assert(_inited);
// bts_assert(_orders_placed_once);
// bts_assert(all_orders_acked() && !has_terminal_order());
// for (auto &o : _orders)
// _prune_qty(o);
// }
// ///////////////////////////////////////////////
// inline Quantity filled_qty() const {
// uint32_t rv = 0;
// for (const auto &o : _orders)
// rv += o.total_fills().value();
// return Quantity(rv);
// }
// inline void filled_price(double &filled_price, uint32_t &filled_qty) const {
// double tot_price = 0.0;
// uint32_t tot_qty = 0;
// for (const auto &o : _orders) {
// Price so_price;
// Quantity so_qty;
// o.total_fills(so_qty, so_price);
// if (so_qty.value() > 0) {
// tot_qty += so_qty.value();
// tot_price += so_price.value() * so_qty.value();
// }
// }
// if (tot_qty > 0)
// tot_price /= tot_qty;
// filled_price = tot_price;
// filled_qty = tot_qty;
// }
// inline bool last_update_has_fills() const {
// for (const auto &o : _orders)
// if (o.last_update_filled_quantity().value() > 0)
// return true;
// return false;
// }
// inline bool has_cancelled_order() const {
// for (const auto &o : _orders)
// if (o.cancelled())
// return true;
// return false;
// }
// inline bool has_completed_order() const {
// for (const auto &o : _orders)
// if (o.completed())
// return true;
// return false;
// }
// inline bool has_errored_order() const {
// for (const auto &o : _orders)
// if (o.errored())
// return true;
// return false;
// }
// inline bool has_terminal_order() const {
// for (const auto &o : _orders)
// if (o.terminal_status())
// return true;
// return false;
// }
// inline bool all_orders_terminal() const {
// for (const auto &o : _orders)
// if (!o.terminal_status())
// return false;
// return true;
// }
// inline bool all_orders_acked() const {
// for (const auto &o : _orders)
// if (!o.acknowledged())
// return false;
// return true;
// }
private:
void _modify_order(Price prev_price, Quantity) {
double move_inc = _top_down_tick_inc(_tick_size, _side);
int tick_diff = bts::securities::price_diff_to_ticks(
prev_price.value(), _target_price.value(), _tick_size);
Quantity remaining_target = _remaining();
bool moving_up = tick_diff > 0;
double price_i = _target_price.value();
for (uint16_t i = 0; i < _n_eff_orders; i++) {
SpreaderChildOrderState *order_i =
_find_working_order_at_price(price_i);
if (order_i == nullptr) {
SpreaderChildOrderState &to_move =
moving_up ? _lowest_price_order() : _highest_price_order();
uint32_t new_ord_qty = _adjusted_qty(
to_move.current_fills().value(), to_move.out_quantity().value(),
remaining_target.value(),
true); // qty inc since its
// changing price levels
// to_move.modify_order(Price(price_i),Quantity(new_ord_qty));
_modify_child_order(to_move, Price(price_i), Quantity(new_ord_qty));
} else
_prune_qty(*order_i);
price_i += move_inc;
}
}
// decreases qty if the order at any level can overfill
// the total, ie for an order out_qty-filled > total_rem
// if rem=5, and this order is 3/9 this will make it 3/8
void _prune_qty(SpreaderChildOrderState &o) {
uint32_t new_ord_qty = _adjusted_qty(
o.current_fills().value(), o.out_quantity().value(),
_remaining().value(),
false); // dont inc the qty
bts_assert(new_ord_qty > 0);
// o.modify_order(o.out_price(),Quantity(new_ord_qty));
_modify_child_order(o, o.out_price(), Quantity(new_ord_qty));
}
Quantity _remaining() const {
if (filled_qty().value() >= _target_qty.value())
return Quantity(0);
else
return Quantity(_target_qty.value() - filled_qty().value());
}
// finds the order who's out_price matches
// the arg
SpreaderChildOrderState *_find_working_order_at_price(double price) {
for (auto &o : _orders)
if (bts::securities::test_price_equality(
o.out_price().value(), price, _tick_size) &&
!o.terminal_status())
return &o;
return nullptr;
}
SpreaderChildOrderState &_highest_price_order() {
SpreaderChildOrderState *rv = &_orders[0];
for (auto &o : _orders)
if (o.out_price().value() > rv->out_price().value())
rv = &o;
return *rv;
}
SpreaderChildOrderState &_lowest_price_order() {
SpreaderChildOrderState *rv = &_orders[0];
for (auto &o : _orders)
if (o.out_price().value() < rv->out_price().value())
rv = &o;
return *rv;
}
Quantity _force_show_qty(Quantity target_qty) {
return _show_qty == 0 ? target_qty
: Quantity(std::min(target_qty.value(), _show_qty));
}
void _place_order(SpreaderChildOrderState &ord, Price price, Quantity qty) {
qty = _force_show_qty(qty);
ord.place_order(price, qty, bts::Timer::now());
}
void _modify_child_order(
SpreaderChildOrderState &ord, Price price, Quantity qty) {
qty = _force_show_qty(qty);
if (ord.out_quantity() == qty &&
bts::securities::test_price_equality(
price.value(), ord.out_price().value(), _tick_size))
return;
ord.modify_order(price, qty);
}
// given an order's filled and order qty, and the
// target left on the total qho, finds the adjusted
// qty of the order
static uint32_t _adjusted_qty(
uint32_t filled_qty,
uint32_t order_qty,
uint32_t rem_target,
bool allow_inc) {
bts_assert(filled_qty <= order_qty);
uint32_t rem_fill = order_qty - filled_qty;
if (rem_fill > rem_target) {
uint32_t dec = rem_fill - rem_target;
return order_qty - dec;
} else if (rem_fill < rem_target && allow_inc) {
uint32_t inc = rem_target - rem_fill;
return order_qty + inc;
}
return order_qty;
}
// the tick inc when traversing from the top
// (highest buy, lowest sell) down
static double _top_down_tick_inc(double tick_size, api::Side side) {
return side == api::Side::SIDE_BUY ? -1.0 * tick_size : tick_size;
}
};
} // namespace orderman
} // namespace bts
// private:
// void _modify_order(Price prev_price, Quantity) {
// double move_inc = _top_down_tick_inc(_tick_size, _side);
// int tick_diff = bts::securities::price_diff_to_ticks(
// prev_price.value(), _target_price.value(), _tick_size);
// Quantity remaining_target = _remaining();
// bool moving_up = tick_diff > 0;
// double price_i = _target_price.value();
// for (uint16_t i = 0; i < _n_eff_orders; i++) {
// SpreaderChildOrderState *order_i =
// _find_working_order_at_price(price_i);
// if (order_i == nullptr) {
// SpreaderChildOrderState &to_move =
// moving_up ? _lowest_price_order() : _highest_price_order();
// uint32_t new_ord_qty = _adjusted_qty(
// to_move.current_fills().value(), to_move.out_quantity().value(),
// remaining_target.value(),
// true); // qty inc since its
// // changing price levels
// // to_move.modify_order(Price(price_i),Quantity(new_ord_qty));
// _modify_child_order(to_move, Price(price_i), Quantity(new_ord_qty));
// } else
// _prune_qty(*order_i);
// price_i += move_inc;
// }
// }
// // decreases qty if the order at any level can overfill
// // the total, ie for an order out_qty-filled > total_rem
// // if rem=5, and this order is 3/9 this will make it 3/8
// void _prune_qty(SpreaderChildOrderState &o) {
// uint32_t new_ord_qty = _adjusted_qty(
// o.current_fills().value(), o.out_quantity().value(),
// _remaining().value(),
// false); // dont inc the qty
// bts_assert(new_ord_qty > 0);
// // o.modify_order(o.out_price(),Quantity(new_ord_qty));
// _modify_child_order(o, o.out_price(), Quantity(new_ord_qty));
// }
// Quantity _remaining() const {
// if (filled_qty().value() >= _target_qty.value())
// return Quantity(0);
// else
// return Quantity(_target_qty.value() - filled_qty().value());
// }
// // finds the order who's out_price matches
// // the arg
// SpreaderChildOrderState *_find_working_order_at_price(double price) {
// for (auto &o : _orders)
// if (bts::securities::test_price_equality(
// o.out_price().value(), price, _tick_size) &&
// !o.terminal_status())
// return &o;
// return nullptr;
// }
// SpreaderChildOrderState &_highest_price_order() {
// SpreaderChildOrderState *rv = &_orders[0];
// for (auto &o : _orders)
// if (o.out_price().value() > rv->out_price().value())
// rv = &o;
// return *rv;
// }
// SpreaderChildOrderState &_lowest_price_order() {
// SpreaderChildOrderState *rv = &_orders[0];
// for (auto &o : _orders)
// if (o.out_price().value() < rv->out_price().value())
// rv = &o;
// return *rv;
// }
// Quantity _force_show_qty(Quantity target_qty) {
// return _show_qty == 0 ? target_qty
// : Quantity(std::min(target_qty.value(), _show_qty));
// }
// void _place_order(SpreaderChildOrderState &ord, Price price, Quantity qty) {
// qty = _force_show_qty(qty);
// ord.place_order(price, qty, bts::Timer::now());
// }
// void _modify_child_order(
// SpreaderChildOrderState &ord, Price price, Quantity qty) {
// qty = _force_show_qty(qty);
// if (ord.out_quantity() == qty &&
// bts::securities::test_price_equality(
// price.value(), ord.out_price().value(), _tick_size))
// return;
// ord.modify_order(price, qty);
// }
// // given an order's filled and order qty, and the
// // target left on the total qho, finds the adjusted
// // qty of the order
// static uint32_t _adjusted_qty(
// uint32_t filled_qty,
// uint32_t order_qty,
// uint32_t rem_target,
// bool allow_inc) {
// bts_assert(filled_qty <= order_qty);
// uint32_t rem_fill = order_qty - filled_qty;
// if (rem_fill > rem_target) {
// uint32_t dec = rem_fill - rem_target;
// return order_qty - dec;
// } else if (rem_fill < rem_target && allow_inc) {
// uint32_t inc = rem_target - rem_fill;
// return order_qty + inc;
// }
// return order_qty;
// }
// // the tick inc when traversing from the top
// // (highest buy, lowest sell) down
// static double _top_down_tick_inc(double tick_size, api::Side side) {
// return side == api::Side::SIDE_BUY ? -1.0 * tick_size : tick_size;
// }
// };
// } // namespace orderman
// } // namespace bts
@@ -1,156 +1,156 @@
#include "QueueHolderOrder.h"
#include "lib/domain_model/Security.h"
#include "lib/logging/logging.h"
#include "lib/orderman/OrderBuilder.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include <algorithm>
#include <sstream>
// #include "QueueHolderOrder.h"
// #include "lib/domain_model/Security.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/OrderBuilder.h"
// #include "lib/orderman/events/BookUpdateEvent.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
// #include <algorithm>
// #include <sstream>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
QueueHolderOrder::QueueHolderOrder()
: _is_succ_inited(false), _is_recovered(false),
_termination_reason(TerminationReason::NONE), _order_def(),
_tick_size(bts::os::nan()), _order_builder(), _order_manager(),
_services(nullptr), _reported_state(), _sent_state(), _book(),
_state_worker(nullptr), _start_state_worker(this),
_waiting_for_ack_state_worker(this), _working_state_worker(this),
_waiting_for_cancel_all_state_worker(this), _end_state_worker(this) {
_switch_state(QueueHolderOrderState::START);
}
QueueHolderOrder::~QueueHolderOrder() {}
const api::orderman::OrderDefinition &QueueHolderOrder::definition() const {
return _order_def;
}
void QueueHolderOrder::on_init_new(
const api::orderman::OrderDefinition &order_def,
const PreActivationOrderServices &pre_os) {
LOG(OrderMan, Info, "QueueHolderOrder::on_init_new key:{} state:{}", key(),
_state_int());
_state_worker->on_init_new(order_def, pre_os);
}
void QueueHolderOrder::on_init_recovered(
const api::orderman::OrderRecord &rec,
const PreActivationOrderServices &pre_os) {
LOG(OrderMan, Info, "QueueHolderOrder::on_init_recovered key:{} state:{}",
key(), _state_int());
_state_worker->on_init_recovered(rec, pre_os);
}
OrderOpResult QueueHolderOrder::on_activate(
Price price, Quantity qty, OrderServices &services) {
LOG(OrderMan, Info,
"QueueHolderOrder::on_activate key:{} state:{} price:{} qty:{}", key(),
_state_int(), price, qty);
_state_worker->on_activate(price, qty, services);
_report_state();
return OrderOpResult{};
}
// order price, qty change
OrderOpResult QueueHolderOrder::on_update(Price price, Quantity qty) {
_state_worker->on_update(price, qty);
_report_state();
return OrderOpResult{};
}
OrderOpResult QueueHolderOrder::on_cancel() {
LOG(OrderMan, Info, "QueueHolderOrder::on_cancel key:{} state:{}", key(),
_state_int());
_state_worker->on_cancel();
_report_state();
return OrderOpResult{};
}
OrderOpResult QueueHolderOrder::on_detach() { return OrderOpResult{}; }
void QueueHolderOrder::_on_book_update(const BookUpdateEvent &b) {
_state_worker->on_book(b.book, b.trace);
}
void QueueHolderOrder::_on_child_order_update(const OrderStateUpdateEvent &c) {
LOG(OrderMan, Diag,
"QueueHolderOrder::_on_child_order_update key:{} state:{} "
"order_key:{} order_state:{}",
key(), _state_int(), c.key, c.status);
_state_worker->on_child_order_update(c);
_report_state();
}
void QueueHolderOrder::_on_time(const bts::mercury::TimerEvent &te) {
_state_worker->on_time(te.ts);
}
void QueueHolderOrder::_switch_state(QueueHolderOrderState new_state) {
int s0 = _state_int();
switch (new_state) {
case QueueHolderOrderState::START:
bts_assert(_state_worker == nullptr);
_state_worker = &_start_state_worker;
break;
case QueueHolderOrderState::WAITING_FOR_FIRST_ACK:
bts_assert(
_state_worker->state() == QueueHolderOrderState::START ||
_state_worker->state() == QueueHolderOrderState::WORKING);
_state_worker = &_waiting_for_ack_state_worker;
break;
case QueueHolderOrderState::WORKING:
bts_assert(
_state_worker->state() ==
QueueHolderOrderState::WAITING_FOR_FIRST_ACK);
_state_worker = &_working_state_worker;
break;
case QueueHolderOrderState::WAITING_FOR_CANCEL_ALL:
bts_assert(
_state_worker->state() ==
QueueHolderOrderState::WAITING_FOR_FIRST_ACK ||
_state_worker->state() == QueueHolderOrderState::WORKING);
_state_worker = &_waiting_for_cancel_all_state_worker;
break;
case QueueHolderOrderState::END:
bts_assert(_state_worker->state() != QueueHolderOrderState::END);
_state_worker = &_end_state_worker;
break;
}
_state_worker->on_transition();
_report_state();
LOG(OrderMan, Info, "QueueHolderOrder::_switch_state key:{} {}->{}", key(),
s0, _state_int());
}
api::orderman::OrderStatus
QueueHolderOrder::_status(std::string &message) const {
QueueHolderOrderState curr_state =
_state_worker == nullptr ? QueueHolderOrderState::START : _state();
switch (curr_state) {
case QueueHolderOrderState::START:
case QueueHolderOrderState::WAITING_FOR_FIRST_ACK:
case QueueHolderOrderState::WORKING:
case QueueHolderOrderState::WAITING_FOR_CANCEL_ALL:
return api::orderman::OrderStatus::ORDER_STATUS_WORKING;
case QueueHolderOrderState::END:
switch (_termination_reason) {
case TerminationReason::NONE: {
// this will happen if the order is
// recovered or the init failed
bts_assert(!_is_succ_inited || _is_recovered);
if (!_is_succ_inited) {
message = "Could not initialize order";
return api::orderman::OrderStatus::ORDER_STATUS_REJECTED_BY_SPARK;
} else {
message = "Recovered order";
// we could have done better here by [reserving ]
return api::orderman::OrderStatus::ORDER_STATUS_COMPLETED;
}
}
case TerminationReason::EXTERNAL_CANCEL:
return api::orderman::OrderStatus::ORDER_STATUS_CANCELED;
case TerminationReason::FILLED:
return api::orderman::OrderStatus::ORDER_STATUS_COMPLETED;
case TerminationReason::UNEXPECTED_CHILD_CANCEL:
message = "Child order cancelled";
return api::orderman::OrderStatus::ORDER_STATUS_CANCELED;
case TerminationReason::CHILD_ERROR:
message = "Child order error";
return api::orderman::OrderStatus::ORDER_STATUS_CANCELED;
}
}
return api::orderman::OrderStatus::ORDER_STATUS_WORKING;
}
} // namespace orderman
} // namespace bts
// QueueHolderOrder::QueueHolderOrder()
// : _is_succ_inited(false), _is_recovered(false),
// _termination_reason(TerminationReason::NONE), _order_def(),
// _tick_size(bts::os::nan()), _order_builder(), _order_manager(),
// _services(nullptr), _reported_state(), _sent_state(), _book(),
// _state_worker(nullptr), _start_state_worker(this),
// _waiting_for_ack_state_worker(this), _working_state_worker(this),
// _waiting_for_cancel_all_state_worker(this), _end_state_worker(this) {
// _switch_state(QueueHolderOrderState::START);
// }
// QueueHolderOrder::~QueueHolderOrder() {}
// const api::orderman::OrderDefinition &QueueHolderOrder::definition() const {
// return _order_def;
// }
// void QueueHolderOrder::on_init_new(
// const api::orderman::OrderDefinition &order_def,
// const PreActivationOrderServices &pre_os) {
// LOG(OrderMan, Info, "QueueHolderOrder::on_init_new key:{} state:{}", key(),
// _state_int());
// _state_worker->on_init_new(order_def, pre_os);
// }
// void QueueHolderOrder::on_init_recovered(
// const api::orderman::OrderRecord &rec,
// const PreActivationOrderServices &pre_os) {
// LOG(OrderMan, Info, "QueueHolderOrder::on_init_recovered key:{} state:{}",
// key(), _state_int());
// _state_worker->on_init_recovered(rec, pre_os);
// }
// OrderOpResult QueueHolderOrder::on_activate(
// Price price, Quantity qty, OrderServices &services) {
// LOG(OrderMan, Info,
// "QueueHolderOrder::on_activate key:{} state:{} price:{} qty:{}", key(),
// _state_int(), price, qty);
// _state_worker->on_activate(price, qty, services);
// _report_state();
// return OrderOpResult{};
// }
// // order price, qty change
// OrderOpResult QueueHolderOrder::on_update(Price price, Quantity qty) {
// _state_worker->on_update(price, qty);
// _report_state();
// return OrderOpResult{};
// }
// OrderOpResult QueueHolderOrder::on_cancel() {
// LOG(OrderMan, Info, "QueueHolderOrder::on_cancel key:{} state:{}", key(),
// _state_int());
// _state_worker->on_cancel();
// _report_state();
// return OrderOpResult{};
// }
// OrderOpResult QueueHolderOrder::on_detach() { return OrderOpResult{}; }
// void QueueHolderOrder::_on_book_update(const BookUpdateEvent &b) {
// _state_worker->on_book(b.book, b.trace);
// }
// void QueueHolderOrder::_on_child_order_update(const OrderStateUpdateEvent &c) {
// LOG(OrderMan, Diag,
// "QueueHolderOrder::_on_child_order_update key:{} state:{} "
// "order_key:{} order_state:{}",
// key(), _state_int(), c.key, c.status);
// _state_worker->on_child_order_update(c);
// _report_state();
// }
// void QueueHolderOrder::_on_time(const bts::mercury::TimerEvent &te) {
// _state_worker->on_time(te.ts);
// }
// void QueueHolderOrder::_switch_state(QueueHolderOrderState new_state) {
// int s0 = _state_int();
// switch (new_state) {
// case QueueHolderOrderState::START:
// bts_assert(_state_worker == nullptr);
// _state_worker = &_start_state_worker;
// break;
// case QueueHolderOrderState::WAITING_FOR_FIRST_ACK:
// bts_assert(
// _state_worker->state() == QueueHolderOrderState::START ||
// _state_worker->state() == QueueHolderOrderState::WORKING);
// _state_worker = &_waiting_for_ack_state_worker;
// break;
// case QueueHolderOrderState::WORKING:
// bts_assert(
// _state_worker->state() ==
// QueueHolderOrderState::WAITING_FOR_FIRST_ACK);
// _state_worker = &_working_state_worker;
// break;
// case QueueHolderOrderState::WAITING_FOR_CANCEL_ALL:
// bts_assert(
// _state_worker->state() ==
// QueueHolderOrderState::WAITING_FOR_FIRST_ACK ||
// _state_worker->state() == QueueHolderOrderState::WORKING);
// _state_worker = &_waiting_for_cancel_all_state_worker;
// break;
// case QueueHolderOrderState::END:
// bts_assert(_state_worker->state() != QueueHolderOrderState::END);
// _state_worker = &_end_state_worker;
// break;
// }
// _state_worker->on_transition();
// _report_state();
// LOG(OrderMan, Info, "QueueHolderOrder::_switch_state key:{} {}->{}", key(),
// s0, _state_int());
// }
// api::orderman::OrderStatus
// QueueHolderOrder::_status(std::string &message) const {
// QueueHolderOrderState curr_state =
// _state_worker == nullptr ? QueueHolderOrderState::START : _state();
// switch (curr_state) {
// case QueueHolderOrderState::START:
// case QueueHolderOrderState::WAITING_FOR_FIRST_ACK:
// case QueueHolderOrderState::WORKING:
// case QueueHolderOrderState::WAITING_FOR_CANCEL_ALL:
// return api::orderman::OrderStatus::ORDER_STATUS_WORKING;
// case QueueHolderOrderState::END:
// switch (_termination_reason) {
// case TerminationReason::NONE: {
// // this will happen if the order is
// // recovered or the init failed
// bts_assert(!_is_succ_inited || _is_recovered);
// if (!_is_succ_inited) {
// message = "Could not initialize order";
// return api::orderman::OrderStatus::ORDER_STATUS_REJECTED_BY_SPARK;
// } else {
// message = "Recovered order";
// // we could have done better here by [reserving ]
// return api::orderman::OrderStatus::ORDER_STATUS_COMPLETED;
// }
// }
// case TerminationReason::EXTERNAL_CANCEL:
// return api::orderman::OrderStatus::ORDER_STATUS_CANCELED;
// case TerminationReason::FILLED:
// return api::orderman::OrderStatus::ORDER_STATUS_COMPLETED;
// case TerminationReason::UNEXPECTED_CHILD_CANCEL:
// message = "Child order cancelled";
// return api::orderman::OrderStatus::ORDER_STATUS_CANCELED;
// case TerminationReason::CHILD_ERROR:
// message = "Child order error";
// return api::orderman::OrderStatus::ORDER_STATUS_CANCELED;
// }
// }
// return api::orderman::OrderStatus::ORDER_STATUS_WORKING;
// }
// } // namespace orderman
// } // namespace bts
@@ -1,181 +1,181 @@
#pragma once
// #pragma once
#include "lib/api/services/OrderService.server.pb.h"
#include "lib/domain_model/Security.h"
#include "lib/orderman/orders/Order.h"
#include "lib/orderman/orders/OrderOpResult.h"
#include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
#include "lib/orderman/orders/queue_holder/QHOOrderManager.h"
#include "lib/orderman/orders/queue_holder/state_workers/QHOEndStateWorker.h"
#include "lib/orderman/orders/queue_holder/state_workers/QHOStartStateWorker.h"
#include "lib/orderman/orders/queue_holder/state_workers/QHOWaitingForAckStateWorker.h"
#include "lib/orderman/orders/queue_holder/state_workers/QHOWaitingForCancelAllStateWorker.h"
#include "lib/orderman/orders/queue_holder/state_workers/QHOWorkingStateWorker.h"
#include <memory>
// #include "lib/api/services/OrderService.server.pb.h"
// #include "lib/domain_model/Security.h"
// #include "lib/orderman/orders/Order.h"
// #include "lib/orderman/orders/OrderOpResult.h"
// #include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
// #include "lib/orderman/orders/queue_holder/QHOOrderManager.h"
// #include "lib/orderman/orders/queue_holder/state_workers/QHOEndStateWorker.h"
// #include "lib/orderman/orders/queue_holder/state_workers/QHOStartStateWorker.h"
// #include "lib/orderman/orders/queue_holder/state_workers/QHOWaitingForAckStateWorker.h"
// #include "lib/orderman/orders/queue_holder/state_workers/QHOWaitingForCancelAllStateWorker.h"
// #include "lib/orderman/orders/queue_holder/state_workers/QHOWorkingStateWorker.h"
// #include <memory>
namespace bts {
namespace api {
namespace orderman {
class OrderDefinition;
}
} // namespace api
namespace mercury {
class TimerEvent;
}
namespace md {
class PriceSize;
}
namespace orderman {
// namespace bts {
// namespace api {
// namespace orderman {
// class OrderDefinition;
// }
// } // namespace api
// namespace mercury {
// class TimerEvent;
// }
// namespace md {
// class PriceSize;
// }
// namespace orderman {
class OrderStateUpdateEvent;
class BookUpdateEvent;
class PreActivationOrderServices;
// class OrderStateUpdateEvent;
// class BookUpdateEvent;
// class PreActivationOrderServices;
class QueueHolderOrder : public Order {
friend QHOStartStateWorker;
friend QHOWaitingForAckStateWorker;
friend QHOWorkingStateWorker;
friend QHOWaitingForCancelAllStateWorker;
friend QHOEndStateWorker;
// class QueueHolderOrder : public Order {
// friend QHOStartStateWorker;
// friend QHOWaitingForAckStateWorker;
// friend QHOWorkingStateWorker;
// friend QHOWaitingForCancelAllStateWorker;
// friend QHOEndStateWorker;
public:
QueueHolderOrder();
QueueHolderOrder(const QueueHolderOrder &) = delete;
QueueHolderOrder(QueueHolderOrder &&) = delete;
QueueHolderOrder &operator=(const QueueHolderOrder &) = delete;
QueueHolderOrder &operator=(QueueHolderOrder &&) = delete;
virtual ~QueueHolderOrder();
api::orderman::OrderDefinition const &definition() const;
void on_init_new(
api::orderman::OrderDefinition const &,
const PreActivationOrderServices &) override;
void on_init_recovered(
api::orderman::OrderRecord const &,
const PreActivationOrderServices &) override;
OrderOpResult on_activate(Price, Quantity, OrderServices &) override;
OrderOpResult on_update(Price, Quantity) override;
OrderOpResult on_cancel() override;
OrderOpResult on_detach() override;
// public:
// QueueHolderOrder();
// QueueHolderOrder(const QueueHolderOrder &) = delete;
// QueueHolderOrder(QueueHolderOrder &&) = delete;
// QueueHolderOrder &operator=(const QueueHolderOrder &) = delete;
// QueueHolderOrder &operator=(QueueHolderOrder &&) = delete;
// virtual ~QueueHolderOrder();
// api::orderman::OrderDefinition const &definition() const;
// void on_init_new(
// api::orderman::OrderDefinition const &,
// const PreActivationOrderServices &) override;
// void on_init_recovered(
// api::orderman::OrderRecord const &,
// const PreActivationOrderServices &) override;
// OrderOpResult on_activate(Price, Quantity, OrderServices &) override;
// OrderOpResult on_update(Price, Quantity) override;
// OrderOpResult on_cancel() override;
// OrderOpResult on_detach() override;
private:
enum class TerminationReason {
// initial value at the time of the first cancel
NONE,
EXTERNAL_CANCEL,
// order got filled (since there are multiple
// orders we need to cancel the rest)
FILLED,
// one or more child orders got cancelled
UNEXPECTED_CHILD_CANCEL,
// child order errored out
CHILD_ERROR
};
// private:
// enum class TerminationReason {
// // initial value at the time of the first cancel
// NONE,
// EXTERNAL_CANCEL,
// // order got filled (since there are multiple
// // orders we need to cancel the rest)
// FILLED,
// // one or more child orders got cancelled
// UNEXPECTED_CHILD_CANCEL,
// // child order errored out
// CHILD_ERROR
// };
private:
// callbacks from book and child order udpates
void _on_book_update(const BookUpdateEvent &);
void _on_child_order_update(OrderStateUpdateEvent const &);
void _on_time(const bts::mercury::TimerEvent &);
// private:
// // callbacks from book and child order udpates
// void _on_book_update(const BookUpdateEvent &);
// void _on_child_order_update(OrderStateUpdateEvent const &);
// void _on_time(const bts::mercury::TimerEvent &);
private:
QueueHolderOrderState _state() const { return _state_worker->state(); }
int _state_int() const {
if (_state_worker == nullptr)
return -1;
return static_cast<int>(_state_worker->state());
}
bool _activated() const { return _services != nullptr; }
void _cancel_order(TerminationReason termination_reason) {
bts_assert(termination_reason != TerminationReason::NONE);
bts_assert(_termination_reason == TerminationReason::NONE);
_termination_reason = termination_reason;
_switch_state(QueueHolderOrderState::WAITING_FOR_CANCEL_ALL);
}
api::orderman::OrderStatus _status(std::string &message) const;
// sends orders for the first time
void _report_state() {
if (_activated()) {
std::string &err_msg = *(_reported_state.mutable_last_msg());
// both status and err_msg are set here
_reported_state.set_status(_status(err_msg));
double filled_price;
uint32_t filled_qty;
_order_manager.filled_price(filled_price, filled_qty);
if (filled_qty > _reported_state.filled_quantity()) {
// this means there was a fill reported bw last report
// and this, hence last_price needs to be set
uint32_t last_trade_qty =
filled_qty - _reported_state.filled_quantity();
double last_trade_price =
filled_price * filled_qty -
_reported_state.avg_price() * _reported_state.filled_quantity();
last_trade_price /= last_trade_qty;
_reported_state.set_last_price(last_trade_price);
}
_reported_state.set_avg_price(filled_price);
_reported_state.set_filled_quantity(filled_qty);
_reported_state.set_display_status(
bts::api::orderman::OrderState::HIDDEN);
if (!_state_eq()) {
_sent_state = _reported_state;
_services->report_state(_reported_state);
}
}
}
void _set_state_price_qty(Price price, Quantity qty) {
_reported_state.set_price(price.value());
_reported_state.set_quantity(qty.value());
}
bool _filled() const {
return _order_manager.filled_qty().value() >= _reported_state.quantity();
}
// private:
// QueueHolderOrderState _state() const { return _state_worker->state(); }
// int _state_int() const {
// if (_state_worker == nullptr)
// return -1;
// return static_cast<int>(_state_worker->state());
// }
// bool _activated() const { return _services != nullptr; }
// void _cancel_order(TerminationReason termination_reason) {
// bts_assert(termination_reason != TerminationReason::NONE);
// bts_assert(_termination_reason == TerminationReason::NONE);
// _termination_reason = termination_reason;
// _switch_state(QueueHolderOrderState::WAITING_FOR_CANCEL_ALL);
// }
// api::orderman::OrderStatus _status(std::string &message) const;
// // sends orders for the first time
// void _report_state() {
// if (_activated()) {
// std::string &err_msg = *(_reported_state.mutable_last_msg());
// // both status and err_msg are set here
// _reported_state.set_status(_status(err_msg));
// double filled_price;
// uint32_t filled_qty;
// _order_manager.filled_price(filled_price, filled_qty);
// if (filled_qty > _reported_state.filled_quantity()) {
// // this means there was a fill reported bw last report
// // and this, hence last_price needs to be set
// uint32_t last_trade_qty =
// filled_qty - _reported_state.filled_quantity();
// double last_trade_price =
// filled_price * filled_qty -
// _reported_state.avg_price() * _reported_state.filled_quantity();
// last_trade_price /= last_trade_qty;
// _reported_state.set_last_price(last_trade_price);
// }
// _reported_state.set_avg_price(filled_price);
// _reported_state.set_filled_quantity(filled_qty);
// _reported_state.set_display_status(
// bts::api::orderman::OrderState::HIDDEN);
// if (!_state_eq()) {
// _sent_state = _reported_state;
// _services->report_state(_reported_state);
// }
// }
// }
// void _set_state_price_qty(Price price, Quantity qty) {
// _reported_state.set_price(price.value());
// _reported_state.set_quantity(qty.value());
// }
// bool _filled() const {
// return _order_manager.filled_qty().value() >= _reported_state.quantity();
// }
void _switch_state(QueueHolderOrderState new_state);
// void _switch_state(QueueHolderOrderState new_state);
void _assert_false(const std::string &str) const {
std::ostringstream oss;
oss << str << " key:" << key();
bts_assert_msg(false, oss.str());
}
bool _state_eq() const {
return _reported_state.status() == _sent_state.status() &&
securities::is_same(
Price(_reported_state.price()), Price(_sent_state.price())) &&
_reported_state.quantity() == _sent_state.quantity() &&
_reported_state.filled_quantity() ==
_sent_state.filled_quantity() &&
securities::is_same(
Price(_reported_state.last_price()),
Price(_sent_state.last_price())) &&
securities::is_same(
Price(_reported_state.avg_price()),
Price(_sent_state.avg_price())) &&
_reported_state.last_msg() == _sent_state.last_msg();
}
// void _assert_false(const std::string &str) const {
// std::ostringstream oss;
// oss << str << " key:" << key();
// bts_assert_msg(false, oss.str());
// }
// bool _state_eq() const {
// return _reported_state.status() == _sent_state.status() &&
// securities::is_same(
// Price(_reported_state.price()), Price(_sent_state.price())) &&
// _reported_state.quantity() == _sent_state.quantity() &&
// _reported_state.filled_quantity() ==
// _sent_state.filled_quantity() &&
// securities::is_same(
// Price(_reported_state.last_price()),
// Price(_sent_state.last_price())) &&
// securities::is_same(
// Price(_reported_state.avg_price()),
// Price(_sent_state.avg_price())) &&
// _reported_state.last_msg() == _sent_state.last_msg();
// }
private:
// if the init call succeeded which means
// i. order is properly built
// ii. security was successfully resolved
bool _is_succ_inited;
// tells if the order is a recovered order
// (as opposed to a new order)
bool _is_recovered;
TerminationReason _termination_reason;
api::orderman::OrderDefinition _order_def;
double _tick_size;
OrderBuilder _order_builder;
QHOOrderManager _order_manager;
OrderServices *_services;
api::orderman::OrderState _reported_state, _sent_state;
Book _book;
IQueueHolderOrderStateWorker *_state_worker;
QHOStartStateWorker _start_state_worker;
QHOWaitingForAckStateWorker _waiting_for_ack_state_worker;
QHOWorkingStateWorker _working_state_worker;
QHOWaitingForCancelAllStateWorker _waiting_for_cancel_all_state_worker;
QHOEndStateWorker _end_state_worker;
};
// private:
// // if the init call succeeded which means
// // i. order is properly built
// // ii. security was successfully resolved
// bool _is_succ_inited;
// // tells if the order is a recovered order
// // (as opposed to a new order)
// bool _is_recovered;
// TerminationReason _termination_reason;
// api::orderman::OrderDefinition _order_def;
// double _tick_size;
// OrderBuilder _order_builder;
// QHOOrderManager _order_manager;
// OrderServices *_services;
// api::orderman::OrderState _reported_state, _sent_state;
// Book _book;
// IQueueHolderOrderStateWorker *_state_worker;
// QHOStartStateWorker _start_state_worker;
// QHOWaitingForAckStateWorker _waiting_for_ack_state_worker;
// QHOWorkingStateWorker _working_state_worker;
// QHOWaitingForCancelAllStateWorker _waiting_for_cancel_all_state_worker;
// QHOEndStateWorker _end_state_worker;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,22 +1,22 @@
#pragma once
// #pragma once
namespace bts {
namespace orderman {
enum class QueueHolderOrderState {
// remains here till on_activate is called
START = 0,
// once the order is submitted it gets into
// this state until it gets an ack on all
// the sub orders
WAITING_FOR_FIRST_ACK = 1,
// all orders are ack'ed, free to accept
// updates
WORKING = 2,
// cancel has been sent, wait for all
// sub orders to be in final state
WAITING_FOR_CANCEL_ALL = 3,
// final state, no going back
END = 4
};
}
} // namespace bts
// namespace bts {
// namespace orderman {
// enum class QueueHolderOrderState {
// // remains here till on_activate is called
// START = 0,
// // once the order is submitted it gets into
// // this state until it gets an ack on all
// // the sub orders
// WAITING_FOR_FIRST_ACK = 1,
// // all orders are ack'ed, free to accept
// // updates
// WORKING = 2,
// // cancel has been sent, wait for all
// // sub orders to be in final state
// WAITING_FOR_CANCEL_ALL = 3,
// // final state, no going back
// END = 4
// };
// }
// } // namespace bts
@@ -1,55 +1,55 @@
#include "lib/orderman/orders/queue_holder/state_workers/QHOEndStateWorker.h"
#include "lib/domain_model/Security.h"
#include "lib/logging/logging.h"
#include "lib/orderman/OrderBuilder.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include <algorithm>
#include <sstream>
// #include "lib/orderman/orders/queue_holder/state_workers/QHOEndStateWorker.h"
// #include "lib/domain_model/Security.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/OrderBuilder.h"
// #include "lib/orderman/events/BookUpdateEvent.h"
// #include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
// #include <algorithm>
// #include <sstream>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
QHOEndStateWorker::QHOEndStateWorker(QueueHolderOrder *parent)
: _porder(parent) {}
// QHOEndStateWorker::QHOEndStateWorker(QueueHolderOrder *parent)
// : _porder(parent) {}
void QHOEndStateWorker::on_transition() {}
// void QHOEndStateWorker::on_transition() {}
void QHOEndStateWorker::on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) {
_porder->_assert_false("QHOEndStateWorker::on_init_new");
}
void QHOEndStateWorker::on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) {
_porder->_assert_false("QHOEndStateWorker::on_init_recovered");
}
void QHOEndStateWorker::on_activate(Price, Quantity, OrderServices &) {
_porder->_assert_false("QHOEndStateWorker::on_activate");
}
void QHOEndStateWorker::on_update(Price, Quantity) {
LOG(OrderMan, Warn,
"QHOEndStateWorker::on_update "
"key:{} rejected",
_porder->key());
}
void QHOEndStateWorker::on_cancel() {
LOG(OrderMan, Warn,
"QHOEndStateWorker::on_cancel "
"key:{} already in termminal state",
_porder->key());
}
void QHOEndStateWorker::on_child_order_update(const OrderStateUpdateEvent &ev) {
LOG(OrderMan, Error,
"QHOEndStateWorker::on_child_order_update "
"key:{} order_key:{}",
_porder->key(), ev.key);
}
void QHOEndStateWorker::on_book(const Book &, const trace::CorID &) {}
void QHOEndStateWorker::on_time(const TimeStamp &) {}
} // namespace orderman
} // namespace bts
// void QHOEndStateWorker::on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) {
// _porder->_assert_false("QHOEndStateWorker::on_init_new");
// }
// void QHOEndStateWorker::on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) {
// _porder->_assert_false("QHOEndStateWorker::on_init_recovered");
// }
// void QHOEndStateWorker::on_activate(Price, Quantity, OrderServices &) {
// _porder->_assert_false("QHOEndStateWorker::on_activate");
// }
// void QHOEndStateWorker::on_update(Price, Quantity) {
// LOG(OrderMan, Warn,
// "QHOEndStateWorker::on_update "
// "key:{} rejected",
// _porder->key());
// }
// void QHOEndStateWorker::on_cancel() {
// LOG(OrderMan, Warn,
// "QHOEndStateWorker::on_cancel "
// "key:{} already in termminal state",
// _porder->key());
// }
// void QHOEndStateWorker::on_child_order_update(const OrderStateUpdateEvent &ev) {
// LOG(OrderMan, Error,
// "QHOEndStateWorker::on_child_order_update "
// "key:{} order_key:{}",
// _porder->key(), ev.key);
// }
// void QHOEndStateWorker::on_book(const Book &, const trace::CorID &) {}
// void QHOEndStateWorker::on_time(const TimeStamp &) {}
// } // namespace orderman
// } // namespace bts
@@ -1,36 +1,36 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
// #include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class QueueHolderOrder;
// this is the starting node. since the hedger
// can be reused, the end state can lead back to this
class QHOEndStateWorker : public IQueueHolderOrderStateWorker {
private:
QueueHolderOrder *const _porder;
// class QueueHolderOrder;
// // this is the starting node. since the hedger
// // can be reused, the end state can lead back to this
// class QHOEndStateWorker : public IQueueHolderOrderStateWorker {
// private:
// QueueHolderOrder *const _porder;
public:
QHOEndStateWorker(QueueHolderOrder *parent);
QueueHolderOrderState state() const override {
return QueueHolderOrderState::END;
}
void on_transition() override;
void on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) override;
void on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) override;
void on_activate(Price, Quantity, OrderServices &) override;
void on_update(Price, Quantity) override;
void on_cancel() override;
void on_child_order_update(const OrderStateUpdateEvent &) override;
void on_book(const Book &, const trace::CorID &) override;
void on_time(const TimeStamp &) override;
};
// public:
// QHOEndStateWorker(QueueHolderOrder *parent);
// QueueHolderOrderState state() const override {
// return QueueHolderOrderState::END;
// }
// void on_transition() override;
// void on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) override;
// void on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) override;
// void on_activate(Price, Quantity, OrderServices &) override;
// void on_update(Price, Quantity) override;
// void on_cancel() override;
// void on_child_order_update(const OrderStateUpdateEvent &) override;
// void on_book(const Book &, const trace::CorID &) override;
// void on_time(const TimeStamp &) override;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,120 +1,120 @@
#include "lib/orderman/orders/queue_holder/state_workers/QHOStartStateWorker.h"
#include "lib/domain_model/Security.h"
#include "lib/logging/logging.h"
#include "lib/orderman/OrderBuilder.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/orders/PreActivationOrderServices.h"
#include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include <algorithm>
#include <sstream>
// #include "lib/orderman/orders/queue_holder/state_workers/QHOStartStateWorker.h"
// #include "lib/domain_model/Security.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/OrderBuilder.h"
// #include "lib/orderman/events/BookUpdateEvent.h"
// #include "lib/orderman/orders/PreActivationOrderServices.h"
// #include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
// #include <algorithm>
// #include <sstream>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
QHOStartStateWorker::QHOStartStateWorker(QueueHolderOrder *parent)
: _porder(parent) {}
// QHOStartStateWorker::QHOStartStateWorker(QueueHolderOrder *parent)
// : _porder(parent) {}
void QHOStartStateWorker::on_transition() {}
// void QHOStartStateWorker::on_transition() {}
void QHOStartStateWorker::on_init_new(
const api::orderman::OrderDefinition &order_def,
const PreActivationOrderServices &pre_os) {
_porder->_is_succ_inited = _on_init_new(order_def, pre_os);
}
void QHOStartStateWorker::on_init_recovered(
const bts::api::orderman::OrderRecord &rec,
const PreActivationOrderServices &pre_os) {
_porder->_is_succ_inited = _on_init_new(rec.def(), pre_os);
_porder->_is_recovered = true;
}
void QHOStartStateWorker::on_activate(
Price price, Quantity qty, OrderServices &services) {
api::orderman::OrderState &state = _porder->_reported_state;
// reference is kept even for orders that are not initialized
// properly or when constructed from the disk
_porder->_services = &services;
state.set_price(price.value());
state.set_quantity(qty.value());
// failed to init or recovered orders are sent directly
// to end (recovered have to be dealt with later)
if (_porder->_is_recovered || !_porder->_is_succ_inited)
_porder->_switch_state(QueueHolderOrderState::END);
else {
const auto &qho_order = _porder->_order_def.custom_def().queue_holder();
_porder->_order_builder.activate(services);
_porder->_order_manager.init(
_porder->_order_builder,
SecurityKey(_porder->_order_def.security_key()),
_porder->_order_def.side(), _porder->_tick_size,
qho_order.n_queue_orders(),
qho_order.has_show_quantity() ? qho_order.show_quantity().quantity()
: 0,
price, qty);
services.subscribe_to_child_updates(
[this](const OrderStateUpdateEvent &os) {
_porder->_on_child_order_update(os);
});
services.subscribe_to_book(
SecurityKey(_porder->_order_def.security_key()),
[this](const BookUpdateEvent &bk_event) {
_porder->_on_book_update(bk_event);
});
services.subscribe_to_time([this](const bts::mercury::TimerEvent &te) {
_porder->_on_time(te);
});
_porder->_switch_state(QueueHolderOrderState::WAITING_FOR_FIRST_ACK);
}
}
void QHOStartStateWorker::on_update(Price, Quantity) {
_porder->_assert_false("QHOStartStateWorker::on_update");
}
void QHOStartStateWorker::on_cancel() {
_porder->_assert_false("QHOStartStateWorker::on_cancel");
}
void QHOStartStateWorker::on_child_order_update(const OrderStateUpdateEvent &) {
_porder->_assert_false("QHOStartStateWorker::on_child_order_update");
}
void QHOStartStateWorker::on_book(const Book &, const trace::CorID &) {
_porder->_assert_false("QHOStartStateWorker::on_book");
}
void QHOStartStateWorker::on_time(const TimeStamp &) {
_porder->_assert_false("QHOStartStateWorker::on_time");
}
bool QHOStartStateWorker::_on_init_new(
const api::orderman::OrderDefinition &order_def,
const PreActivationOrderServices &pre_os) {
_porder->_order_def = order_def;
if (!order_def.has_custom_def() ||
!order_def.custom_def().has_queue_holder()) {
LOG(OrderMan, Error,
"QHOStartStateWorker::_on_init_new key:{} "
"OrderDefinition does not have QueueHolderDefinition",
_porder->key());
return false;
}
const bts::api::Security *sec_ptr =
pre_os.sec_db.resolve(SecurityKey(order_def.security_key()));
if (sec_ptr == nullptr) {
LOG(OrderMan, Error,
"QHOStartStateWorker::_on_init_new key:{} "
"Could not resolve sec_key:{}",
_porder->key(), order_def.security_key());
return false;
}
_porder->_tick_size = sec_ptr->contract_spec().low_tick();
const api::orderman::QueueHolderDefinition &qho_def =
order_def.custom_def().queue_holder();
api::orderman::OrderDefinition child_order_def = order_def;
child_order_def.mutable_custom_def()->CopyFrom(qho_def.child());
_porder->_order_builder = OrderBuilder(
_porder->key(), child_order_def, pre_os.order_factory,
qho_def.n_queue_orders() + 1);
LOG(OrderMan, Info, "QHOStartStateWorker::_on_init_new key:{} ",
_porder->key());
return true;
}
} // namespace orderman
} // namespace bts
// void QHOStartStateWorker::on_init_new(
// const api::orderman::OrderDefinition &order_def,
// const PreActivationOrderServices &pre_os) {
// _porder->_is_succ_inited = _on_init_new(order_def, pre_os);
// }
// void QHOStartStateWorker::on_init_recovered(
// const bts::api::orderman::OrderRecord &rec,
// const PreActivationOrderServices &pre_os) {
// _porder->_is_succ_inited = _on_init_new(rec.def(), pre_os);
// _porder->_is_recovered = true;
// }
// void QHOStartStateWorker::on_activate(
// Price price, Quantity qty, OrderServices &services) {
// api::orderman::OrderState &state = _porder->_reported_state;
// // reference is kept even for orders that are not initialized
// // properly or when constructed from the disk
// _porder->_services = &services;
// state.set_price(price.value());
// state.set_quantity(qty.value());
// // failed to init or recovered orders are sent directly
// // to end (recovered have to be dealt with later)
// if (_porder->_is_recovered || !_porder->_is_succ_inited)
// _porder->_switch_state(QueueHolderOrderState::END);
// else {
// const auto &qho_order = _porder->_order_def.custom_def().queue_holder();
// _porder->_order_builder.activate(services);
// _porder->_order_manager.init(
// _porder->_order_builder,
// SecurityKey(_porder->_order_def.security_key()),
// _porder->_order_def.side(), _porder->_tick_size,
// qho_order.n_queue_orders(),
// qho_order.has_show_quantity() ? qho_order.show_quantity().quantity()
// : 0,
// price, qty);
// services.subscribe_to_child_updates(
// [this](const OrderStateUpdateEvent &os) {
// _porder->_on_child_order_update(os);
// });
// services.subscribe_to_book(
// SecurityKey(_porder->_order_def.security_key()),
// [this](const BookUpdateEvent &bk_event) {
// _porder->_on_book_update(bk_event);
// });
// services.subscribe_to_time([this](const bts::mercury::TimerEvent &te) {
// _porder->_on_time(te);
// });
// _porder->_switch_state(QueueHolderOrderState::WAITING_FOR_FIRST_ACK);
// }
// }
// void QHOStartStateWorker::on_update(Price, Quantity) {
// _porder->_assert_false("QHOStartStateWorker::on_update");
// }
// void QHOStartStateWorker::on_cancel() {
// _porder->_assert_false("QHOStartStateWorker::on_cancel");
// }
// void QHOStartStateWorker::on_child_order_update(const OrderStateUpdateEvent &) {
// _porder->_assert_false("QHOStartStateWorker::on_child_order_update");
// }
// void QHOStartStateWorker::on_book(const Book &, const trace::CorID &) {
// _porder->_assert_false("QHOStartStateWorker::on_book");
// }
// void QHOStartStateWorker::on_time(const TimeStamp &) {
// _porder->_assert_false("QHOStartStateWorker::on_time");
// }
// bool QHOStartStateWorker::_on_init_new(
// const api::orderman::OrderDefinition &order_def,
// const PreActivationOrderServices &pre_os) {
// _porder->_order_def = order_def;
// if (!order_def.has_custom_def() ||
// !order_def.custom_def().has_queue_holder()) {
// LOG(OrderMan, Error,
// "QHOStartStateWorker::_on_init_new key:{} "
// "OrderDefinition does not have QueueHolderDefinition",
// _porder->key());
// return false;
// }
// const bts::api::Security *sec_ptr =
// pre_os.sec_db.resolve(SecurityKey(order_def.security_key()));
// if (sec_ptr == nullptr) {
// LOG(OrderMan, Error,
// "QHOStartStateWorker::_on_init_new key:{} "
// "Could not resolve sec_key:{}",
// _porder->key(), order_def.security_key());
// return false;
// }
// _porder->_tick_size = sec_ptr->contract_spec().low_tick();
// const api::orderman::QueueHolderDefinition &qho_def =
// order_def.custom_def().queue_holder();
// api::orderman::OrderDefinition child_order_def = order_def;
// child_order_def.mutable_custom_def()->CopyFrom(qho_def.child());
// _porder->_order_builder = OrderBuilder(
// _porder->key(), child_order_def, pre_os.order_factory,
// qho_def.n_queue_orders() + 1);
// LOG(OrderMan, Info, "QHOStartStateWorker::_on_init_new key:{} ",
// _porder->key());
// return true;
// }
// } // namespace orderman
// } // namespace bts
@@ -1,44 +1,44 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
// #include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class QueueHolderOrder;
// this is the starting state where the int_new/recover
// and activation is done. upon activation
// if everything is good, it places suborders and moves
// transitions to WAITING_FOR_FIRST_ACK
// else transitions to END
class QHOStartStateWorker : public IQueueHolderOrderStateWorker {
private:
QueueHolderOrder *const _porder;
// class QueueHolderOrder;
// // this is the starting state where the int_new/recover
// // and activation is done. upon activation
// // if everything is good, it places suborders and moves
// // transitions to WAITING_FOR_FIRST_ACK
// // else transitions to END
// class QHOStartStateWorker : public IQueueHolderOrderStateWorker {
// private:
// QueueHolderOrder *const _porder;
public:
QHOStartStateWorker(QueueHolderOrder *parent);
QueueHolderOrderState state() const override {
return QueueHolderOrderState::START;
}
void on_transition() override;
void on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) override;
void on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) override;
void on_activate(Price, Quantity, OrderServices &) override;
void on_update(Price, Quantity) override;
void on_cancel() override;
void on_child_order_update(const OrderStateUpdateEvent &) override;
void on_book(const Book &, const trace::CorID &) override;
void on_time(const TimeStamp &) override;
// public:
// QHOStartStateWorker(QueueHolderOrder *parent);
// QueueHolderOrderState state() const override {
// return QueueHolderOrderState::START;
// }
// void on_transition() override;
// void on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) override;
// void on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) override;
// void on_activate(Price, Quantity, OrderServices &) override;
// void on_update(Price, Quantity) override;
// void on_cancel() override;
// void on_child_order_update(const OrderStateUpdateEvent &) override;
// void on_book(const Book &, const trace::CorID &) override;
// void on_time(const TimeStamp &) override;
private:
bool _on_init_new(
const api::orderman::OrderDefinition &order_def,
const PreActivationOrderServices &pre_os);
};
// private:
// bool _on_init_new(
// const api::orderman::OrderDefinition &order_def,
// const PreActivationOrderServices &pre_os);
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,71 +1,71 @@
#include "lib/orderman/orders/queue_holder/state_workers/QHOWaitingForAckStateWorker.h"
#include "lib/domain_model/Security.h"
#include "lib/logging/logging.h"
#include "lib/orderman/OrderBuilder.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include <algorithm>
#include <sstream>
// #include "lib/orderman/orders/queue_holder/state_workers/QHOWaitingForAckStateWorker.h"
// #include "lib/domain_model/Security.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/OrderBuilder.h"
// #include "lib/orderman/events/BookUpdateEvent.h"
// #include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
// #include <algorithm>
// #include <sstream>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
QHOWaitingForAckStateWorker::QHOWaitingForAckStateWorker(
QueueHolderOrder *parent)
: _porder(parent), _orders_placed_once(false) {}
// QHOWaitingForAckStateWorker::QHOWaitingForAckStateWorker(
// QueueHolderOrder *parent)
// : _porder(parent), _orders_placed_once(false) {}
void QHOWaitingForAckStateWorker::on_transition() {
// this is the only place where orders
// are placed
if (!_orders_placed_once) {
_porder->_order_manager.place_order();
_orders_placed_once = true;
} else
_porder->_order_manager.reenter_filled_orders();
}
// void QHOWaitingForAckStateWorker::on_transition() {
// // this is the only place where orders
// // are placed
// if (!_orders_placed_once) {
// _porder->_order_manager.place_order();
// _orders_placed_once = true;
// } else
// _porder->_order_manager.reenter_filled_orders();
// }
void QHOWaitingForAckStateWorker::on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) {
_porder->_assert_false("QHOWaitingForAckStateWorker::on_init_new");
}
void QHOWaitingForAckStateWorker::on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) {
_porder->_assert_false("QHOWaitingForAckStateWorker::on_init_recovered");
}
void QHOWaitingForAckStateWorker::on_activate(
Price, Quantity, OrderServices &) {
_porder->_assert_false("QHOWaitingForAckStateWorker::on_activate");
}
void QHOWaitingForAckStateWorker::on_update(Price price, Quantity quantity) {
// cache it here, and update order when
// transferring to WORKING
_porder->_set_state_price_qty(price, quantity);
if (_porder->_filled())
_porder->_cancel_order(QueueHolderOrder::TerminationReason::FILLED);
}
void QHOWaitingForAckStateWorker::on_cancel() {
_porder->_cancel_order(QueueHolderOrder::TerminationReason::EXTERNAL_CANCEL);
}
void QHOWaitingForAckStateWorker::on_child_order_update(
const OrderStateUpdateEvent &ev) {
QHOOrderManager &order_man = _porder->_order_manager;
if (order_man.on_order_update(ev)) {
if (_porder->_filled())
_porder->_cancel_order(QueueHolderOrder::TerminationReason::FILLED);
else if (order_man.all_orders_acked())
_porder->_switch_state(QueueHolderOrderState::WORKING);
} else
LOG(OrderMan, Error,
"QHOWaitingForAckStateWorker::on_child_order_update "
"key:{} order:{} could not be found",
_porder->key(), ev.key);
}
void QHOWaitingForAckStateWorker::on_book(const Book &, const trace::CorID &) {}
void QHOWaitingForAckStateWorker::on_time(const TimeStamp &) {}
} // namespace orderman
} // namespace bts
// void QHOWaitingForAckStateWorker::on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) {
// _porder->_assert_false("QHOWaitingForAckStateWorker::on_init_new");
// }
// void QHOWaitingForAckStateWorker::on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) {
// _porder->_assert_false("QHOWaitingForAckStateWorker::on_init_recovered");
// }
// void QHOWaitingForAckStateWorker::on_activate(
// Price, Quantity, OrderServices &) {
// _porder->_assert_false("QHOWaitingForAckStateWorker::on_activate");
// }
// void QHOWaitingForAckStateWorker::on_update(Price price, Quantity quantity) {
// // cache it here, and update order when
// // transferring to WORKING
// _porder->_set_state_price_qty(price, quantity);
// if (_porder->_filled())
// _porder->_cancel_order(QueueHolderOrder::TerminationReason::FILLED);
// }
// void QHOWaitingForAckStateWorker::on_cancel() {
// _porder->_cancel_order(QueueHolderOrder::TerminationReason::EXTERNAL_CANCEL);
// }
// void QHOWaitingForAckStateWorker::on_child_order_update(
// const OrderStateUpdateEvent &ev) {
// QHOOrderManager &order_man = _porder->_order_manager;
// if (order_man.on_order_update(ev)) {
// if (_porder->_filled())
// _porder->_cancel_order(QueueHolderOrder::TerminationReason::FILLED);
// else if (order_man.all_orders_acked())
// _porder->_switch_state(QueueHolderOrderState::WORKING);
// } else
// LOG(OrderMan, Error,
// "QHOWaitingForAckStateWorker::on_child_order_update "
// "key:{} order:{} could not be found",
// _porder->key(), ev.key);
// }
// void QHOWaitingForAckStateWorker::on_book(const Book &, const trace::CorID &) {}
// void QHOWaitingForAckStateWorker::on_time(const TimeStamp &) {}
// } // namespace orderman
// } // namespace bts
@@ -1,44 +1,44 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
// #include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class QueueHolderOrder;
// orders are placed on the transition to this state. it
// leaves the state when
// order is cxled -> WAITING_FOR_CANCEL_ALL
// order is overfilled -> WAITING_FOR_CANCEL_ALL
// all orders acked -> WORKING
// updates are passed over to the state and
// maybe used in deiciding if filled or not
// (since we are gong to to do away with this state
// most of the logic is moved out)
class QHOWaitingForAckStateWorker : public IQueueHolderOrderStateWorker {
private:
QueueHolderOrder *const _porder;
bool _orders_placed_once;
// class QueueHolderOrder;
// // orders are placed on the transition to this state. it
// // leaves the state when
// // order is cxled -> WAITING_FOR_CANCEL_ALL
// // order is overfilled -> WAITING_FOR_CANCEL_ALL
// // all orders acked -> WORKING
// // updates are passed over to the state and
// // maybe used in deiciding if filled or not
// // (since we are gong to to do away with this state
// // most of the logic is moved out)
// class QHOWaitingForAckStateWorker : public IQueueHolderOrderStateWorker {
// private:
// QueueHolderOrder *const _porder;
// bool _orders_placed_once;
public:
QHOWaitingForAckStateWorker(QueueHolderOrder *parent);
QueueHolderOrderState state() const override {
return QueueHolderOrderState::WAITING_FOR_FIRST_ACK;
}
void on_transition() override;
void on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) override;
void on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) override;
void on_activate(Price, Quantity, OrderServices &) override;
void on_update(Price, Quantity) override;
void on_cancel() override;
void on_child_order_update(const OrderStateUpdateEvent &) override;
void on_book(const Book &, const trace::CorID &) override;
void on_time(const TimeStamp &) override;
};
// public:
// QHOWaitingForAckStateWorker(QueueHolderOrder *parent);
// QueueHolderOrderState state() const override {
// return QueueHolderOrderState::WAITING_FOR_FIRST_ACK;
// }
// void on_transition() override;
// void on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) override;
// void on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) override;
// void on_activate(Price, Quantity, OrderServices &) override;
// void on_update(Price, Quantity) override;
// void on_cancel() override;
// void on_child_order_update(const OrderStateUpdateEvent &) override;
// void on_book(const Book &, const trace::CorID &) override;
// void on_time(const TimeStamp &) override;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,74 +1,74 @@
#include "lib/orderman/orders/queue_holder/state_workers/QHOWaitingForCancelAllStateWorker.h"
#include "lib/domain_model/Security.h"
#include "lib/logging/logging.h"
#include "lib/orderman/OrderBuilder.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include <algorithm>
#include <sstream>
// #include "lib/orderman/orders/queue_holder/state_workers/QHOWaitingForCancelAllStateWorker.h"
// #include "lib/domain_model/Security.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/OrderBuilder.h"
// #include "lib/orderman/events/BookUpdateEvent.h"
// #include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
// #include <algorithm>
// #include <sstream>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
QHOWaitingForCancelAllStateWorker::QHOWaitingForCancelAllStateWorker(
QueueHolderOrder *parent)
: _porder(parent) {}
// QHOWaitingForCancelAllStateWorker::QHOWaitingForCancelAllStateWorker(
// QueueHolderOrder *parent)
// : _porder(parent) {}
void QHOWaitingForCancelAllStateWorker::on_transition() {
_analyze_and_cancel();
}
// void QHOWaitingForCancelAllStateWorker::on_transition() {
// _analyze_and_cancel();
// }
void QHOWaitingForCancelAllStateWorker::on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) {
_porder->_assert_false("QHOWaitingForCancelAllStateWorker::on_init_new");
}
void QHOWaitingForCancelAllStateWorker::on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) {
_porder->_assert_false(
"QHOWaitingForCancelAllStateWorker::on_init_recovered");
}
void QHOWaitingForCancelAllStateWorker::on_activate(
Price, Quantity, OrderServices &) {
_porder->_assert_false("QHOWaitingForCancelAllStateWorker::on_activate");
}
void QHOWaitingForCancelAllStateWorker::on_update(Price, Quantity) {
LOG(OrderMan, Warn,
"QHOWaitingForCancelAllStateWorker::on_update "
"key:{} rejected",
_porder->key());
}
void QHOWaitingForCancelAllStateWorker::on_cancel() {
LOG(OrderMan, Info,
"QHOWaitingForCancelAllStateWorker::on_cancel "
"key:{} already cancelled",
_porder->key());
}
void QHOWaitingForCancelAllStateWorker::on_child_order_update(
const OrderStateUpdateEvent &ev) {
if (_porder->_order_manager.on_order_update(ev))
_analyze_and_cancel();
else
LOG(OrderMan, Error,
"QHOWaitingForCancelAllStateWorker::on_child_order_update "
"key:{} order:{} could not be found",
_porder->key(), ev.key);
}
void QHOWaitingForCancelAllStateWorker::on_book(
const Book &, const trace::CorID &) {}
void QHOWaitingForCancelAllStateWorker::on_time(const TimeStamp &) {
/// TODO, time-out
}
void QHOWaitingForCancelAllStateWorker::_analyze_and_cancel() {
QHOOrderManager &order_man = _porder->_order_manager;
if (!order_man.all_orders_terminal())
order_man.cancel();
else
_porder->_switch_state(QueueHolderOrderState::END);
}
} // namespace orderman
} // namespace bts
// void QHOWaitingForCancelAllStateWorker::on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) {
// _porder->_assert_false("QHOWaitingForCancelAllStateWorker::on_init_new");
// }
// void QHOWaitingForCancelAllStateWorker::on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) {
// _porder->_assert_false(
// "QHOWaitingForCancelAllStateWorker::on_init_recovered");
// }
// void QHOWaitingForCancelAllStateWorker::on_activate(
// Price, Quantity, OrderServices &) {
// _porder->_assert_false("QHOWaitingForCancelAllStateWorker::on_activate");
// }
// void QHOWaitingForCancelAllStateWorker::on_update(Price, Quantity) {
// LOG(OrderMan, Warn,
// "QHOWaitingForCancelAllStateWorker::on_update "
// "key:{} rejected",
// _porder->key());
// }
// void QHOWaitingForCancelAllStateWorker::on_cancel() {
// LOG(OrderMan, Info,
// "QHOWaitingForCancelAllStateWorker::on_cancel "
// "key:{} already cancelled",
// _porder->key());
// }
// void QHOWaitingForCancelAllStateWorker::on_child_order_update(
// const OrderStateUpdateEvent &ev) {
// if (_porder->_order_manager.on_order_update(ev))
// _analyze_and_cancel();
// else
// LOG(OrderMan, Error,
// "QHOWaitingForCancelAllStateWorker::on_child_order_update "
// "key:{} order:{} could not be found",
// _porder->key(), ev.key);
// }
// void QHOWaitingForCancelAllStateWorker::on_book(
// const Book &, const trace::CorID &) {}
// void QHOWaitingForCancelAllStateWorker::on_time(const TimeStamp &) {
// /// TODO, time-out
// }
// void QHOWaitingForCancelAllStateWorker::_analyze_and_cancel() {
// QHOOrderManager &order_man = _porder->_order_manager;
// if (!order_man.all_orders_terminal())
// order_man.cancel();
// else
// _porder->_switch_state(QueueHolderOrderState::END);
// }
// } // namespace orderman
// } // namespace bts
@@ -1,42 +1,42 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
// #include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class QueueHolderOrder;
// transfer to this state means the order needs to
// be cancelled. _analyze_and_cancel() is called
// on_transition and everytime there is an order update.
// _analyze_and_cancel() tried cancelling all orders,
// and if orders are in terminal state goes to end
class QHOWaitingForCancelAllStateWorker : public IQueueHolderOrderStateWorker {
private:
QueueHolderOrder *const _porder;
// class QueueHolderOrder;
// // transfer to this state means the order needs to
// // be cancelled. _analyze_and_cancel() is called
// // on_transition and everytime there is an order update.
// // _analyze_and_cancel() tried cancelling all orders,
// // and if orders are in terminal state goes to end
// class QHOWaitingForCancelAllStateWorker : public IQueueHolderOrderStateWorker {
// private:
// QueueHolderOrder *const _porder;
public:
QHOWaitingForCancelAllStateWorker(QueueHolderOrder *parent);
QueueHolderOrderState state() const override {
return QueueHolderOrderState::WAITING_FOR_CANCEL_ALL;
}
void on_transition() override;
void on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) override;
void on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) override;
void on_activate(Price, Quantity, OrderServices &) override;
void on_update(Price, Quantity) override;
void on_cancel() override;
void on_child_order_update(const OrderStateUpdateEvent &) override;
void on_book(const Book &, const trace::CorID &) override;
void on_time(const TimeStamp &) override;
// public:
// QHOWaitingForCancelAllStateWorker(QueueHolderOrder *parent);
// QueueHolderOrderState state() const override {
// return QueueHolderOrderState::WAITING_FOR_CANCEL_ALL;
// }
// void on_transition() override;
// void on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) override;
// void on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) override;
// void on_activate(Price, Quantity, OrderServices &) override;
// void on_update(Price, Quantity) override;
// void on_cancel() override;
// void on_child_order_update(const OrderStateUpdateEvent &) override;
// void on_book(const Book &, const trace::CorID &) override;
// void on_time(const TimeStamp &) override;
private:
void _analyze_and_cancel();
};
// private:
// void _analyze_and_cancel();
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,119 +1,119 @@
#include "lib/orderman/orders/queue_holder/state_workers/QHOWorkingStateWorker.h"
#include "lib/domain_model/Security.h"
#include "lib/logging/logging.h"
#include "lib/orderman/OrderBuilder.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/os/os.h"
#include "lib/utils/PBUtils.h"
#include <algorithm>
#include <sstream>
// #include "lib/orderman/orders/queue_holder/state_workers/QHOWorkingStateWorker.h"
// #include "lib/domain_model/Security.h"
// #include "lib/logging/logging.h"
// #include "lib/orderman/OrderBuilder.h"
// #include "lib/orderman/events/BookUpdateEvent.h"
// #include "lib/orderman/orders/queue_holder/QueueHolderOrder.h"
// #include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
// #include "lib/os/os.h"
// #include "lib/utils/PBUtils.h"
// #include <algorithm>
// #include <sstream>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
QHOWorkingStateWorker::QHOWorkingStateWorker(QueueHolderOrder *parent)
: _porder(parent) {}
void QHOWorkingStateWorker::on_transition() {
if (!_analyze_moving_out()) {
const api::orderman::OrderState &state = _porder->_reported_state;
// cached updates while waiting for acks
on_update(Price(state.price()), Quantity(state.quantity()));
}
}
void QHOWorkingStateWorker::on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) {
_porder->_assert_false("QHOWorkingStateWorker::on_init_new");
}
void QHOWorkingStateWorker::on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) {
_porder->_assert_false("QHOWorkingStateWorker::on_init_recovered");
}
void QHOWorkingStateWorker::on_activate(Price, Quantity, OrderServices &) {
_porder->_assert_false("QHOWorkingStateWorker::on_activate");
}
void QHOWorkingStateWorker::on_update(Price price, Quantity qty) {
_porder->_set_state_price_qty(price, qty);
if (_porder->_filled())
_porder->_cancel_order(QueueHolderOrder::TerminationReason::FILLED);
else
_porder->_order_manager.modify_order(price, qty);
}
void QHOWorkingStateWorker::on_cancel() {
_porder->_order_manager.cancel();
_porder->_cancel_order(QueueHolderOrder::TerminationReason::EXTERNAL_CANCEL);
}
void QHOWorkingStateWorker::on_child_order_update(
const OrderStateUpdateEvent &ev) {
QHOOrderManager &order_man = _porder->_order_manager;
if (order_man.on_order_update(ev)) {
if (!_analyze_moving_out()) {
if (order_man.last_update_has_fills())
// in case of fills, we can reduce
// the qty of the trailing orders to
// decrease probablity of overfills
order_man.prune_quantities();
}
} else
LOG(OrderMan, Error,
"QHOWorkingStateWorker::on_child_order_update "
"key:{} order:{} could not be found",
_porder->key(), ev.key);
}
// returns true if state is changed
bool QHOWorkingStateWorker::_analyze_moving_out() {
// order is cancelled if over filled or any of the
// constituent orders end up in an unexpected terminal
// state
if (_porder->_filled()) {
LOG(OrderMan, Info,
"QHOWorkingStateWorker::_analyze_moving_out "
"_filled key:{}",
_porder->key());
_porder->_cancel_order(QueueHolderOrder::TerminationReason::FILLED);
return true;
}
const QHOOrderManager &order_man = _porder->_order_manager;
if (order_man.has_cancelled_order()) {
LOG(OrderMan, Info,
"QHOWorkingStateWorker::_analyze_moving_out "
"has_cancelled_order key:{}",
_porder->key());
_porder->_cancel_order(
QueueHolderOrder::TerminationReason::UNEXPECTED_CHILD_CANCEL);
return true;
}
if (order_man.has_errored_order()) {
LOG(OrderMan, Info,
"QHOWorkingStateWorker::_analyze_moving_out "
"has_errored_order key:{}",
_porder->key());
_porder->_cancel_order(QueueHolderOrder::TerminationReason::CHILD_ERROR);
return true;
}
// QHO order's target qty has not been reached
// yet one or more component orders has been filled.
// switching to WAITING_FOR_FIRST_ACK will cause
// filled orders to be replaced with new orders
if (order_man.has_completed_order()) {
LOG(OrderMan, Info,
"QHOWorkingStateWorker::_analyze_moving_out "
"has_completed_order key:{}",
_porder->key());
_porder->_switch_state(QueueHolderOrderState::WAITING_FOR_FIRST_ACK);
return true;
}
// all orders have to be acked in this state &&
// if an order was cxled or errored out, the first
// if would have caught it. and if filled, the
// second would have
bts_assert(order_man.all_orders_acked() && !order_man.has_terminal_order());
return false;
}
void QHOWorkingStateWorker::on_book(const Book &, const trace::CorID &) {}
void QHOWorkingStateWorker::on_time(const TimeStamp &) {}
} // namespace orderman
} // namespace bts
// QHOWorkingStateWorker::QHOWorkingStateWorker(QueueHolderOrder *parent)
// : _porder(parent) {}
// void QHOWorkingStateWorker::on_transition() {
// if (!_analyze_moving_out()) {
// const api::orderman::OrderState &state = _porder->_reported_state;
// // cached updates while waiting for acks
// on_update(Price(state.price()), Quantity(state.quantity()));
// }
// }
// void QHOWorkingStateWorker::on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) {
// _porder->_assert_false("QHOWorkingStateWorker::on_init_new");
// }
// void QHOWorkingStateWorker::on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) {
// _porder->_assert_false("QHOWorkingStateWorker::on_init_recovered");
// }
// void QHOWorkingStateWorker::on_activate(Price, Quantity, OrderServices &) {
// _porder->_assert_false("QHOWorkingStateWorker::on_activate");
// }
// void QHOWorkingStateWorker::on_update(Price price, Quantity qty) {
// _porder->_set_state_price_qty(price, qty);
// if (_porder->_filled())
// _porder->_cancel_order(QueueHolderOrder::TerminationReason::FILLED);
// else
// _porder->_order_manager.modify_order(price, qty);
// }
// void QHOWorkingStateWorker::on_cancel() {
// _porder->_order_manager.cancel();
// _porder->_cancel_order(QueueHolderOrder::TerminationReason::EXTERNAL_CANCEL);
// }
// void QHOWorkingStateWorker::on_child_order_update(
// const OrderStateUpdateEvent &ev) {
// QHOOrderManager &order_man = _porder->_order_manager;
// if (order_man.on_order_update(ev)) {
// if (!_analyze_moving_out()) {
// if (order_man.last_update_has_fills())
// // in case of fills, we can reduce
// // the qty of the trailing orders to
// // decrease probablity of overfills
// order_man.prune_quantities();
// }
// } else
// LOG(OrderMan, Error,
// "QHOWorkingStateWorker::on_child_order_update "
// "key:{} order:{} could not be found",
// _porder->key(), ev.key);
// }
// // returns true if state is changed
// bool QHOWorkingStateWorker::_analyze_moving_out() {
// // order is cancelled if over filled or any of the
// // constituent orders end up in an unexpected terminal
// // state
// if (_porder->_filled()) {
// LOG(OrderMan, Info,
// "QHOWorkingStateWorker::_analyze_moving_out "
// "_filled key:{}",
// _porder->key());
// _porder->_cancel_order(QueueHolderOrder::TerminationReason::FILLED);
// return true;
// }
// const QHOOrderManager &order_man = _porder->_order_manager;
// if (order_man.has_cancelled_order()) {
// LOG(OrderMan, Info,
// "QHOWorkingStateWorker::_analyze_moving_out "
// "has_cancelled_order key:{}",
// _porder->key());
// _porder->_cancel_order(
// QueueHolderOrder::TerminationReason::UNEXPECTED_CHILD_CANCEL);
// return true;
// }
// if (order_man.has_errored_order()) {
// LOG(OrderMan, Info,
// "QHOWorkingStateWorker::_analyze_moving_out "
// "has_errored_order key:{}",
// _porder->key());
// _porder->_cancel_order(QueueHolderOrder::TerminationReason::CHILD_ERROR);
// return true;
// }
// // QHO order's target qty has not been reached
// // yet one or more component orders has been filled.
// // switching to WAITING_FOR_FIRST_ACK will cause
// // filled orders to be replaced with new orders
// if (order_man.has_completed_order()) {
// LOG(OrderMan, Info,
// "QHOWorkingStateWorker::_analyze_moving_out "
// "has_completed_order key:{}",
// _porder->key());
// _porder->_switch_state(QueueHolderOrderState::WAITING_FOR_FIRST_ACK);
// return true;
// }
// // all orders have to be acked in this state &&
// // if an order was cxled or errored out, the first
// // if would have caught it. and if filled, the
// // second would have
// bts_assert(order_man.all_orders_acked() && !order_man.has_terminal_order());
// return false;
// }
// void QHOWorkingStateWorker::on_book(const Book &, const trace::CorID &) {}
// void QHOWorkingStateWorker::on_time(const TimeStamp &) {}
// } // namespace orderman
// } // namespace bts
@@ -1,40 +1,40 @@
#pragma once
// #pragma once
#include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
// #include "lib/orderman/orders/queue_holder/IQueueHolderOrderStateWorker.h"
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class QueueHolderOrder;
// main working state
class QHOWorkingStateWorker : public IQueueHolderOrderStateWorker {
private:
QueueHolderOrder *const _porder;
// class QueueHolderOrder;
// // main working state
// class QHOWorkingStateWorker : public IQueueHolderOrderStateWorker {
// private:
// QueueHolderOrder *const _porder;
public:
QHOWorkingStateWorker(QueueHolderOrder *parent);
QueueHolderOrderState state() const override {
return QueueHolderOrderState::WORKING;
}
void on_transition() override;
void on_init_new(
const api::orderman::OrderDefinition &,
const PreActivationOrderServices &) override;
void on_init_recovered(
const bts::api::orderman::OrderRecord &,
const PreActivationOrderServices &) override;
void on_activate(Price, Quantity, OrderServices &) override;
void on_update(Price, Quantity) override;
void on_cancel() override;
void on_child_order_update(const OrderStateUpdateEvent &) override;
void on_book(const Book &, const trace::CorID &) override;
void on_time(const TimeStamp &) override;
// public:
// QHOWorkingStateWorker(QueueHolderOrder *parent);
// QueueHolderOrderState state() const override {
// return QueueHolderOrderState::WORKING;
// }
// void on_transition() override;
// void on_init_new(
// const api::orderman::OrderDefinition &,
// const PreActivationOrderServices &) override;
// void on_init_recovered(
// const bts::api::orderman::OrderRecord &,
// const PreActivationOrderServices &) override;
// void on_activate(Price, Quantity, OrderServices &) override;
// void on_update(Price, Quantity) override;
// void on_cancel() override;
// void on_child_order_update(const OrderStateUpdateEvent &) override;
// void on_book(const Book &, const trace::CorID &) override;
// void on_time(const TimeStamp &) override;
private:
// returns true if the state is changed
bool _analyze_moving_out();
bool _needs_cancelling() const;
};
// private:
// // returns true if the state is changed
// bool _analyze_moving_out();
// bool _needs_cancelling() const;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
@@ -1,23 +1,17 @@
#pragma once
#include "lib/domain_model/Security.pb.h"
#include "lib/orderman/events/SettlementUpdateEvent.h"
#include "lib/orderman/orderman_utils/orderman_common.h"
#include "lib/orderman/orders/spreader_orders/market_data/Book.h"
#include "lib/orderman/orders/spreader_orders/market_data/SettlementUpdateEvent.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h" // fix this (not needed, all the headers can be fwd ref's)
#include "lib/orderman/orders/spreader_orders/spreader_states/SpreaderState.h"
#include "lib/sparkproto/SparkProtos.pb.h"
#include "lib/utils/bts_chrono.h"
namespace bts {
namespace api {
namespace orderman {
namespace bts::api::orderman {
class OrderDefinition;
}
} // namespace api
namespace orderman {
namespace bts::spreader {
class PreActivationOrderServices;
class OrderServices;
} // namespace orderman
namespace orderman {
class SpreaderOrder;
class Book;
class ISpreaderStateWorker {
@@ -67,5 +61,4 @@ class ISpreaderStateWorker {
// child orders free
virtual void on_detach() {}
};
} // namespace orderman
} // namespace bts
} // namespace bts::spreader
@@ -2,31 +2,34 @@
#include "lib/config/config.h"
#include "lib/config/protos/spreader.pb.h"
#include "lib/logging/logging.h"
#include "lib/orderman/events/SettlementUpdateEvent.h"
#include "lib/orderman/orders/spreader_orders/spreader/LegPropertyData.h"
#include "lib/orderman/orders/spreader_orders/spreader/NLegsHedgeRecompData.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderLegData.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
#include "lib/orderman/orders/spreader_orders/spreader/external_interfacing/IAlternateOrderGateway.h"
#include "lib/orderman/orders/spreader_orders/spreader/external_interfacing/IOrderGateway.h"
#include "lib/os/os.h"
#include "lib/tracing/bts_trace.h"
#include "lib/utils/PBUtils.h"
#include "lib/utils/helper_macros.h"
namespace bts {
namespace orderman {
namespace spreader {
/**************************Cons/Dest*******************************/
SpreaderOrder::SpreaderOrder(IAlternateOrderGateway *alt_order_gateway)
: _alt_order_gateway(alt_order_gateway), _cancelled(false),
_report_dirty(false), _active(true), _cancel_hedges_on_cancel(false),
_cw_helper(), _cancel_timeout(5000), // TODO:get it from config
SpreaderOrder::SpreaderOrder(
bts::spreader::OrderKey spreader_id,
const bts::api::orderman::OrderDefinition &order_def,
bts::integration::IOrderGateway *order_gateway)
: _key(spreader_id), _def(order_def), _order_gateway(order_gateway),
_cancelled(false), _report_dirty(false), _active(true),
_cancel_hedges_on_cancel(false), _cw_helper(),
_cancel_timeout(5000), // TODO:get it from config
_check_termination_period(
250), // should also be a setting/config like above
_bookkeeping_data_ptr(), _order_services(nullptr), _curr_timestamp(),
_last_check_termination_timestamp(), _perf_ts(utils::TimeStamp::now()),
_last_check_termination_timestamp(), _perf_ts(TimeStamp::now()),
_state_worker(nullptr), _start_state_worker(this),
_waiting_for_initial_data_state_worker(this), _working_state_worker(this),
_waiting_for_cancel_all_state_worker(this), _end_state_worker(this) {
@@ -45,26 +48,25 @@ const api::orderman::OrderDefinition &SpreaderOrder::definition() const {
}
void SpreaderOrder::on_init_new(
const bts::api::orderman::OrderDefinition &order_def,
const PreActivationOrderServices &pao_services) {
const PreActivationOrderServices &) {
if (!_active)
return;
_on_init_new(order_def, SpreaderPreActivationOrderServices(&pao_services));
_on_init_new(order_def, SpreaderPreActivationOrderServices());
_report_state();
}
void SpreaderOrder::on_init_recovered(
const bts::api::orderman::OrderRecord &order_record,
const PreActivationOrderServices &pao_services) {
const PreActivationOrderServices &) {
LOG(OrderMan, Info, "SpreaderOrder.on_init_recovered order_key:{}",
key().value());
_state_worker->on_init_recovered(
order_record, SpreaderPreActivationOrderServices(&pao_services));
order_record, SpreaderPreActivationOrderServices());
_report_state();
}
OrderOpResult SpreaderOrder::on_activate(
Price price, Quantity quantity, OrderServices &services) {
OrderOpResult SpreaderOrder::on_activate(Price price, Quantity quantity) {
if (!_active)
return OrderOpResult{api::orderman::ORDER_ERROR_OP_NOT_SUPPORTED};
SpreaderOrderServices spreader_order_services(&services);
SpreaderOrderServices spreader_order_services;
_on_activate(price, quantity, spreader_order_services);
// the reason why this is immediatly reported (vs in the timer
// event) is that if this spreader was constructed through
@@ -113,23 +115,18 @@ void SpreaderOrder::setup_new_quoter_order(size_t leg_index) {
key().value(), leg_index);
SpreaderLegData &leg_data = _bookkeeping_data().get_leg_data(leg_index);
bts_assert(leg_data.leg_property_data().is_active_quoting());
bts::orderman::ChildOrder child_order =
leg_data.quoter_order_builder().build();
auto child_order = leg_data.quoter_order_builder().build();
leg_data.order_update_message().re_init(child_order);
}
ChildOrder SpreaderOrder::get_new_hedge_order(size_t leg_index) {
bts::spreader::ChildOrder SpreaderOrder::get_new_hedge_order(size_t leg_index) {
SpreaderLegData &leg_data = _bookkeeping_data().get_leg_data(leg_index);
bts::orderman::ChildOrder child_order =
leg_data.hedger_order_builder().build();
auto child_order = leg_data.hedger_order_builder().build();
return child_order;
}
IAlternateOrderGateway *SpreaderOrder::alt_order_gateway() {
return _alt_order_gateway;
}
// events from orders system ////////////////////
/////////////////////////////////////////////////
void SpreaderOrder::_on_time(const bts::mercury::TimerEvent &t_event) {
_curr_timestamp = t_event.ts;
void SpreaderOrder::_on_time(const TimeStamp &ts) {
_curr_timestamp = ts;
if (!_active)
return;
_state_worker->on_time(_curr_timestamp);
@@ -142,12 +139,13 @@ void SpreaderOrder::_on_time(const bts::mercury::TimerEvent &t_event) {
_comp_and_report_state();
}
}
void SpreaderOrder::_on_book_update(const BookUpdateEvent &book_event) {
void SpreaderOrder::_on_book_update(const Book &book) {
if (!_active)
return;
auto scoped = _order_services->log_scope(
"SpreaderOrder::_on_book_update", key().value());
_state_worker->on_book(book_event.book);
// SPREADER_CHANGE, no need for this
// auto scoped = _order_services->log_scope(
// "SpreaderOrder::_on_book_update", key().value());
_state_worker->on_book(book);
}
void SpreaderOrder::_on_settlement_update(
const SettlementUpdateEvent &settlement_event) {
@@ -161,37 +159,38 @@ void SpreaderOrder::_on_settlement_update(
_state_worker->on_settlement_price(settlement_event);
}
void SpreaderOrder::_keep_hedging_path_hot() {
bool fire = _inited() && _active && _cw_helper.enable_fills &&
(ms_diff(_curr_timestamp, _cw_helper.last_cache_warm_fill_ts) >=
_cw_helper.fill_fire_period) &&
_state_worker != nullptr &&
_state_worker->state() == SpreaderState::WORKING;
if (!fire)
return;
_cw_helper.set_forced_flag();
for (size_t i = 0; i < _bookkeeping_data().n_legs(); i++) {
SpreaderLegData &leg = _bookkeeping_data().get_leg_data(i);
// only for quoting legs with active orders out there
if (leg.leg_property_data().is_active_quoting() &&
leg.quoter().is_working_order()) {
auto &q_ord = leg.order_update_message();
bts::api::orderman::OrderState state;
state.set_filled_quantity(q_ord.current_fills().value());
state.set_last_price(q_ord.last_update_filled_price().value());
OrderStateUpdateEvent ord_ev(q_ord.order_key(), key(), def(), state);
ord_ev.status = q_ord.status();
ord_ev.price = q_ord.last_reported_order_price().value();
_on_child_order_update(ord_ev);
}
if (_cw_helper.enable_child_order_activate)
leg.hedger_manager().get_hedger().keep_hedger_order_warm();
}
_cw_helper.unset_forced_flag();
// bool fire = _inited() && _active && _cw_helper.enable_fills &&
// (ms_diff(_curr_timestamp, _cw_helper.last_cache_warm_fill_ts)
// >=
// _cw_helper.fill_fire_period) &&
// _state_worker != nullptr &&
// _state_worker->state() == SpreaderState::WORKING;
// if (!fire)
// return;
// _cw_helper.set_forced_flag();
// for (size_t i = 0; i < _bookkeeping_data().n_legs(); i++) {
// SpreaderLegData &leg = _bookkeeping_data().get_leg_data(i);
// // only for quoting legs with active orders out there
// if (leg.leg_property_data().is_active_quoting() &&
// leg.quoter().is_working_order()) {
// auto &q_ord = leg.order_update_message();
// bts::api::orderman::OrderState state;
// state.set_filled_quantity(q_ord.current_fills().value());
// state.set_last_price(q_ord.last_update_filled_price().value());
// OrderStateUpdateEvent ord_ev(q_ord.order_key(), key(), def(),
// state); ord_ev.status = q_ord.status(); ord_ev.price =
// q_ord.last_reported_order_price().value();
// _on_child_order_update(ord_ev);
// }
// if (_cw_helper.enable_child_order_activate)
// leg.hedger_manager().get_hedger().keep_hedger_order_warm();
// }
// _cw_helper.unset_forced_flag();
}
void SpreaderOrder::_on_child_order_update(
const OrderStateUpdateEvent &ord_ev) {
_perf_ts = utils::TimeStamp::now();
_perf_ts = TimeStamp::now();
auto start = _perf_ts;
bool order_acked = SpreaderUtils::order_acknowledged(ord_ev.status);
if (!_active || !order_acked)
@@ -217,7 +216,7 @@ void SpreaderOrder::_on_child_order_update(
_report_state(!_cw_helper.in_cache_warming_cycle);
LOG(OrderMan, _cw_helper.llevel, "h_p rep {} {}", cb_type, _diff_ns());
LOG(OrderMan, _cw_helper.llevel, "_on_child_order_update {} {}", cb_type,
(utils::TimeStamp::now() - start).nanoseconds());
(TimeStamp::now() - start).nanoseconds());
}
}
void SpreaderOrder::_on_non_quoter_order(const OrderStateUpdateEvent &ord_ev) {
@@ -301,12 +300,12 @@ void SpreaderOrder::_on_init_new(
_state_worker->on_init_new(order_def, spreader_pao_services);
}
void SpreaderOrder::_on_activate(
Price price, Quantity quantity, SpreaderOrderServices &order_services) {
Price price, Quantity quantity, SpreaderOrderServices &) {
bts_assert(_inited());
bts_assert(!_activated());
LOG(OrderMan, Info, "SpreaderOrder.on_activate key:{} price:{} quantity:{}",
key().value(), price.value(), quantity.value());
_order_services = new SpreaderOrderServices(order_services);
_order_services = new SpreaderOrderServices();
_state_worker->on_activate(price, quantity, *_order_services);
}
void SpreaderOrder::_cancel_orders(bool cancel_quotes, bool cancel_hedges) {
@@ -495,7 +494,8 @@ bts::api::orderman::OrderStatus SpreaderOrder::_get_order_status() const {
}
}
void SpreaderOrder::_send_check_termination(bool force) {
if (force || ms_diff(_curr_timestamp, _last_check_termination_timestamp) >=
if (force || SpreaderUtils::ms_diff(
_curr_timestamp, _last_check_termination_timestamp) >=
_check_termination_period) {
_last_check_termination_timestamp = _curr_timestamp;
_state_worker->on_check_termination();
@@ -566,5 +566,5 @@ void SpreaderOrder::_touch_hedging_data() {
}
}
} // namespace orderman
} // namespace spreader
} // namespace bts
@@ -2,13 +2,13 @@
#include "lib/api/services/OrderService.pb.h"
#include "lib/domain_model/Security.pb.h"
#include "lib/mercury/events/TimerEvent.h"
#include "lib/orderman/events/BookUpdateEvent.h"
#include "lib/orderman/events/OrderStateUpdateEvent.h"
#include "lib/orderman/orders/Order.h"
#include "lib/orderman/orders/spreader_orders/market_data/OrderOpResult.h"
#include "lib/orderman/orders/spreader_orders/market_data/TimeStamp.h"
#include "lib/orderman/orders/spreader_orders/market_data/ValueWrapperT.h"
#include "lib/orderman/orders/spreader_orders/spreader/ISpreaderManager.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderBookkeepingData.h"
#include "lib/orderman/orders/spreader_orders/spreader/SpreaderCacheWarmingState.h"
#include "lib/orderman/orders/spreader_orders/spreader/external_interfacing/IOrderGateway.h"
#include "lib/orderman/orders/spreader_orders/spreader_states/SpreaderEndStateWorker.h"
#include "lib/orderman/orders/spreader_orders/spreader_states/SpreaderStartStateWorker.h"
#include "lib/orderman/orders/spreader_orders/spreader_states/SpreaderState.h"
@@ -16,23 +16,22 @@
#include "lib/orderman/orders/spreader_orders/spreader_states/SpreaderWaitingForInitialDataStateWorker.h"
#include "lib/orderman/orders/spreader_orders/spreader_states/SpreaderWorkingStateWorker.h"
#include "lib/sparkproto/SparkProtos.pb.h"
#include "lib/utils/bts_chrono.h"
#include "lib/utils/is_unittest_environment.h"
//#include "lib/utils/bts_chrono.h"
#include <memory>
namespace bts {
namespace api {
namespace orderman {
namespace bts::integration {
class IOrderGateway;
}
namespace bts::api::orderman {
class OrderState;
class CreateSpreaderOrder;
} // namespace orderman
} // namespace api
namespace orderman {
} // namespace bts::api::orderman
namespace bts::spreader {
class SettlementUpdateEvent;
class IAlternateOrderGateway;
class SpreaderOrder : public Order, public ISpreaderManager {
class SpreaderOrder : public ISpreaderManager {
friend class SpreaderStartStateWorker;
friend class SpreaderWaitingForInitialDataStateWorker;
friend class SpreaderWorkingStateWorker;
@@ -53,7 +52,9 @@ class SpreaderOrder : public Order, public ISpreaderManager {
// no error condition
NONE
};
IAlternateOrderGateway *const _alt_order_gateway;
const OrderKey _key = OrderKey(0);
const bts::api::orderman::OrderDefinition _def;
bts::integration::IOrderGateway *_order_gateway;
// when a fill update, this is populated with
// the indices of the other legs
// std::array<uint8_t,16> _non_filled_indices;
@@ -74,14 +75,16 @@ class SpreaderOrder : public Order, public ISpreaderManager {
// sent to the state worker
const int64_t _check_termination_period;
std::unique_ptr<SpreaderBookkeepingData> _bookkeeping_data_ptr;
std::vector<bts::api::orderman::OrderDefinition> _q_order_defs,
_h_order_defs;
// pointer kept to send reports
SpreaderOrderServices *_order_services;
// timestamp of last the on_time call
bts::TimeStamp _curr_timestamp;
TimeStamp _curr_timestamp;
// spreader fires a periodic check
bts::TimeStamp _last_check_termination_timestamp;
TimeStamp _last_check_termination_timestamp;
// current state
utils::TimeStamp _perf_ts;
TimeStamp _perf_ts;
ISpreaderStateWorker *_state_worker;
SpreaderStartStateWorker _start_state_worker;
SpreaderWaitingForInitialDataStateWorker
@@ -94,7 +97,10 @@ class SpreaderOrder : public Order, public ISpreaderManager {
// copied either way)
//////////////////////////////////
public:
SpreaderOrder(IAlternateOrderGateway * = nullptr);
SpreaderOrder(
bts::spreader::OrderKey spreader_id,
const bts::api::orderman::OrderDefinition &order_def,
bts::integration::IOrderGateway *order_gateway = nullptr);
SpreaderOrder(const SpreaderOrder &) = delete;
SpreaderOrder(SpreaderOrder &&) = delete;
SpreaderOrder &operator=(const SpreaderOrder &) = delete;
@@ -103,54 +109,71 @@ class SpreaderOrder : public Order, public ISpreaderManager {
// Order Derived ////////////////////
/////////////////////////////////////
public:
bts::spreader::OrderKey key() const { return bts::spreader::OrderKey(0); }
// inherited function to return the OrderDefinition. asserts
// if on_init_new has not been called yet
const api::orderman::OrderDefinition &definition() const;
// this is the construction, can be slow
void on_init_new(
const bts::api::orderman::OrderDefinition &order_def,
const PreActivationOrderServices &) override;
const PreActivationOrderServices &);
// called when the order is recovered from disk (not dealing with it now)
void on_init_recovered(
bts::api::orderman::OrderRecord const &,
const PreActivationOrderServices &) override;
const PreActivationOrderServices &);
// this is in the critical path, orders can be submitted now
// (this is only called once and after the on_init_new)
OrderOpResult on_activate(Price, Quantity, OrderServices &) override;
OrderOpResult on_activate(Price, Quantity);
// called when the user (or a higher level order)
// updates the order's price or quantity
OrderOpResult on_update(Price, Quantity) override;
OrderOpResult on_update(Price, Quantity);
// called when the user (or a higher level order)
// updates the order's params (including possibly price or quantity)
OrderOpResult on_update(const api::orderman::Update &) override;
OrderOpResult on_update(const api::orderman::Update &);
// called when the user (or higher level order) cancels an order
OrderOpResult on_cancel() override;
OrderOpResult on_cancel();
// detach means to let the lower level orders lose from the higher
// level order and cancel the higher level order
OrderOpResult on_detach() override;
OrderOpResult on_detach();
// ISpreaderManager ////////////////////
////////////////////////////////////////
public:
bts::orderman::OrderKey spreader_order_key() const override { return key(); }
bts::TimeStamp current_time() const override { return _curr_timestamp; }
const bts::orderman::Book &get_book(size_t leg_index) const override {
bts::spreader::OrderKey spreader_order_key() const override { return key(); }
TimeStamp current_time() const override { return _curr_timestamp; }
const bts::spreader::Book &get_book(size_t leg_index) const override {
return _bookkeeping_data().get_leg_data(leg_index).book();
}
const LegPropertyData &get_leg_data(size_t leg_index) const override {
return _bookkeeping_data().get_leg_data(leg_index).leg_property_data();
}
void setup_new_quoter_order(size_t leg_index) override;
ChildOrder get_new_hedge_order(size_t leg_index) override;
IAlternateOrderGateway *alt_order_gateway() override;
bts::spreader::ChildOrder get_new_hedge_order(size_t leg_index) override;
bts::integration::IOrderGateway *order_gateway() override {
return _order_gateway;
}
inline bool in_cache_warming_cycle() const override {
return _cw_helper.in_cache_warming_cycle;
}
bts::api::orderman::OrderDefinition const *quoter_order_definition(
size_t leg_idx) override {
bts_assert(leg_idx < _q_order_defs.size());
const auto &rv = _q_order_defs[leg_idx];
return &rv;
}
bts::api::orderman::OrderDefinition const *hedger_order_definition(
size_t leg_idx) override {
bts_assert(leg_idx < _h_order_defs.size());
const auto &rv = _h_order_defs[leg_idx];
return &rv;
}
private:
// events from orders system ////////////////////
/////////////////////////////////////////////////
void _on_time(const bts::mercury::TimerEvent &);
void _on_book_update(const BookUpdateEvent &);
void _on_time(const TimeStamp &ts);
void _on_book_update(const bts::spreader::Book &);
void _on_settlement_update(const SettlementUpdateEvent &);
void _on_child_order_update(const OrderStateUpdateEvent &);
// order handling
@@ -227,8 +250,8 @@ class SpreaderOrder : public Order, public ISpreaderManager {
// on leg 'leg_to_hedge_index' in response to a fill
// on leg 'filled_leg_index' with price 'filled_price'
// quantity 'filled_quantity'
void
_send_hedge(size_t hedge_leg_index, Price hedge_price, Quantity hedge_qty);
void _send_hedge(
size_t hedge_leg_index, Price hedge_price, Quantity hedge_qty);
// computes the hedges in the case of nlegs
// from crossing the hedge markets. If the
// crossing doesnt result in better hedge prices
@@ -302,14 +325,14 @@ class SpreaderOrder : public Order, public ISpreaderManager {
// retuns the nano seconds from last
// order callback
double _diff_ns() {
double rv = (utils::TimeStamp::now() - _perf_ts).nanoseconds();
_perf_ts = utils::TimeStamp::now();
double rv = (TimeStamp::now() - _perf_ts).nanoseconds();
_perf_ts = TimeStamp::now();
return rv;
}
double _diff_ns(utils::TimeStamp &t_stop, bool reset = true) {
double rv = (utils::TimeStamp::now() - t_stop).nanoseconds();
double _diff_ns(TimeStamp &t_stop, bool reset = true) {
double rv = (TimeStamp::now() - t_stop).nanoseconds();
if (reset)
t_stop = utils::TimeStamp::now();
t_stop = TimeStamp::now();
return rv;
}
// hot path related
@@ -331,12 +354,10 @@ class SpreaderOrder : public Order, public ISpreaderManager {
return _bookkeeping_data();
}
int64_t test_cancel_timeout() { return _cancel_timeout; }
void test_only_on_book_update(const BookUpdateEvent &b) {
void test_only_on_book_update(const bts::spreader::Book &b) {
_on_book_update(b);
}
void test_only_on_book_update(const Book &b) {
_on_book_update(BookUpdateEvent(b));
}
void test_only_on_child_order_update(
OrderKey order_key,
bts::api::orderman::OrderStatus status,
@@ -347,7 +368,8 @@ class SpreaderOrder : public Order, public ISpreaderManager {
order_state.set_filled_quantity(total_filled_qty.value());
order_state.set_last_price(avg_price.value());
order_state.set_price(avg_price.value());
OrderStateUpdateEvent ord_ev(order_key, OrderKey(0), def(), order_state);
OrderStateUpdateEvent ord_ev(
order_key, OrderKey(0), nullptr, /* def(),*/ order_state);
_on_child_order_update(ord_ev);
}
void test_only_on_init_new(
@@ -362,5 +384,4 @@ class SpreaderOrder : public Order, public ISpreaderManager {
void test_only_on_time(TimeStamp) {}
void test_only_keep_hedging_path_hot() { _keep_hedging_path_hot(); }
};
} // namespace orderman
} // namespace bts
} // namespace bts::spreader
@@ -1,18 +1,18 @@
#pragma once
// #pragma once
#include <memory>
// #include <memory>
namespace bts {
namespace orderman {
// namespace bts {
// namespace orderman {
class SpreaderOrderHeader {
public:
OrderKey order_key;
AccountName account_name;
OperatorID operator_id;
api::orderman::CustomerOrFirm customer_or_firm;
api::orderman::CTICode cti_code;
};
// class SpreaderOrderHeader {
// public:
// OrderKey order_key;
// AccountName account_name;
// OperatorID operator_id;
// api::orderman::CustomerOrFirm customer_or_firm;
// api::orderman::CTICode cti_code;
// };
} // namespace orderman
} // namespace bts
// } // namespace orderman
// } // namespace bts
Whitespace-only changes.
@@ -0,0 +1,54 @@
#pragma once
#include "PriceSize.h"
#include <memory>
namespace bts {
namespace spreader {
using BookSide = std::array<PriceSize, 5>;
class Book {
public:
Book() : _security_key(), _book() {}
Book(SecurityKey key) : _security_key(key), _book() {}
Book(SecurityKey key, BookSide const &bid, BookSide const &ask)
: _security_key(key), _book{bid, ask} {}
Book(Book const &rhs) : _security_key(rhs._security_key), _book(rhs._book) {}
Book &operator=(Book const &rhs) {
_security_key = rhs._security_key;
_book = rhs._book;
return *this;
}
SecurityKey security_key() const { return _security_key; };
BookSide const &bid() const { return _book[0]; }
BookSide const &ask() const { return _book[1]; }
BookSide &bid() { return _book[0]; }
BookSide &ask() { return _book[1]; }
size_t n_levels() const { return std::tuple_size<BookSide>(); }
BookSide const &side(api::Side bs) const {
bts_assert_d(bs != api::SIDE_UNDEFINED);
return _book[bs - api::SIDE_BUY];
}
BookSide &side(api::Side bs) {
bts_assert_d(bs != api::SIDE_UNDEFINED);
return _book[bs - api::SIDE_BUY];
}
private:
// friend class MDSource;
SecurityKey _security_key;
std::array<BookSide, 2> _book;
};
} // namespace spreader
} // namespace bts
Loaded 100 of 219 files, more files were not shown because too many files have changed in this diff. Show more