Compare commits
31
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b95da40fec | ||
|
|
755f841a6e | ||
|
|
2d10c94b69 | ||
|
|
dfc5fcc16a | ||
|
|
864e2a3edc | ||
|
|
b9142202e0 | ||
|
|
7353a6dc3c | ||
|
|
b55eecc8b9 | ||
|
|
3f0078d582 | ||
|
|
e47186f0fd | ||
|
|
4b92292fa1 | ||
|
|
3a8125bba3 | ||
|
|
0b14bd1ca3 | ||
|
|
63252b017b | ||
|
|
df1efb2295 | ||
|
|
3f37777eb9 | ||
|
|
04629f28e8 | ||
|
|
47c4d8c4a7 | ||
|
|
eb87ebcfa1 | ||
|
|
195fd11e2e | ||
|
|
413c24d147 | ||
|
|
fe4be118ee | ||
|
|
7ff09902b8 | ||
|
|
f90e41ea65 | ||
|
|
ff2546da56 | ||
|
|
f7426d9f0f | ||
|
|
36bbcf849c | ||
|
|
bd39d407aa | ||
|
|
81dde8e9df | ||
|
|
3f4a4fbac8 | ||
|
|
2e6676b151 |
No files matched your search
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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": ""
|
||||
}
|
||||
}
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
+15
@@ -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 @@
|
||||
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
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
#pragma once
|
||||
|
||||
#ifndef _WIN32
|
||||
#include <bits/stdint-uintn.h>
|
||||
#endif
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "OutFIX.h"
|
||||
|
||||
namespace bts {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "AsyncReader.h"
|
||||
|
||||
#include "IConnection.h"
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "IConnection.h"
|
||||
#endif
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "JetFIXGateway.h"
|
||||
|
||||
namespace bts {
|
||||
|
||||
@@ -34,7 +34,7 @@ struct LogRec {
|
||||
};
|
||||
GCC_DIAG_RESTORE()
|
||||
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
// static_assert(sizeof(LogRec) == 256, "LogRec should be 128 bytes");
|
||||
#endif
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,6 +1,6 @@
|
||||
#include "SecurityDB.h"
|
||||
#include "lib/logging/logging.h"
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
|
||||
#include <SQLiteCpp/SQLiteCpp.h>
|
||||
#include <sqlite/sqlite3.h>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "lib/domain_model/Security.h"
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "SecurityIDRecorder.h"
|
||||
|
||||
#include "lib/config/config.h"
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "MDProducerHandle.h"
|
||||
#include "common.h"
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "SettlementRecorder.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/system.pb.h"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "SyntheticSecurityService.h"
|
||||
|
||||
#include "lib/api/public/ServiceContext.h"
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,4 +1,4 @@
|
||||
#ifndef WIN32
|
||||
#ifndef _WIN32
|
||||
#include "OnixsUtils.h"
|
||||
#include "lib/config/config.h"
|
||||
#include "lib/config/protos/market_data.pb.h"
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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 ¶ms = 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 ¶ms = 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 ¶ms = 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> ¶ms) {
|
||||
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> ¶ms) {
|
||||
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 ¶ms,
|
||||
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> ¶ms) {
|
||||
// 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> ¶ms) {
|
||||
// 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 ¶ms,
|
||||
// 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
|
||||
+67
-67
@@ -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
|
||||
+70
-70
@@ -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
|
||||
+36
-36
@@ -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
Reference in new issue
Block a user