Compare commits

...
33 Commits
Author SHA1 Message Date
Eric Harding ed06924df1 Merged in SPARK-1376 (pull request #42)
SPARK-1176 Named Window

Approved-by: Michael Harding <michael.harding@bluetradesys.com>
Approved-by: Jake Norbut <jake.norbut@bluetradesys.com>
2018-07-09 21:45:19 +00:00
Eric Harding b78179ccee SPARK-1376 Tweak feature name, allow remove from context menu. 2018-07-09 17:44:28 -04:00
Eric Harding 5a00c5eb2a SPARK-1376 Save closed named windows with workspace 2018-07-09 16:17:48 -04:00
Eric Harding 74fa41f151 Quick cleanup of methods that show up on 'object's intellisense 2018-07-09 16:17:48 -04:00
Eric Harding 32d5fc2c37 SPARK-1376 Bookmarks launcher module 2018-07-09 16:17:48 -04:00
Eric Harding 1049046d0b SPARK-1176 Named Windows 2018-07-09 16:17:48 -04:00
Eric Harding 1f8e0a1e2b SPARK-1446 Try to add securities to security DB even if they already have an assigned security key 2018-07-09 16:09:35 -04:00
Kevin Darby 5d3d5251c7 added git_switch_linux.cmd to switch branches on sparkvm so we don't have to unison linux 2018-07-05 15:07:30 -04:00
Taha Afzal f7cee30511 test for n legs 2018-07-05 10:00:05 -04:00
Taha Afzal 98690ee747 ractional hedging test 2018-07-05 00:04:25 -04:00
Taha Afzal 3e14d83dde Merge branch 'prod' of bitbucket.org:bluetradesys/spark into nlegs 2018-07-03 18:54:56 -04:00
Taha Afzal 746622161c SPARK-1366 unit tests 2018-07-02 20:33:34 -04:00
Taha Afzal c064cdd215 SPARK-1366 code complete, writing tests now 2018-07-02 18:59:51 -04:00
Eric Harding acce1e3f7d SPARK-1376 Need to explicitly ask for direct book maintenance 2018-07-02 11:30:50 -04:00
Taha Afzal 9bdf084838 SPARK-1366 ciruclar buffer for keeping history of drivers complete 2018-07-01 23:40:57 -04:00
Taha Afzal 5132f81bc1 name change to QuoteLegDriver 2018-07-01 20:17:41 -04:00
Taha Afzal ab797a311e QuoterLegDriverData broken into a cpp 2018-07-01 19:48:51 -04:00
Taha Afzal d032b23841 added 'compute_driver' which calculates the driver price/qty from a quoting leg qty - this is now called by both n legs and 2 legs 2018-07-01 18:33:04 -04:00
Taha Afzal 8c68a66ef1 Merge branch 'prod' of bitbucket.org:bluetradesys/spark into nlegs 2018-06-29 19:14:56 -04:00
Taha Afzal d617d1597c SPARK 1432 fractional legs 2018-06-29 18:58:44 -04:00
Pedro Pinto 5206a378ff Fixed stop order price encoding 2018-06-27 11:12:34 -04:00
Pedro Pinto 7d2047d7ce Fixed the shared data problem that was crashing O'Grady. 2018-06-27 11:12:34 -04:00
Taha Afzal 8a43a4d157 SPARK-1416 (reactivated terminal status) 2018-06-27 11:12:34 -04:00
Jake Norbut 230e9e525f SPARK-1409 - slow picker fixed? 2018-06-27 11:12:34 -04:00
Jake Norbut 034318db5e fix early orders unsub - exception was taking out the observable 2018-06-27 11:12:34 -04:00
Pedro Pinto 0758dd6f46 Fixed clang build. 2018-06-27 11:12:34 -04:00
Pedro Pinto 83f9f3375c Fixed crash on client close. 2018-06-27 11:12:33 -04:00
Pedro Pinto 98a6616804 Removed current link in jetfix, it doesn't work well with docker. 2018-06-27 11:12:33 -04:00
Pedro Pinto b46f811cb5 Tweaks to pp custom build environment. 2018-06-27 11:12:33 -04:00
John Colvin 6d918c0962 SPARK-1418 Remove constraint on creating NLegs spreads 2018-06-25 17:10:04 -04:00
Taha Afzal 5b5274ab5f 1366 added hedging test 2018-06-21 18:02:08 -04:00
Taha Afzal 2d5211ac9b SPARK-1366 n legs 2018-06-20 21:13:02 -04:00
Taha Afzal e62d116a44 SPARK-1366 n legs 2018-06-20 20:42:36 -04:00
54 changed files with 1128 additions and 331 deletions

No files matched your search

+2 -4
View File
@@ -47,7 +47,7 @@ namespace mbts.ui
//public static ImageSource Ticket { get { return load_image("Ticket.png"); } } // replaced with AddOrders
public static ImageSource Date { get { return load_image("Date.png"); } }
public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; /*return load_image("Cube-Molecule.png");*/ } }
public static ImageSource Cube3d { get { return (DrawingImage)_resources["Cube"]; } }
public static ImageSource Algo { get { return Cube3d; } }
public static ImageSource Quote { get { return Math; } }
public static ImageSource Math { get { return (DrawingImage)_resources["Surface"]; } }
@@ -62,9 +62,7 @@ namespace mbts.ui
#pragma warning restore CS0618
// Vector icons
public static ImageSource Favorites { get { return Favorites_Gold; } }
public static ImageSource Favorites_Gold { get { return (DrawingImage)_resources["FavoritesGold"]; } }
public static ImageSource Favorites_White { get { return (DrawingImage)_resources["FavoritesWhite"]; } }
public static ImageSource Bookmarks { get { return (DrawingImage)_resources["Bookmarks"]; } }
public static ImageSource Window { get { return (DrawingImage)_resources["Window"]; } }
public static ImageSource ActiveWindow { get { return (DrawingImage)_resources["ActiveWindow"]; } }
public static ImageSource Trash { get { return (DrawingImage)_resources["Trash"]; } }
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

+3 -19
View File
@@ -3,33 +3,17 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:mbts="clr-namespace:mbts.ui">
<DrawingImage x:Key="FavoritesGold">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M1540.22,2082.07L1546.95,2102.78 1568.73,2102.78 1551.11,2115.58 1557.84,2136.29 1540.22,2123.49 1522.6,2136.29 1529.33,2115.58 1511.71,2102.78 1533.49,2102.78 1540.22,2082.07z">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<SolidColorBrush Color="#ffd932" />
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingGroup.Children>
</DrawingGroup>
</DrawingImage.Drawing>
</DrawingImage>
<DrawingImage x:Key="FavoritesWhite">
<DrawingImage x:Key="Bookmarks">
<DrawingImage.Drawing>
<DrawingGroup>
<DrawingGroup.Children>
<GeometryDrawing Geometry="M1540.22,2082.07L1546.95,2102.78 1568.73,2102.78 1551.11,2115.58 1557.84,2136.29 1540.22,2123.49 1522.6,2136.29 1529.33,2115.58 1511.71,2102.78 1533.49,2102.78 1540.22,2082.07z">
<GeometryDrawing.Pen>
<Pen Thickness="1" Brush="Black" />
<Pen Thickness="3" Brush="Black" />
</GeometryDrawing.Pen>
<GeometryDrawing.Brush>
<SolidColorBrush Color="#ffd932" />
<SolidColorBrush Color="Transparent" />
</GeometryDrawing.Brush>
</GeometryDrawing>
</DrawingGroup.Children>
@@ -0,0 +1,81 @@
using bts.spark;
using mbts.spark;
using mbts.ui;
using mbts.ui.settings;
using mbts.ui.shell;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Media;
namespace bts.ui {
[Export(typeof(IShellModule))]
public class BookmarkShellModule : ILazyMenuModule {
readonly WindowManager _wm;
readonly IBookmarks _bookmarks;
readonly ISavedWindowFactory _window_factory;
readonly WorkspacesModel _workspaces;
[ImportingConstructor]
public BookmarkShellModule(
WorkspacesModel workspaces,
WindowManager wm,
IBookmarks bm,
ISavedWindowFactory swf) {
_workspaces = workspaces;
_wm = wm;
_bookmarks = bm;
_window_factory = swf;
}
public ImageSource icon => Icons.Bookmarks;
public string name => "My Windows";
public int priority => 35;
public IEnumerable<SparkCommand> actions {
get {
// The windows that are open
var open =
_wm.list_documents()
.Select(td => td.ViewModel)
.Where(vm => vm.HasCustomTitle)
.Select(vm =>
SparkCommand.create(vm.Title, vm.Icon, SparkTags.ActivateWindow, () => vm.Model))
.Cast<SparkCommand>();
var open_set = new HashSet<string>(open.Select(c => c.title));
// The windows that are not open
var closed =
_bookmarks.bookmarks
.Where(state => !open_set.Contains(state.title))
.Select(state =>
SparkCommand.create(state.title, Icons.Bookmarks, SparkTags.ShowWindowAsync,
() => {
_bookmarks.remove(state);
return _window_factory.restore(state);
}))
.Cast<SparkCommand>();
// todo: The windows that are closed
foreach (var w in open.Concat(closed).OrderBy(w => w.title)) {
yield return w;
}
}
}
public Task<IShellWindow> create(Stream load_stream) {
// This module shouldn't have any windows
throw new NotImplementedException();
}
public void Dispose() { }
}
}
@@ -0,0 +1,48 @@
using mbts.ui.settings;
using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace mbts.ui.shell {
public interface IBookmarks {
void save_bookmark(IShellWindow window);
void save_bookmark(WindowState window);
void remove(WindowState window);
void clear();
IEnumerable<WindowState> bookmarks { get; }
}
[Export(typeof(IBookmarks))]
public class BookmarksModel : IBookmarks {
readonly ISettingsCollector _settings_collector;
readonly Dictionary<string, WindowState> _bookmarks = new Dictionary<string, WindowState>();
[ImportingConstructor]
public BookmarksModel(ISettingsCollector settings) {
_settings_collector = settings;
}
public void save_bookmark(IShellWindow window) {
var state = _settings_collector.get_window_state(window);
if (state != null) {
_bookmarks[state.title] = state;
}
}
public void save_bookmark(WindowState window) {
_bookmarks[window.title] = window;
}
public void clear() {
_bookmarks.Clear();
}
public void remove(WindowState window) {
_bookmarks.Remove(window.title);
}
public IEnumerable<WindowState> bookmarks => _bookmarks.Values;
}
}
@@ -34,7 +34,7 @@ namespace bts.ui {
public int priority => 40;
public IEnumerable<SparkCommand> actions => new []{
public IEnumerable<SparkCommand> actions => new SparkCommand[]{
SparkCommands.ShowWorkspaces
//SparkCommands.ShowSettings
};
@@ -173,7 +173,8 @@ namespace mbts.spark {
}
_is_setting_price_mults = false;
};
_can_add_leg = backing(nameof(can_add_leg), leg_tuples.ObserveSelf().Select(_ => leg_count < 2), leg_count < 2);
//_can_add_leg = backing(nameof(can_add_leg), leg_tuples.ObserveSelf().Select(_ => leg_count < 2), leg_count < 2);
_can_add_leg = backing(nameof(can_add_leg), leg_tuples.ObserveSelf().Select(_ => true), true);
} else {
_can_add_leg = backing(nameof(can_add_leg), false);
}
@@ -75,7 +75,7 @@ namespace mbts.ui.tests
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_windows_disposed");
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
var thread_factory = new ShellThreadFactory();
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), null, new SimpleBus());
Assert.IsNotNull(Application.Current);
Assert.IsNotNull(Application.Current.Windows);
Assert.AreEqual(Application.Current.Windows.Count, 0);
@@ -100,7 +100,7 @@ namespace mbts.ui.tests
Logger.log(bts.log.Level.Info, bts.log.Subsystem.Ui, "Begin: test_slow_windows_disposed");
// Make sure that an IShellWindow and ISlowWindow added to the Windows collection is eventually disposed
var thread_factory = new ShellThreadFactory();
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), new TestUndo(), new SimpleBus());
IWindowManager wm = new WindowManager(thread_factory, new Lazy<ISettingsCollector>(() => new TestSettingsCollector()), new TestSavedWindowFactory(), null, new SimpleBus());
Assert.AreEqual(Application.Current.Windows.Count, 0);
// an ISlowWindow gets its own thread
+3
View File
@@ -5,6 +5,7 @@ using bts.spark;
using mbts.ui;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using static mbts.spark.SparkBusData;
namespace mbts.spark
@@ -34,6 +35,8 @@ namespace mbts.spark
public static ITag<(PartialCommand<SecurityMD>, SecurityCriteria)> FilterPickOne { get; } = new SparkTag<(PartialCommand<SecurityMD>, SecurityCriteria)>("FilterPick1");
public static ITag<IShellWindow> ShowWindow { get; } = new SparkTag<IShellWindow>("Show Window");
public static ITag<Task<IShellWindow>> ShowWindowAsync { get; } = new SparkTag<Task<IShellWindow>>("ShowWindowAsync");
public static ITag<IShellWindow> ActivateWindow { get; } = new SparkTag<IShellWindow>("Activate Window");
public static ITag<Nothing> ShowWorkspaces { get; } = new SparkTag<Nothing>(nameof(ShowWorkspaces));
public static ITag<Nothing> ShowSettings { get; } = new SparkTag<Nothing>(nameof(ShowSettings));
public static ITag<Uri> ShowUri { get; } = new SparkTag<Uri>("ShowUri");
@@ -48,10 +48,10 @@ namespace System.Collections.Generic {
return rv;
}
public static IEnumerable<T> ToIEnumerable<T>(this T item) {
yield return item;
}
// use "new [] { item }" - avoid putting this on the intellisense for every item everywhere
//public static IEnumerable<T> ToIEnumerable<T>(this T item) {
// yield return item;
//}
public static IEnumerable<T> TakeWhile<T, TAccumulate>(this IEnumerable<T> source, TAccumulate seed, Func<TAccumulate, T, TAccumulate> accumulate, Func<TAccumulate, T, bool> predicate) {
TAccumulate a = seed;
@@ -69,16 +69,18 @@ namespace mbts.ui.settings
if (window == null || window.Owner == null) return null;
MemoryStream out_stream = new MemoryStream();
window.GetState(out_stream);
var pos = _interop.GetPosition(window);
var state = new WindowState() {
owner = window.Owner.key(),
app_data = out_stream.ToArray(),
position = _interop.GetPosition(window),
title = window.Title
position = pos,
title = string.IsNullOrEmpty(pos.custom_title) ? window.Title : pos.custom_title
};
return state;
}
catch (Exception e) {
Logger.log(bts.log.Level.Error, bts.log.Subsystem.Ui, "Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
Logger.log(Level.Error, Subsystem.Ui, "Error saving window state for type {0}.\n {1}".fmt(window.GetType(), e.Message));
}
return null;
}
@@ -39,6 +39,9 @@ namespace mbts.ui.shell {
[ProtoMember(102), DefaultValue(true)]
public bool always_on_top = true;
//[ProtoMember(103)] //used
[ProtoMember(104)]
public string custom_title;
}
[ProtoContract]
@@ -58,7 +61,7 @@ namespace mbts.ui.shell {
public WindowPosition position;
[ProtoMember(9)]
public string title;
public string title; // for debugging
}
[ProtoContract]
@@ -23,6 +23,9 @@ namespace mbts.ui
Task<IShellWindow> create(Stream load_stream);
}
// ILazyModule may also impement IShellMenu to get opening/closing notification
public interface ILazyMenuModule : IShellModule { }
public interface IPersistShellModule : IShellModule {
// Called at startup before the UI is shown
// This is an opportunity for you to kick off background services
@@ -17,5 +17,5 @@ namespace mbts.ui
WindowPosition GetPosition(IShellWindow window);
bool IsVisible(IShellWindow window);
void PinWindow(IShellWindow window);
}
}
}
@@ -10,9 +10,11 @@ namespace mbts.ui.controls {
static SparkMenu() =>
DefaultStyleKeyProperty.OverrideMetadata(typeof(SparkMenu), new FrameworkPropertyMetadata(typeof(SparkMenu)));
public SparkMenu() =>
public SparkMenu() {
SetCurrentValue(FocusableProperty, false);
}
public SparkMenu(IEnumerable<SparkCommand> commands, IBus bus) : this() =>
ItemsSource = commands.Select(c => new SparkCommandVM(c, bus)).ToList();
@@ -61,16 +61,16 @@ namespace mbts.ui
_source_window = window as FloatingWindow;
if (_source != null) {
_mouse_start = e.GetPosition(window);
if (!is_button_descendant((DependencyObject)e.OriginalSource)) {
if (!is_clickable_descendant((DependencyObject)e.OriginalSource)) {
Mouse.Capture(_source);
}
}
}
static bool is_button_descendant(DependencyObject target) {
static bool is_clickable_descendant(DependencyObject target) {
while (target != null && !(target is TabDocument)) {
target = VisualTreeHelper.GetParent(target);
if (target is Button) return true;
if (target is Button || target is TextBox) return true;
}
return false;
}
@@ -24,38 +24,7 @@ namespace mbts.ui
}
}
public static async Task FromEvent(this object target, string event_name) {
var evt = target.GetType().GetEvent(event_name);
var delegate_type = evt.EventHandlerType;
var tcs = new TaskCompletionSource<object>();
Action result = () => { tcs.TrySetResult(null); };
var d = Delegate.CreateDelegate(evt.EventHandlerType, result, "Invoke");
try {
evt.AddEventHandler(target, d);
await tcs.Task;
}
finally {
evt.RemoveEventHandler(target, d);
}
}
public static async Task<T> FromEvent<T>(this object target, string event_name) {
var evt = target.GetType().GetEvent(event_name);
var delegate_type = evt.EventHandlerType;
var tcs = new TaskCompletionSource<T>();
Action<T> result = (arg) => { tcs.TrySetResult(arg); };
var d = Delegate.CreateDelegate(evt.EventHandlerType, result, "Invoke");
try {
evt.AddEventHandler(target, d);
await tcs.Task;
return tcs.Task.Result;
}
finally {
evt.RemoveEventHandler(target, d);
}
}
public static async Task<Tuple<T1,T2>> FromEvent<T1,T2>(this object target, string event_name) {
public static async Task<Tuple<T1,T2>> FromEvent<T1,T2>(this DependencyObject target, string event_name) {
var evt = target.GetType().GetEvent(event_name);
var delegate_type = evt.EventHandlerType;
var tcs = new TaskCompletionSource<Tuple<T1,T2>>();
@@ -14,18 +14,19 @@ namespace mbts.ui.shell
public partial class FloatingWindow : Window
{
bool _closed = false;
private IUndoActions _undo;
readonly IBookmarks _bookmark;
private IWindowManager _wm;
private ISavedWindowFactory _window_factory;
private ISettingsCollector _window_settings;
private Stack<Action> _recently_closed_windows = new Stack<Action>();
// if is_loaded is true, will NOT set width/height
internal FloatingWindow(TabDocument document, IWindowManager wm, IUndoActions undo, ISettingsCollector settings, ISavedWindowFactory factory, bool is_loaded=false) {
internal FloatingWindow(TabDocument document,
IWindowManager wm, IBookmarks bookmarks, ISettingsCollector settings, ISavedWindowFactory factory, bool is_loaded=false) {
_wm = wm;
_undo = undo;
_window_settings = settings;
_window_factory = factory;
_bookmark = bookmarks;
InitializeComponent();
if (string.IsNullOrEmpty(document.ViewModel.host_id)) {
@@ -67,6 +68,7 @@ namespace mbts.ui.shell
private ItemCollection Items { get { return _tabs.Items; } }
public IEnumerable<TabDocument> Documents => _tabs.Items.OfType<TabDocument>();
public bool IsEmpty { get { return _tabs.Items.Count == 0; } }
public bool SingleChild { get { return _tabs.Items.Count == 1; } }
public string host_id { get; private set; }
@@ -88,10 +90,10 @@ namespace mbts.ui.shell
if (sender != this) {
var undoaction = create_undo_action(document);
if (undoaction != null) {
IDisposable undoitem = _undo.push("Restore Tab '{0}'".fmt(document.Title), undoaction);
_recently_closed_windows.Push(() => { undoaction(); undoitem.Dispose(); });
_recently_closed_windows.Push(() => { undoaction(); });
}
}
_bookmark.save_bookmark(document.Model);
}
public void remove_document(TabDocument document) {
@@ -144,22 +146,9 @@ namespace mbts.ui.shell
protected override void OnClosed(EventArgs e) {
_closed = true;
TabDocument[] items = _tabs.Items.OfType<TabDocument>().ToArray();
if (items.Length > 0) {
// If we're closing and still have Tabs open we save the whole "window" worth of state in the undo stack
// If the application is shutting down this might be unnecessary work but it's not too costly
var undo_actions = items.Select(t => create_undo_action(t.ViewModel)).Where(item => item != null).ToArray();
if (undo_actions.Length > 0) {
string name = "Restore Window: '{0}'".fmt(string.Join("', '", items.Select(t => t.ViewModel.Title)));
_undo.push(name, () => {
foreach (var u in undo_actions) { u(); }
});
}
}
base.OnClosed(e);
foreach (TabDocument document in items) {
foreach (TabDocument document in _tabs.Items.OfType<TabDocument>().ToList()) {
if (document != null && document.ViewModel.CloseCommand.CanExecute(this)) {
document.ViewModel.CloseCommand.Execute(this);
}
+39 -12
View File
@@ -3,7 +3,9 @@
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
mc:Ignorable="d" Style="{StaticResource {x:Type TabItem}}" d:DesignWidth="125"
xmlns:ui="clr-namespace:mbts.ui"
xmlns:convert="clr-namespace:mbts.ui.converters"
mc:Ignorable="d" d:DesignWidth="125"
Header="{Binding}" Content="{Binding AppContent}"
Background="#e6e6e6">
@@ -18,6 +20,18 @@
<Storyboard x:Key="_slide_back">
<DoubleAnimation Storyboard.TargetName="translate_transform" Storyboard.TargetProperty="X" To="100" Duration="0:0:0.25" FillBehavior="Stop" />
</Storyboard>
<DataTemplate x:Key="_edit_template">
<TextBox Text="{Binding TitleOverride}"
ui:TextBoxBehavior.CommitOnEnter="True"
ui:TextBoxBehavior.FocusOnLoaded="True"
ui:TextBoxBehavior.SelectAllOnFocus="True"
ui:TextBoxBehavior.ReleaseOnEscape="True"
Width="150" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0" />
</DataTemplate>
<DataTemplate x:Key="_template">
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0"></TextBlock>
</DataTemplate>
<convert:BooleanToObjectConverter x:Key="MenuHeaderConverter" ValueForTrue="Rename Window" ValueForFalse="Add to My Windows" />
</ResourceDictionary>
</TabItem.Resources>
<TabItem.LayoutTransform>
@@ -28,25 +42,32 @@
<TabItem.RenderTransform>
<TranslateTransform x:Name="translate_transform" X="0" />
</TabItem.RenderTransform>
<TabItem.HeaderTemplate>
<DataTemplate>
<TextBlock Text="{Binding Title}" TextTrimming="CharacterEllipsis" HorizontalAlignment="Left" VerticalAlignment="Stretch" Grid.Column="0" Margin="0,0,4,0"> </TextBlock>
</DataTemplate>
</TabItem.HeaderTemplate>
<TabItem.Template>
<ControlTemplate TargetType="{x:Type TabItem}">
<Border x:Name="bd" BorderBrush="Black" BorderThickness="0,0,0.5,0" Background="#d6d6d6" Width="{DynamicResource TabWidth}" Height="{DynamicResource TabHeight}" MinWidth="40" Margin="0" SnapsToDevicePixels="True" DockPanel.Dock="Left">
<Border.ToolTip>
<TextBlock Text="{Binding Title}" />
</Border.ToolTip>
<Border.ContextMenu>
<ContextMenu>
<MenuItem Header="{Binding HasCustomTitle, Converter={StaticResource MenuHeaderConverter}}" Command="{Binding rename_command}">
<MenuItem.Icon>
<Image Source="{StaticResource Bookmarks}" />
</MenuItem.Icon>
</MenuItem>
<MenuItem Header="Remove from My Windows" Command="{Binding unrename_command}" IsEnabled="{Binding HasCustomTitle}" />
</ContextMenu>
</Border.ContextMenu>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="Auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="Auto" />
</Grid.ColumnDefinitions>
<Image SnapsToDevicePixels="True" MaxHeight="20" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Margin="5,0,5,0" Source="{Binding Icon, Mode=OneTime}" RenderOptions.BitmapScalingMode="HighQuality" />
<Image SnapsToDevicePixels="True" MaxHeight="20" VerticalAlignment="Center" HorizontalAlignment="Left" Stretch="Uniform" Margin="5,0,5,0"
Source="{Binding Icon, Mode=OneTime}" RenderOptions.BitmapScalingMode="HighQuality" />
<!--<Image MaxHeight="10" HorizontalAlignment="Left" VerticalAlignment="Bottom" Stretch="Uniform" Margin="5"
Source="{StaticResource Bookmarks}" Visibility="{Binding HasCustomTitle, Converter={StaticResource BoolToVisibility}}" />-->
<ContentPresenter Grid.Column="1" x:Name="content" ContentSource="Header" TextBlock.Foreground="#666"
RecognizesAccessKey="True" SnapsToDevicePixels="{TemplateBinding SnapsToDevicePixels}"
HorizontalAlignment="Left"
@@ -62,12 +83,18 @@
<Setter Property="Panel.ZIndex" Value="1" />
<Setter Property="Background" TargetName="bd" Value="#F9F9F9" />
<Setter Property="TextBlock.Foreground" TargetName="content" Value="black" />
<!--<Setter Property="BorderThickness" TargetName="bd" Value="0,4,0.5,0" />-->
<!--<Setter Property="Margin" Value="-2,-2,-2,-1" />
<Setter Property="Margin" TargetName="content" Value="0,0,0,1" />
<Setter Property="Margin" TargetName="bd" Value="0,0,0,0" />-->
</Trigger>
</ControlTemplate.Triggers>
</ControlTemplate>
</TabItem.Template>
<TabItem.Style>
<Style TargetType="TabItem" BasedOn="{StaticResource {x:Type TabItem}}">
<Setter Property="HeaderTemplate" Value="{StaticResource _template}" />
<Style.Triggers>
<DataTrigger Binding="{Binding EditMode}" Value="True">
<Setter Property="HeaderTemplate" Value="{StaticResource _edit_template}" />
</DataTrigger>
</Style.Triggers>
</Style>
</TabItem.Style>
</TabItem>
@@ -18,8 +18,15 @@ namespace mbts.ui.shell {
public ImageSource IconSource { get { return _module.icon; } }
public UIElement MenuContent => new SparkMenu(_module.actions, _bus);
public void opening() { }
public void opening() {
(_module as IShellMenu)?.opening();
if (_module is ILazyMenuModule) {
RaisePropertyChanged(nameof(MenuContent));
}
}
public void closing() { }
public void closing() {
(_module as IShellMenu)?.closing();
}
}
}
@@ -24,10 +24,11 @@ namespace mbts.ui.shell
Dispatcher _dispatcher;
HwndContentHost _hwnd_host;
public ShellWindowVM(IShellWindow model, IWindowManager wm, IShellThreadFactory threads, string host = "") {
Logger.assert(model != null);
_model = model;
_model.TitleChanged += () => RaisePropertyChanged("Title");
_model.TitleChanged += () => { if (!HasCustomTitle) RaisePropertyChanged("Title"); };
_model.IconChanged += () => { _icon = null; RaisePropertyChanged("Icon"); };
_wm = wm;
host_id = host;
@@ -40,13 +41,55 @@ namespace mbts.ui.shell
if (HasTabAffinity) {
this.host_id = ((ITabAffineWindow)model).tab_group;
}
_edit_mode = backing(nameof(EditMode), false);
}
public string host_id { get; set; }
public Dispatcher Dispatcher { get { return _dispatcher; } }
public string Title { get { return _model.Title; } }
public string Title { get { return HasCustomTitle? TitleOverride : _model.Title; } }
string _title_override;
public string TitleOverride {
get => _title_override;
set {
if (_title_override != value) {
if (value == _model.Title) {
_title_override = null;
}
else {
_title_override = value;
}
RaisePropertyChanged(nameof(TitleOverride), nameof(Title), nameof(HasCustomTitle));
}
_edit_mode.set(false);
}
}
public bool HasCustomTitle => !string.IsNullOrWhiteSpace(TitleOverride);
Property<bool> _edit_mode;
public bool EditMode => _edit_mode.Value;
public ICommand rename_command => new DelegateCommand(begin_rename);
public ICommand unrename_command => new DelegateCommand(remove_custom);
private void begin_rename() {
if (string.IsNullOrEmpty(TitleOverride)) {
_title_override = Title;
}
_edit_mode.set(true);
}
private void remove_custom() {
TitleOverride = string.Empty;
//begin_close(this);
}
public string OwnerName { get { return _model.Owner == null ? "" : _model.Owner.name; } }
public ICommand CloseCommand { get { return new DelegateCommand(begin_close); } }
public double InitialWidth { get; private set; }
public double InitialHeight { get; private set; }
@@ -155,18 +198,14 @@ namespace mbts.ui.shell
}
}
public byte[] GetSaveState() {
MemoryStream stream = new MemoryStream();
_model.GetState(stream);
return stream.ToArray();
}
public IShellWindow Model { get { return _model; } }
public bool IsSlow { get { return _model is ISlowWindow; } }
public bool IsShared { get { return _model is ISharedWindow; } }
public bool HasTabAffinity { get { return _model is ITabAffineWindow; } }
public void Dispose() {
if (IsShared) {
// Shared threads continue running
+26 -13
View File
@@ -20,25 +20,31 @@ namespace mbts.ui.shell {
[Export(typeof(IWindowManager))]
[Export(typeof(IWindowsInterop))]
[Export]
public class WindowManager : IWindowManager, IWindowsInterop, IDisposable {
Dictionary<IShellWindow, TabDocument> _documents = new Dictionary<IShellWindow, TabDocument>();
Dictionary<string, FloatingWindow> _windows = new Dictionary<string, FloatingWindow>();
IShellThreadFactory _threads;
readonly IShellThreadFactory _threads;
Lazy<ISettingsCollector> _window_settings;
ISavedWindowFactory _window_factory;
IUndoActions _undo;
readonly ISavedWindowFactory _window_factory;
readonly Lazy<IBookmarks> _bookmarks;
IDisposable _dispose;
object _lock = new object();
[ImportingConstructor]
public WindowManager(
IShellThreadFactory threads, Lazy<ISettingsCollector> settings,
ISavedWindowFactory factory, IUndoActions undo, IBus bus) {
ISavedWindowFactory factory, Lazy<IBookmarks> bookmarks, IBus bus) {
_threads = threads;
_window_settings = settings;
_window_factory = factory;
_undo = undo;
_dispose = bus.subscribe(SparkTags.ShowWindow).OnDispatcher().Subscribe(w => add(w).ignore());
_bookmarks = bookmarks;
_dispose =
Disposable.Create(
bus.subscribe(SparkTags.ShowWindow).OnDispatcher().Subscribe(w => add(w).ignore()),
bus.subscribe(SparkTags.ShowWindowAsync).OnDispatcher().Subscribe(w => add(w).ignore()),
bus.subscribe(SparkTags.ActivateWindow).OnDispatcher().Subscribe(MakeVisible)
);
}
#region Window creation
@@ -48,6 +54,7 @@ namespace mbts.ui.shell {
var state = model.start_position();
if (state != null) {
viewmodel.host_id = state.host_id ?? "";
viewmodel.TitleOverride = state.custom_title;
}
var window = CreateWindow(content, state);
@@ -66,10 +73,10 @@ namespace mbts.ui.shell {
}
} else {
if (position != null) {
window = new FloatingWindow(document, this, _undo, _window_settings.Value, _window_factory, true);
window = new FloatingWindow(document, this, _bookmarks.Value, _window_settings.Value, _window_factory, true);
window.set_position(position);
} else
window = new FloatingWindow(document, this, _undo, _window_settings.Value, _window_factory);
window = new FloatingWindow(document, this, _bookmarks.Value, _window_settings.Value, _window_factory);
_windows.Add(window.host_id, window);
window.Closed += (o, e) => _windows.Remove(window.host_id);
}
@@ -81,12 +88,19 @@ namespace mbts.ui.shell {
public IReadOnlyList<IShellWindow> list_windows() =>
_documents.Keys.ToList();
public IReadOnlyList<TabDocument> list_documents() => _documents.Values.ToList();
public async Task add(IShellWindow window) {
if (!Application.Current.Dispatcher.CheckAccess())
await Application.Current.Dispatcher.InvokeAsync(() => add(window));
Logger.info(bts.log.Subsystem.Ui, $"Opening window {window.diagnostic_name()}.");
lock (_lock)
lock (_lock) {
_documents[window] = _create_document(window);
}
}
public async Task add(Task<IShellWindow> window) {
await add(await window);
}
public void remove(IShellWindow window) {
@@ -139,14 +153,13 @@ namespace mbts.ui.shell {
#region IWindowsInterop
public WindowPosition GetPosition(IShellWindow shell_window) {
return GetPosition(shell_window, null);
}
public WindowPosition GetPosition(IShellWindow shell_window, FloatingWindow owning_window) {
FloatingWindow window = owning_window == null ? window_for(shell_window) : owning_window;
FloatingWindow window = window_for(shell_window);
var doc = window.Documents.FirstOrDefault(td => td.ViewModel.Model == shell_window);
if (window != null) {
WindowPosition pos = window.get_position();
pos.tab_index = window.document_index(shell_window);
pos.host_id = window.host_id;
pos.custom_title = doc?.ViewModel?.TitleOverride;
return pos;
}
Logger.log(bts.log.Level.Warn, bts.log.Subsystem.Ui, "IShellWindow '{0}' does not have a valid position. It may not be displayed".fmt(shell_window.Title));
@@ -19,6 +19,7 @@ using mbts.persist;
namespace mbts.ui.shell
{
[Export]
public class WorkspacesModel : ViewModelBaseWithValidation
{
@@ -35,10 +36,11 @@ namespace mbts.ui.shell
private bool _closing = false;
private ILauncher _launcher;
private IUndoActions _undo;
readonly IBookmarks _bookmarks;
[ImportingConstructor]
public WorkspacesModel(ISettingsCollector settings, ISavedWindowFactory window_restore,
IWindowManager windows, ObservableCollection<IShellModule> modules, IPersist persist,
IWindowManager windows, ObservableCollection<IShellModule> modules, IPersist persist, IBookmarks bm,
IUserPersist user_persist, ILauncher launcher, IUndoActions undo, [Import("workspace")]string initial_workspace) {
_settings_collector = settings;
_window_restore = window_restore;
@@ -48,6 +50,7 @@ namespace mbts.ui.shell
_modules = modules;
_launcher = launcher;
_undo = undo;
_bookmarks = bm;
_launcher.closing += _launcher_closing;
Workspaces = new ObservableCollection<WorkspaceViewModel>();
init(initial_workspace).ignore();
@@ -150,6 +153,7 @@ namespace mbts.ui.shell
workspace.windows = _settings_collector.get_window_state().ToList();
workspace.modules = _settings_collector.get_module_state().ToList();
workspace.launcher_state = _settings_collector.get_launcher_state();
workspace.named_windows = _bookmarks.bookmarks.ToList();
return workspace;
}
@@ -166,6 +170,7 @@ namespace mbts.ui.shell
await Task.Yield();
}
workspace.launcher_state = _settings_collector.get_launcher_state();
workspace.named_windows = _bookmarks.bookmarks.ToList();
return workspace;
}
@@ -226,10 +231,15 @@ namespace mbts.ui.shell
}
}
// Notify workspace changed
if (WorkspaceLoaded != null) {
WorkspaceLoaded(name);
_bookmarks.clear();
if (def.named_windows != null) {
foreach (var window in def.named_windows) {
_bookmarks.save_bookmark(window);
}
}
// Notify workspace changed
WorkspaceLoaded?.Invoke(name);
}
public Task delete_workspace(string name, WorkspaceDefinition def) {
@@ -244,18 +254,20 @@ namespace mbts.ui.shell
}
return (def.is_shared ? _public : _private).clear(WORKSPACE_NAMESPACE, name);
}
}
// The serialized data for the Workspace
[ProtoContract(Name = "WorkspaceDefinition")]
public class WorkspaceDefinition
{
// Converted the protobuf type ShellSettings to c#
// Avoid re-using id: 1,3,4,5,6,7
[ProtoMember(2, IsRequired = false)]
public List<WindowState> windows { get; set; }
// named windows which are not currently open
[ProtoMember(3)]
public List<WindowState> named_windows { get; set; }
[ProtoMember(9, IsRequired = false)]
public List<ModuleState> modules { get; set; }
@@ -37,13 +37,13 @@ namespace bts.spark
if (this.IsRoot) {
return Enumerable.Empty<OrderNode>();
}
return this.Parent.ToIEnumerable().Concat(this.Parent.Ancestors);
return new[] { this.Parent }.Concat(this.Parent.Ancestors);
}
}
public IEnumerable<OrderNode> MeAndDescedants {
get {
return this.ToIEnumerable().Concat(this.children.SelectMany(c => c.MeAndDescedants));
return new[] { this }.Concat(this.children.SelectMany(c => c.MeAndDescedants));
}
}
+2 -5
View File
@@ -89,7 +89,7 @@ size_t load_securities(
void store_if_new(const std::vector<Security*>& securities) {
auto dbptr = open_db();
auto insert = utils::prepare_query(dbptr, "insert into securities (key, symbol, venue, type, security_blob, expiration) values (?,?,?,?,?,?)");
auto insert = utils::prepare_query(dbptr, "insert or ignore into securities (key, symbol, venue, type, security_blob, expiration) values (?,?,?,?,?,?)");
sqlite3_exec_nofail(dbptr, "BEGIN TRANSACTION");
std::string buffer;
for (const api::Security* psecurity : securities) {
@@ -100,10 +100,7 @@ void store_if_new(const std::vector<Security*>& securities) {
SQLITE_CHK(sqlite3_bind_int(insert.get(), 4, securitytype_of(*psecurity)), dbptr);
SQLITE_CHK(sqlite3_bind_blob(insert.get(), 5, buffer.c_str(), buffer.size(), SQLITE_STATIC), dbptr);
SQLITE_CHK(sqlite3_bind_int(insert.get(), 6, expiration(*psecurity).to_short()), dbptr);
auto result = sqlite3_step(insert.get());
if (result != SQLITE_DONE) {
LOG(md, Warn, "Failed to insert security into security db. key={}", psecurity->id());
}
sqlite3_step(insert.get());
sqlite3_reset(insert.get());
}
}
+13 -3
View File
@@ -133,6 +133,8 @@ void SecurityIDRecorder::work_db() {
// Once a security has a key it is public so record it in the security database
std::shared_ptr<SecurityIDEntry> temp;
vector<std::shared_ptr<SecurityIDEntry>> new_entries;
// SPARK-1446 Need to try to add existing securities to the db in case we deleted them by hand
vector<std::shared_ptr<SecurityIDEntry>> existing_securities;
vector<std::shared_ptr<SecurityIDEntry>> defer;
vector<string> new_descriptors;
size_t count = 0;
@@ -150,6 +152,7 @@ void SecurityIDRecorder::work_db() {
if (key != invalid_security_key) {
temp->security.set_id(key);
temp->callback(temp->security);
existing_securities.push_back(move(temp));
}
else {
// always push in parallel
@@ -168,7 +171,7 @@ void SecurityIDRecorder::work_db() {
}
if (new_entries.size() > 0) {
if (!new_entries.empty()) {
sqlite3_exec_nofail(_self->db.get(), "BEGIN TRANSACTION");
for (size_t x = 0; x < new_entries.size(); ++x) {
@@ -179,15 +182,22 @@ void SecurityIDRecorder::work_db() {
}
sqlite3_exec_nofail(_self->db.get(), "COMMIT TRANSACTION");
}
// record the securities
// record the securities
if (!new_entries.empty() || !existing_securities.empty()) {
std::vector<Security*> save_these;
save_these.reserve(new_entries.size());
save_these.reserve(new_entries.size() + existing_securities.size());
for (const auto& e : new_entries) {
save_these.push_back(&e->security);
}
for (const auto& e : existing_securities) {
save_these.push_back(&e->security);
}
db::store_if_new(save_these);
}
if (!new_entries.empty()) {
for (size_t x = 0; x < new_entries.size(); ++x) {
Security& security = new_entries[x]->security;
new_entries[x]->callback(security);
@@ -216,6 +216,9 @@ unique_ptr<onixs::Handler> MDP3Main::prepare_handler(const string& channel_id) {
if (options.book_type() == CMEOptions::BOOKTYPE_CONSOLIDATED) {
settings.bookManagement().consolidatedBooks().maintain(true);
}
else {
settings.bookManagement().directBooks().maintain(true);
}
// 10k default is *far* too low
settings.recoveryQueueMaxSize(options.recovery_queue_size());
@@ -53,7 +53,7 @@ void SpreaderOrder::on_activate(Price price, Quantity quantity,
}
void SpreaderOrder::on_update(Price price, Quantity quantity,TickToTradeCorID){
if (!_active) return;
LOG(OrderMan, Info, "SpreaderOrder.on_update key: {} price: {} quantity: {}", key().value(), price.value(), quantity.value());
LOG(OrderMan, Info, "SpreaderOrder.on_update key: {} price: {} quantity: {}", key().value(), price.value(), quantity.value());
_state_worker->on_update(price, quantity);
_report_state();
}
@@ -242,45 +242,50 @@ bool SpreaderOrder::_quoters_can_accept_modification (){
}
return true;
}
void SpreaderOrder::_send_hedge(size_t filled_leg_index,
Price filled_price,
Quantity filled_quantity,
size_t hedge_leg_index){
LOG(OrderMan, Info, "SpreaderOrder::_send_hedge key: {}, filled_leg_index: {} filled_price: {} filled_quantity: {} hedge_leg_index: {}",
key().value(), filled_leg_index, filled_price.value(), filled_quantity.value(), hedge_leg_index);
Price hedge_price;
Quantity hedge_quantity;
_compute_hedge_price_quantity (filled_leg_index, filled_price,
filled_quantity, hedge_leg_index,
hedge_price, hedge_quantity);
SingleFillHedger& hedger = _bookkeeping_data().add_new_hedger (*this,
void SpreaderOrder::_send_hedge(size_t hedge_leg_index,
Price hedge_price,
Quantity hedge_qty){
LOG(OrderMan, Info, "SpreaderOrder::_send_hedge key: {}, hedge_leg_index: {} hedge_price: {} hedge_qty: {}",
key().value(), hedge_leg_index, hedge_price, hedge_qty);
SingleFillHedger& hedger = _bookkeeping_data().add_new_hedger (*this,
hedge_leg_index,
hedge_price,
hedge_quantity);
hedge_qty);
hedger.init ();
}
void SpreaderOrder::_compute_hedge_price_quantity(size_t filled_leg_index,
Price fill_price, Quantity fill_quantity,
Price fill_price,
double fill_quantity,
size_t hedge_leg_index,
Price& hedge_price, Quantity& hedge_quantity){
Price& hedge_price,
double& hedge_quantity){
Price computed_hedge_price;
SpreaderLegData& filled_leg_data = _bookkeeping_data().get_leg_data(filled_leg_index);
SpreaderLegData& hedged_leg_data = _bookkeeping_data().get_leg_data(hedge_leg_index);
if (_bookkeeping_data().n_legs() == 2)
SpreaderUtils::compute_hedge_price_size_2_legs (_bookkeeping_data().price(),
filled_leg_data.leg_property_data(), fill_price, fill_quantity,
hedged_leg_data.leg_property_data(), computed_hedge_price, hedge_quantity);
filled_leg_data.leg_property_data(),
fill_price,
fill_quantity,
hedged_leg_data.leg_property_data(),
computed_hedge_price,
hedge_quantity);
else{
const QuoterLegDriverData& driver_data = filled_leg_data.quoter().driver_data(hedge_leg_index);
SpreaderUtils::compute_hedge_price_size_n_legs (filled_leg_data.leg_property_data(), fill_price, fill_quantity,
hedged_leg_data.leg_property_data(), driver_data,
computed_hedge_price, hedge_quantity);
const QuoteLegDriver& driver_data = filled_leg_data.quoter().driver_data(fill_price, hedge_leg_index);
SpreaderUtils::compute_hedge_price_size_n_legs (filled_leg_data.leg_property_data(),
fill_price,
fill_quantity,
hedged_leg_data.leg_property_data(),
driver_data,
computed_hedge_price,
hedge_quantity);
}
hedge_price = SpreaderUtils::apply_pre_hedge_parameters (hedged_leg_data.leg_property_data().leg_params().pre_hedge_params(),
hedged_leg_data.book(),
hedged_leg_data.leg_property_data().trading_side(),
hedged_leg_data.leg_property_data().tick_size(),
computed_hedge_price, hedge_quantity);
Price(computed_hedge_price),
Quantity(hedge_quantity));
}
void SpreaderOrder::_report_state (){
if (_activated()){
@@ -166,17 +166,15 @@ private:
// 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 filled_leg_index, Price filled_price,
Quantity filled_quantity,
size_t leg_to_hedge_index);
// purely a computating function that computes the
// hedge price and quantity (last 2 args) for leg
// 'hedged_leg_index' for a fill on leg 'filled_leg_index'
// with price 'filled_price' and quantity 'filled_quantity'
void _send_hedge(size_t hedge_leg_index,
Price hedge_price,
Quantity hedge_qty);
void _compute_hedge_price_quantity(size_t filled_leg_index,
Price filled_price, Quantity filled_quantity,
Price filled_price,
double filled_quantity,
size_t hedged_leg_index,
Price& hedge_price, Quantity& hedge_quantity);
Price& hedge_price,
double& hedge_quantity);
// sends the report out
void _report_state ();
SpreaderBookkeepingData& _bookkeeping_data(){
@@ -20,6 +20,7 @@ _spreader(spreader),
_this_leg_data (this_leg_property),
_n_spreader_legs (n_spreader_legs),
_child_order_state (child_order_state),
_driver_cache(n_spreader_legs,n_spreader_legs>2?10:0,this_leg_property.tick_size()),
_price_quantity_state(Price(0), Quantity(0), child_order_state),
_is_permenantly_cancelled (false),
_worker(nullptr),
@@ -33,8 +34,7 @@ _end_state_worker(this){
if (_active_quoting){
if (!_two_legged){
for (size_t q_idx = 0; q_idx < _n_spreader_legs; q_idx++){
_drivers_commited.push_back (QuoterLegDriverData());
_drivers_temp_usage.push_back (QuoterLegDriverData());
_drivers_temp_usage.push_back (QuoteLegDriver());
}
}
}
@@ -49,7 +49,7 @@ _this_leg_data (other._this_leg_data),
_n_spreader_legs (other._n_spreader_legs),
_child_order_state (other._child_order_state),
_drivers_temp_usage(other._drivers_temp_usage),
_drivers_commited (other._drivers_commited),
_driver_cache(other._driver_cache),
_price_quantity_state(other._price_quantity_state.target_spread_price(),
other._price_quantity_state.target_leg_quantity(),
other._price_quantity_state.order_state()),
@@ -131,13 +131,8 @@ bool SingleLegQuoter::is_terminal_state () const {
bool SingleLegQuoter::accept_modifications() const {
return _worker->state() == QuoterState::WAITING_FOR_SIZE || _worker->state() == QuoterState::WORKING;
}
Price SingleLegQuoter::driver_price (size_t leg_idx) const{
bts_assert (leg_idx < _n_spreader_legs);
return _drivers_commited[leg_idx].driver_price;
}
void SingleLegQuoter::_incorporate_other_leg_fill (size_t leg_index, Quantity filled_quantity){
Quantity this_leg_reduction = SpreaderUtils::get_leg_quantity_from_another_leg_quantity (_spreader.get_leg_data(leg_index), filled_quantity, _this_leg_data);
_price_quantity_state.add_hedged_reduction (this_leg_reduction);
void SingleLegQuoter::_incorporate_other_leg_fill (size_t, Quantity hedge_reduction){
_price_quantity_state.add_hedged_reduction (hedge_reduction);
}
void SingleLegQuoter::_compute_price_quantity(Price& price, Quantity& quantity){
Quantity remaining_quantity = _price_quantity_state.remaining_to_fill();
@@ -152,11 +147,9 @@ void SingleLegQuoter::_compute_price_quantity(Price& price, Quantity& quantity){
SpreaderUtils::compute_quoting_price_size_n_legs (_price_quantity_state.target_spread_price(), remaining_quantity,
_this_leg_data.leg_index(), _spreader, _drivers_temp_usage, price, quantity);
}
void SingleLegQuoter::_commit_drivers(){
if (!_two_legged)
// this could be done with a pointer swap too
for (size_t idx = 0; idx < _n_spreader_legs; idx++)
_drivers_commited[idx] = _drivers_temp_usage[idx];
void SingleLegQuoter::_commit_drivers(Price quote_price){
if (!_two_legged)
_driver_cache.add(_drivers_temp_usage,quote_price);
}
/*********************************************************************************/
/*******************************state transformations*****************************/
@@ -14,9 +14,10 @@
#include "server/sparkd/orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWaitingForCancelAckStateWorker.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterWorkingStateWorker.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/quoter/quoter_states/QuoterEndStateWorker.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/quoter/QuoterLegDriverData.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/quoter/QuoteLegDriver.h"
#include "server/sparkd/orderman/actors/order_manager/ChildOrder.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/SpreaderChildOrderState.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/quoter/QuoteDriverSetHistory.h"
#include <sstream>
@@ -24,7 +25,6 @@ namespace bts {
namespace orderman {
class ISpreaderManager;
//class SpreaderChildOrderState;
class SingleLegQuoter
{
friend QuoterStartStateWorker;
@@ -45,8 +45,8 @@ private:
// legs requires auxiliary storage for drivers, which
// _drivers_temp_usage provides. once we are ready
// to send the orders, the drivers are committed to
// _drivers_commited
std::vector<QuoterLegDriverData> _drivers_temp_usage, _drivers_commited;
std::vector<QuoteLegDriver> _drivers_temp_usage;
QuoteDriverSetHistory _driver_cache;
QuoteLegPriceQuantityState _price_quantity_state;
bool _is_permenantly_cancelled;
// state workers
@@ -108,13 +108,10 @@ public:
// the quoter returns true if it is in a state where orders
// can be modified
bool accept_modifications() const;
// for a given leg, returns the driver price that was used
// to price the current order
Price driver_price (size_t leg_idx) const;
const QuoterLegDriverData& driver_data (size_t leg_idx) const
{
bts_assert (leg_idx < _drivers_commited.size());
return _drivers_commited[leg_idx];
const QuoteLegDriver& driver_data (Price quote_price, size_t leg_idx) const{
const QuoteLegDriver* leg_driver = _driver_cache.find_driver(quote_price,leg_idx);
bts_assert(leg_driver!=nullptr);
return *leg_driver;
}
// tells if it is expecting to get a cancel (this helps catch
// unsolicited cancels). returns true is its expecting a cancel
@@ -152,12 +149,12 @@ private:
}
void _cancel_order () {_child_order_state.cancel_order();}
// this updates the hedge reduction in _price_quantity_state
void _incorporate_other_leg_fill (size_t leg_index, Quantity filled_quantity);
void _incorporate_other_leg_fill (size_t leg_index, Quantity hedge_reduction);
// this is the function that computes the quoting price and the
// quantity for this leg.
void _compute_price_quantity(Price& price, Quantity& quantity);
// copies the computed driver to committed dri
void _commit_drivers ();
void _commit_drivers (Price quote_price);
void _transform_to_START ();
void _transform_to_WAITING_FOR_SIZE ();
void _transform_to_WAITING_FOR_FIRST_ACK ();
@@ -23,6 +23,7 @@ _reported_state(){
_quoter_child_order_states.reserve(_n_legs);
_quoters.reserve (_n_legs);
_hedgers.reserve (_n_legs);
_unhedged_deltas.reserve(_n_legs);
_leg_data.reserve (_n_legs);
for (const auto& lpd_iter : legs){
_leg_prop_data.push_back(lpd_iter);
@@ -34,9 +35,15 @@ _reported_state(){
_quoter_child_order_states.emplace_back (empty_order,leg_property_data.leg_security_key(),_is_live,true);
_quoters.emplace_back(spreader_manager, leg_property_data, _n_legs, _quoter_child_order_states.back());
_hedgers.emplace_back ();
_leg_data.emplace_back(leg_property_data, _quoters.back(), _quoter_order_builders.back(),
_hedger_order_builders.back(), _hedgers.back (), _quoter_child_order_states.back(),
_books.back());
_unhedged_deltas.emplace_back();
_leg_data.emplace_back(leg_property_data,
_quoters.back(),
_quoter_order_builders.back(),
_hedger_order_builders.back(),
_hedgers.back (),
_quoter_child_order_states.back(),
_books.back(),
_unhedged_deltas.back());
bts_assert (_sec_key_to_index.find(leg_property_data.security_key()) == _sec_key_to_index.end());
_sec_key_to_index.insert(std::pair<uint32_t, size_t>(leg_property_data.security_key(), leg_property_data.leg_index()));
}
@@ -8,6 +8,7 @@
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
#include "server/sparkd/orderman/orders/PreActivationOrderServices.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/hedger/UnhedgedDelta.h"
namespace bts{
namespace orderman {
@@ -32,7 +33,8 @@ private:
std::vector<SpreaderChildOrderState> _quoter_child_order_states;
std::vector<SingleLegQuoter> _quoters;
std::vector<std::vector<SingleFillHedger>> _hedgers;
std::vector<SpreaderLegData> _leg_data;
std::vector<UnhedgedDelta> _unhedged_deltas;
std::vector<SpreaderLegData> _leg_data;
boost::container::flat_map<uint32_t, size_t> _sec_key_to_index;
// state thats sent out in report (its kept here amd modified
// vs creating a new one everytime)
@@ -9,7 +9,8 @@ SpreaderLegData::SpreaderLegData (const LegPropertyData& leg_property_data,
SpreaderOrderBuilder& hedger_order_builder,
std::vector<SingleFillHedger>& hedger_data_vector,
SpreaderChildOrderState& quoter_child_order_state,
Book& book)
Book& book,
UnhedgedDelta& unhedged_delta)
:
_leg_property_data (leg_property_data),
_quoter(quoter),
@@ -17,7 +18,8 @@ _quoter_order_builder (quoter_order_builder),
_hedger_order_builder (hedger_order_builder),
_quoter_child_order_state (quoter_child_order_state),
_hedger_data_vector (hedger_data_vector),
_book(book)
_book(book),
_unhedged_delta(unhedged_delta)
{}
SpreaderLegData::SpreaderLegData(SpreaderLegData&& other)
@@ -28,7 +30,8 @@ _quoter_order_builder(other._quoter_order_builder),
_hedger_order_builder(other._hedger_order_builder),
_quoter_child_order_state (other._quoter_child_order_state),
_hedger_data_vector (other._hedger_data_vector),
_book(other._book){
_book(other._book),
_unhedged_delta(other._unhedged_delta){
bts_assert (false);
}
@@ -5,6 +5,7 @@
#include "SpreaderChildOrderState.h"
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/external_interfacing/SpreaderOrderBuilder.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/hedger/UnhedgedDelta.h"
namespace bts{
namespace orderman{
@@ -28,6 +29,7 @@ private:
SpreaderChildOrderState& _quoter_child_order_state;
std::vector<SingleFillHedger>& _hedger_data_vector;
Book& _book;
UnhedgedDelta& _unhedged_delta;
public:
SpreaderLegData (const LegPropertyData& leg_property_data,
SingleLegQuoter& quoter,
@@ -35,7 +37,8 @@ public:
SpreaderOrderBuilder& hedger_order_builder,
std::vector<SingleFillHedger>& hedger_data_vector,
SpreaderChildOrderState& quoter_child_order_state,
Book& book);
Book& book,
UnhedgedDelta& unhedged_delta);
SpreaderLegData() = delete;
SpreaderLegData(const SpreaderLegData&) = delete;
// this cons is not deleted since for each leg,
@@ -51,12 +54,13 @@ public:
/////////////////////////////////
const LegPropertyData& leg_property_data() const {return _leg_property_data;}
SingleLegQuoter& quoter() {return _quoter;}
const bts::orderman::Book& book () const {return _book;}
const bts::orderman::Book& book () const {return _book;}
SpreaderChildOrderState& order_update_message () {return _quoter_child_order_state;}
bts::orderman::OrderKey key() {return _quoter_child_order_state.order_key();}
bts::orderman::OrderKey order_key() {return _quoter_child_order_state.order_key();}
SpreaderOrderBuilder& quoter_order_builder() {return _quoter_order_builder;}
SpreaderOrderBuilder& hedger_order_builder() {return _hedger_order_builder;}
UnhedgedDelta& unhedged_delta() {return _unhedged_delta;}
// returns the number of active hedgers
size_t n_hedgers() const { return _hedger_data_vector.size(); }
// returns the hedger at a given index
@@ -6,7 +6,7 @@
#include "server/sparkd/orderman/orderman_utils/Book.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/LegPropertyData.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/ISpreaderManager.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/quoter/QuoterLegDriverData.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/quoter/QuoteLegDriver.h"
#include "lib/api/services/OrderService.pb.h"
#include "server/sparkd/orderman/actors/order_manager/OrderBuilder.h"
@@ -423,17 +423,28 @@ public:
// so if we get an N lot fill in l2, we send a hedge on l1
// sized s1 * N / s2
////////////////////////general functions
static double get_leg_quantity_from_another_leg_quantity (const LegPropertyData& source_leg,
double source_leg_quantity,
const LegPropertyData& target_leg){
return std::abs(target_leg.size_multiplier () * source_leg_quantity / source_leg.size_multiplier ());
}
static Quantity get_leg_quantity_from_another_leg_quantity (const LegPropertyData& source_leg,
Quantity source_leg_quantity,
const LegPropertyData& target_leg){
double rv = std::abs(target_leg.size_multiplier () * source_leg_quantity.value () / source_leg.size_multiplier ());
double rv = get_leg_quantity_from_another_leg_quantity(source_leg,
static_cast<double>(source_leg_quantity.value()),
target_leg);
return Quantity(_round_leg_qty_computed_from_another_leg_qty(rv));
}
}
// given the quantity of the spreader order, determines the quantity
// of the leg
static double get_leg_quantity_from_spreader_quantity(const LegPropertyData& leg,
double spreader_order_quantity){
return std::abs (leg.size_multiplier () * spreader_order_quantity);
}
static Quantity get_leg_quantity_from_spreader_quantity(const LegPropertyData& leg,
Quantity spreader_order_quantity){
double rv = std::abs (leg.size_multiplier () * spreader_order_quantity.value());
double rv = get_leg_quantity_from_spreader_quantity(leg,static_cast<double>(spreader_order_quantity.value()));
return Quantity (_round_leg_qty_computed_from_spreader_qty (rv));
}
// finds the average weighted price and total quantity
@@ -450,53 +461,71 @@ public:
out_avg_price = Price((base_price.value() * base_qty.value() + inc_price.value() * inc_qty.value())/out_total_qty.value());
}
////////////////////////2 legged spreads
// given a quoting target qty, finds the "driver" price
// and qty, thats needed to hedge the quote. It also
// finds the ammended quoting target (since the hedge
// book might not have enough to support the entire
// quoting target qty)
static void compute_driver(Quantity quote_target,
const LegPropertyData& quote_leg_data,
const LegPropertyData& hedge_leg_data,
const bts::orderman::Book& hedge_leg_book,
Price& out_hedge_price,
Quantity& out_hedge_quantity,
Quantity& out_quote_target){
// effective_mult is the number of contracts needed to
// hedge one quoting contract
double effective_mult = std::abs(hedge_leg_data.leg_params().quote_params().lean_quantity_ratio() * hedge_leg_data.size_multiplier() / quote_leg_data.size_multiplier());
uint32_t hedge_requirement = _round_hedge_target (effective_mult * quote_target.value()); // rounding hedging up
SpreaderUtils::traverse_depth_for_quantity(hedge_leg_book,
SpreaderUtils::invert_side (hedge_leg_data.trading_side()),
Quantity(hedge_requirement),
out_hedge_price,
out_hedge_quantity);
// when we floor the quoting target (ie _round_quoting_qty)
// we are essentially demanding less in terms of hedging, ex :
// consider the hedge book depth with qty's 5, 10, 5, 20, 10 (5 is the inside)
// and hedge ratio 20 and target qty = 5, so effectively target = 100,
// we get 50 at the last level, out_qty = _round_quoting_qty (50/20) = 2,
// which means we need 2 * 20 and should re-evaluate
uint32_t out_quote_target_uint = _round_quoting_qty (out_hedge_quantity.value() / effective_mult); // rounding quoting down
out_quote_target_uint = std::min(out_quote_target_uint,quote_target.value());
out_quote_target = Quantity (out_quote_target_uint);
}
// calculates the computed price and quantity for a quoting
// leg given the data for the other leg
static void compute_price_quantity_2_legs(Price target_spread_price,
Quantity this_leg_target_quantity,
const LegPropertyData& this_leg_data,
const LegPropertyData& other_leg_data,
const bts::orderman::Book& other_leg_book,
Price& price, Quantity& quantity){
// other_leg_price and other_leg_quantity are the price and quantity calculated
// from traversing the other book, for the requires size
Price other_leg_price; Quantity other_leg_quantity;
// this is the size ratio of the other leg vs this leg
double effective_mult = std::abs(other_leg_data.leg_params().quote_params().lean_quantity_ratio() * other_leg_data.size_multiplier() / this_leg_data.size_multiplier());
// to be conservative round the required size up
uint32_t other_leg_target_quantity = _round_hedge_target (effective_mult * this_leg_target_quantity.value());
SpreaderUtils::traverse_depth_for_quantity(other_leg_book, SpreaderUtils::invert_side (other_leg_data.trading_side()), Quantity(other_leg_target_quantity), other_leg_price, other_leg_quantity);
// when we floor we essentially go back up the hedging ladder
// so we should re-evaluate
// ex, consider the hedge book depth with qty's 5, 10, 5, 20, 10 (5 is the inside)
// and hedge ratio 20 and target qty = 5, so effectively target = 100,
// we get 50 at the last level, out_qty = floor (50/20) = 2, so the
// price should not be the last level but the second last level
uint32_t out_quantity = _round_quoting_qty (other_leg_quantity.value() / effective_mult);
quantity = Quantity (out_quantity);
double out_price = (target_spread_price.value() - other_leg_price.value() * other_leg_data.price_multiplier())/this_leg_data.price_multiplier();
out_price = bts::securities::raw_round_to_quote_price (out_price, this_leg_data.tick_size(), this_leg_data.trading_side());
price = Price(out_price);
Quantity quote_target,
const LegPropertyData& quote_leg_data,
const LegPropertyData& hedge_leg_data,
const bts::orderman::Book& hedge_leg_book,
Price& quote_price,
Quantity& quote_qty){
Price hedge_price;
Quantity hedge_qty;
compute_driver(quote_target,quote_leg_data,hedge_leg_data,hedge_leg_book,
hedge_price,hedge_qty,quote_qty);
double out_price = (target_spread_price.value() - hedge_price.value() * hedge_leg_data.price_multiplier())/quote_leg_data.price_multiplier();
out_price = bts::securities::raw_round_to_quote_price (out_price, quote_leg_data.tick_size(), quote_leg_data.trading_side());
quote_price = Price(out_price);
}
static void compute_hedge_price_size_2_legs (Price spread_price,
const LegPropertyData& quote_filled_leg,
Price quote_fill_price,
Quantity quote_fill_quantity,
double quote_fill_quantity,
const LegPropertyData& hedge_leg,
Price& out_hedge_price,
Quantity& out_hedge_quantity){
// for ratio spreads hedge sizes are simply rounded off (the fraction can be a setting)
double& out_hedge_quantity){
out_hedge_quantity = get_leg_quantity_from_another_leg_quantity (quote_filled_leg, quote_fill_quantity, hedge_leg);
double out_hedge_price_d = (spread_price.value() - quote_fill_price.value() * quote_filled_leg.price_multiplier())/hedge_leg.price_multiplier();
out_hedge_price_d = bts::securities::raw_round_to_quote_price (out_hedge_price_d, hedge_leg.tick_size(), hedge_leg.trading_side());
out_hedge_price = Price(out_hedge_price_d);
out_hedge_price = Price(bts::securities::raw_round_to_quote_price (out_hedge_price_d, hedge_leg.tick_size(), hedge_leg.trading_side()));
}
////////////////////////N legged case
static void compute_quoting_price_size_n_legs (Price target_spread_price,
Quantity quoting_leg_target_quantity,
size_t quoting_leg_index,
const ISpreaderManager& spreader,
std::vector<QuoterLegDriverData>& driver_vector,
std::vector<QuoteLegDriver>& driver_vector,
Price& out_price,
Quantity& out_quantity){
compute_drivers (quoting_leg_target_quantity, quoting_leg_index, spreader, driver_vector);
@@ -514,19 +543,23 @@ public:
static void compute_drivers(Quantity quoting_leg_target_quantity,
size_t quoting_leg_index,
const ISpreaderManager& spreader,
std::vector<QuoterLegDriverData>& out_driver_vector){
const LegPropertyData& quoting_leg_data = spreader.get_leg_data (quoting_leg_index);
std::vector<QuoteLegDriver>& out_driver_vector){
const LegPropertyData& quote_leg_data = spreader.get_leg_data (quoting_leg_index);
size_t n_legs = out_driver_vector.size();
for (size_t idx = 0; idx < n_legs; idx++)
if (idx != quoting_leg_index){
const bts::orderman::Book& driver_leg_book = spreader.get_book(idx);
const LegPropertyData& driver_leg_data = spreader.get_leg_data(idx);
double driver_effective_mult = std::abs(driver_leg_data.leg_params().quote_params().lean_quantity_ratio() * driver_leg_data.size_multiplier() / quoting_leg_data.size_multiplier());
uint32_t driver_leg_target_quantity = _round_hedge_target (driver_effective_mult * quoting_leg_target_quantity.value());
Price driver_price; Quantity driver_quantity;
SpreaderUtils::traverse_depth_for_quantity(driver_leg_book, SpreaderUtils::invert_side (driver_leg_data.trading_side()), Quantity(driver_leg_target_quantity), driver_price, driver_quantity);
Quantity quoting_leg_calc_quantity = Quantity (_round_quoting_qty (driver_quantity.value() / driver_effective_mult));
out_driver_vector[idx].set (driver_price, driver_quantity, quoting_leg_calc_quantity);
const bts::orderman::Book& hedge_leg_book = spreader.get_book(idx);
const LegPropertyData& hedge_leg_data = spreader.get_leg_data(idx);
Price hedge_price;
Quantity hedge_qty, quote_qty;
compute_driver(quoting_leg_target_quantity,
quote_leg_data,
hedge_leg_data,
hedge_leg_book,
hedge_price,
hedge_qty,
quote_qty);
out_driver_vector[idx].set (hedge_price, hedge_qty, quote_qty);
}
}
// computes the quoting price from the leg drivers
@@ -534,13 +567,13 @@ public:
static Price compute_quoting_price_from_drivers(Price target_spread_price,
size_t quoting_leg_index,
const ISpreaderManager& spreader,
const std::vector<QuoterLegDriverData>& driver_vector){
const std::vector<QuoteLegDriver>& driver_vector){
const LegPropertyData& quoting_leg_data = spreader.get_leg_data (quoting_leg_index);
size_t n_legs = driver_vector.size();
double sum = 0;
for (size_t idx = 0; idx < n_legs; idx++)
if (idx != quoting_leg_index)
sum += driver_vector[idx].driver_price.value() * spreader.get_leg_data (idx).price_multiplier();
sum += driver_vector[idx].driver_price().value() * spreader.get_leg_data (idx).price_multiplier();
double rv = (target_spread_price.value() - sum)/quoting_leg_data.price_multiplier();
rv = bts::securities::raw_round_to_quote_price (rv, quoting_leg_data.tick_size(), quoting_leg_data.trading_side());
return Price(rv);
@@ -549,26 +582,40 @@ public:
// what each hedging leg can support. this function
// simply finds that minimum
static Quantity compute_quoting_quantity_from_drivers (size_t quoting_leg_index,
const std::vector<QuoterLegDriverData>& driver_vector){
const std::vector<QuoteLegDriver>& driver_vector){
uint32_t rv = std::numeric_limits<uint32_t>::max();
for (size_t idx = 0; idx < driver_vector.size(); idx++)
if (idx != quoting_leg_index)
if (driver_vector[idx].quoter_leg_quantity.value() < rv)
rv = driver_vector[idx].quoter_leg_quantity.value();
if (driver_vector[idx].quoter_leg_quantity().value() < rv)
rv = driver_vector[idx].quoter_leg_quantity().value();
return Quantity(rv);
}
static void compute_hedge_price_size_n_legs (const LegPropertyData& quote_filled_leg,
Price,
Quantity quote_fill_quantity,
double quote_fill_quantity,
const LegPropertyData& hedge_leg,
const QuoterLegDriverData& leg_driver_data,
Price& out_hedge_price, Quantity& out_hedge_quantity){
out_hedge_price = leg_driver_data.driver_price;
out_hedge_quantity = get_leg_quantity_from_another_leg_quantity (quote_filled_leg, quote_fill_quantity, hedge_leg);
const QuoteLegDriver& leg_driver_data,
Price& out_hedge_price,
double& out_hedge_quantity){
out_hedge_price = Price(leg_driver_data.driver_price().value());
out_hedge_quantity = get_leg_quantity_from_another_leg_quantity (quote_filled_leg,
quote_fill_quantity,
hedge_leg);
}
////////////////////////////////////////////////////
// Rounding functions //////////////////////////////
////////////////////////////////////////////////////
// rounds a double to a whole number if its too close
// to it (this is ued in the case of rounding errors
// when 0.9999999 or 1.0000001 is really 1)
static double round_if_near_whole(double qty,
double vicinity=0.000000001){
double round_qty = std::round(qty);
if (std::abs(round_qty - qty) <= vicinity)
return round_qty;
else
return qty;
}
static uint32_t _round_quantity(double qty, bool is_up){
double round_err = 0.000000001;
double round_qty = std::round(qty);
@@ -10,8 +10,7 @@ namespace orderman {
class SpreaderChildOrderState;
class Book;
class SingleFillHedger;
class IHedgerStateWorker
{
class IHedgerStateWorker{
public:
// called once when transition to this state happens
virtual void on_transition () = 0;
@@ -0,0 +1,29 @@
#include "UnhedgedDelta.h"
#include <cmath>
#include "lib/utils/bts_assert.h"
#include "server/sparkd/orderman/orders/spreader_orders/spreader/SpreaderUtils.h"
namespace bts{
namespace orderman{
UnhedgedDelta::UnhedgedDelta()
:
_qty(0),
_avg_price(0){
}
void UnhedgedDelta::add_unhedged(double price,double qty){
bts_assert(qty >= 0);
auto total_qty=_qty+qty;
_avg_price = (_qty*_avg_price + qty*price)/total_qty;
_qty=total_qty;
}
uint32_t UnhedgedDelta::subtract_hedge_if_available(){
uint32_t rv = 0;
double qty = SpreaderUtils::round_if_near_whole(_qty);
if (qty >= 1){
rv = std::floor(qty);
_qty = qty-rv;
}
return rv;
}
}}
@@ -0,0 +1,25 @@
#pragma once
#include <cstdint>
namespace bts{
namespace orderman{
// in the case when the hedge quantity is not a full contract
// (ex if we get a 1 lot fill on a 1 X 3 spreader, where 3 is
// a quoting leg), this class is used to accumulate the unhedged
// delta
class UnhedgedDelta{
private:
double _qty;
double _avg_price;
public:
UnhedgedDelta();
// adds to the unhedged qty and updates the
// running average price
void add_unhedged (double price, double qty);
// if >= 1 unhedged delta is available, it
// subtracts the integer part and returns it.
// else returns 0
uint32_t subtract_hedge_if_available();
};
}}
@@ -0,0 +1,29 @@
#include "QuoteDriverSet.h"
namespace bts {
namespace orderman {
QuoteDriverSet::QuoteDriverSet(size_t n_legs)
:
_quote_seq_no(0),
_quote_price(0.0),
_drivers(){
_drivers.reserve(n_legs);
for (size_t i=0; i<n_legs; i++)
_drivers.emplace_back();
}
void QuoteDriverSet::update(uint16_t quote_seq_no,
Price quote_price,
const std::vector<QuoteLegDriver>& drivers){
_quote_seq_no=quote_seq_no;
_quote_price=quote_price;
bts_assert(_drivers.size()==drivers.size());
int i=0;
for(const auto& d:drivers)
_drivers[i++]=d;
}
const QuoteLegDriver& QuoteDriverSet::leg_driver(size_t leg_index) const{
bts_assert(leg_index<_drivers.size());
return _drivers[leg_index];
}
}}
@@ -0,0 +1,28 @@
#pragma once
#include "QuoteLegDriver.h"
namespace bts {
namespace orderman {
// this is the list of all drivers for a
// single quoting leg
class QuoteDriverSet{
private:
uint16_t _quote_seq_no;
Price _quote_price;
std::vector<QuoteLegDriver> _drivers;
public:
QuoteDriverSet (size_t n_legs);
uint16_t quote_seq_no() const{
return _quote_seq_no;
}
Price quote_price() const{
return _quote_price;
}
void update(uint16_t quote_seq_no,
Price quote_price,
const std::vector<QuoteLegDriver>& drivers);
const QuoteLegDriver& leg_driver (size_t leg_index) const;
};
}}
@@ -0,0 +1,54 @@
#include "QuoteDriverSetHistory.h"
#include "lib/domain_model/Security.h"
namespace bts {
namespace orderman {
QuoteDriverSetHistory::QuoteDriverSetHistory(size_t n_legs,
size_t cache_size,
double tick_size)
:
_n_legs(n_legs),
_cache_size(cache_size),
_tick_size(tick_size),
_quote_seq_no(0),
_driver_sets(),
_h_ptr(0){
_driver_sets.reserve(cache_size);
for(size_t i=0;i<cache_size;i++)
_driver_sets.emplace_back(n_legs);
}
void QuoteDriverSetHistory::add(const std::vector<QuoteLegDriver>& drivers,
Price quote_price){
bts_assert(_h_ptr<_cache_size);
bts_assert(_cache_size>0);
_quote_seq_no++;
_driver_sets[_h_ptr].update(_quote_seq_no,quote_price,drivers);
// move it to the next writeable place
if(_h_ptr == _cache_size-1)
_h_ptr = 0;
else
_h_ptr++;
}
const QuoteDriverSet* QuoteDriverSetHistory::_latest() const{
if (_quote_seq_no>0)
return &(_driver_sets[_h_ptr==0 ? _cache_size-1 : _h_ptr-1]);
return
nullptr;
}
const QuoteDriverSet* QuoteDriverSetHistory::_find_driver_set(Price quote_price) const{
const QuoteDriverSet* rv=nullptr;
for(const auto& d : _driver_sets)
if (bts::securities::test_price_equality(d.quote_price().value(),
quote_price.value(),
_tick_size))
if (rv==nullptr||d.quote_seq_no()>rv->quote_seq_no())
rv=&d;
return rv!=nullptr ? rv:_latest();
}
const QuoteLegDriver* QuoteDriverSetHistory::find_driver(Price quote_price,
size_t leg_index) const{
const QuoteDriverSet* driver_set_ptr=_find_driver_set(quote_price);
return driver_set_ptr==nullptr ? nullptr : &(driver_set_ptr->leg_driver(leg_index));
}
}}
@@ -0,0 +1,26 @@
#pragma once
#include "QuoteDriverSet.h"
namespace bts {
namespace orderman {
class QuoteDriverSetHistory{
private:
const size_t _n_legs, _cache_size;
const double _tick_size;
uint16_t _quote_seq_no;
std::vector<QuoteDriverSet> _driver_sets;
uint16_t _h_ptr;
const QuoteDriverSet* _latest() const;
const QuoteDriverSet* _find_driver_set(Price quote_price) const;
public:
QuoteDriverSetHistory(size_t n_legs,
size_t cache_size,
double tick_size);
void add(const std::vector<QuoteLegDriver>& drivers,
Price quote_price);
const QuoteLegDriver* find_driver(Price quote_price, size_t leg_index) const;
};
}}
@@ -0,0 +1,12 @@
#include "QuoteLegDriver.h"
namespace bts {
namespace orderman {
QuoteLegDriver::QuoteLegDriver()
:
_driver_price(0.0),
_driver_quantity(0),
_quoter_leg_quantity(0){
}
}}
@@ -0,0 +1,43 @@
#pragma once
#include "server/sparkd/orderman/orderman_utils/orderman_common.h"
namespace bts {
namespace orderman {
// Should be read as Quote_LegDriver, this is the
// price and qty in a hedging leg that can sustain
// a "_quoter_leg_quantity" sized quote in the
// quoting leg
class QuoteLegDriver
{
private:
Price _driver_price;
Quantity _driver_quantity;
Quantity _quoter_leg_quantity;
public:
// driver_price and driver_quantity are the price and quantity
// in the hedging (or driver) leg that can drive a quoter_leg_quantity
// sized order in the quoting leg
QuoteLegDriver();
QuoteLegDriver& operator=(const QuoteLegDriver& s){
set(s.driver_price(),s.driver_quantity(), s.quoter_leg_quantity());
return *this;
}
void set(Price driver_price,
Quantity driver_quantity,
Quantity quoter_leg_quantity){
_driver_price = driver_price;
_driver_quantity = driver_quantity;
_quoter_leg_quantity = quoter_leg_quantity;
}
Price driver_price() const{
return _driver_price;
}
Quantity driver_quantity() const{
return _driver_quantity;
}
Quantity quoter_leg_quantity() const{
return _quoter_leg_quantity;
}
};
}}
@@ -1,32 +0,0 @@
#pragma once
#include "server/sparkd/orderman/orderman_utils/orderman_common.h"
namespace bts {
namespace orderman {
// the concept of a "driver" is the size and price of a leg that
// can sustain a quote in the quoted leg
class QuoterLegDriverData
{
public:
// driver_price and driver_quantity are the price and quantity
// in the hedging (or driver) leg that can drive a quoter_leg_quantity
// sized order in the quoting leg
Price driver_price;
Quantity driver_quantity;
Quantity quoter_leg_quantity;
QuoterLegDriverData()
:
driver_price(0.0),
driver_quantity(0),
quoter_leg_quantity(0)
{}
void set(Price new_driver_price, Quantity new_driver_quantity, Quantity new_quoter_leg_quantity)
{
driver_price = new_driver_price;
driver_quantity = new_driver_quantity;
quoter_leg_quantity = new_quoter_leg_quantity;
}
};
}}
@@ -51,7 +51,7 @@ void QuoterWaitingForSizeStateWorker::_longpath_analyze_and_act (orderman::TickT
Price out_price; Quantity out_quantity;
_quoter->_compute_price_quantity (out_price, out_quantity);
if (out_quantity.value() > 0){
_quoter->_commit_drivers ();
_quoter->_commit_drivers (out_price);
LOG(OrderMan, Info, "QuoterWaitingForSizeStateWorker sending order key: {} price: {} quantity: {}", _quoter->_key(), out_price, out_quantity);
// this is the only place where a quoter order is entered
_quoter->_place_order(out_price, out_quantity, cid);
@@ -94,7 +94,7 @@ void QuoterWorkingStateWorker::_modify_order (orderman::TickToTradeCorID cid, Pr
bts::securities::test_price_equality(_quoter->_price_quantity_state.out_price().value(),
price.value(), _quoter->_this_leg_data.tick_size());
if (!same_price_qty){
_quoter->_commit_drivers ();
_quoter->_commit_drivers (price);
_quoter->_modify_order(price, quantity, cid);
}
}
@@ -97,19 +97,31 @@ void SpreaderWorkingStateWorker::on_quoter_order_fill(SpreaderLegData& leg_data)
order_update_message.last_update_filled_quantity(),
order_update_message.last_update_filled_price());
bts_assert (order_update_message.last_update_filled_quantity().value() > 0);
for (size_t q_idx = 0; q_idx < _spreader->_bookkeeping_data().n_legs(); q_idx++){
SpreaderLegData& other_leg_data = _spreader->_bookkeeping_data().get_leg_data (q_idx);
for (size_t q_idx = 0; q_idx < _spreader->_bookkeeping_data().n_legs(); q_idx++){
if (q_idx != leg_data.leg_property_data().leg_index()){
// if it is an actively quoting leg, inform the quoter about it
// so it can decrease the proportional size
if (other_leg_data.leg_property_data().is_active_quoting())
other_leg_data.quoter().update_other_leg_fills(leg_data.leg_property_data().leg_index(),
order_update_message.last_update_filled_quantity());
// a hedge is sent regardless
_spreader->_send_hedge (leg_data.leg_property_data().leg_index(),
order_update_message.last_update_filled_price(),
order_update_message.last_update_filled_quantity(),
q_idx);
SpreaderLegData& other_leg_data = _spreader->_bookkeeping_data().get_leg_data (q_idx);
Price hedge_price;
double hedge_qty;
_spreader->_compute_hedge_price_quantity(leg_data.leg_property_data().leg_index(),
order_update_message.last_update_filled_price(),
order_update_message.last_update_filled_quantity().value(),
q_idx,
hedge_price,
hedge_qty);
UnhedgedDelta& leg_unhedged_delta = other_leg_data.unhedged_delta();
leg_unhedged_delta.add_unhedged(hedge_price.value(), hedge_qty);
uint32_t total_unhedged_qty=leg_unhedged_delta.subtract_hedge_if_available();
if (total_unhedged_qty != 0){
// if it is an actively quoting leg, inform the quoter about it
// so it can decrease the proportional size
if (other_leg_data.leg_property_data().is_active_quoting())
other_leg_data.quoter().update_other_leg_fills(leg_data.leg_property_data().leg_index(),
Quantity(total_unhedged_qty));
// a hedge is sent regardless
_spreader->_send_hedge(other_leg_data.leg_property_data().leg_index(),
hedge_price,
Quantity(total_unhedged_qty));
}
}
}
// this is done at the end (i dont like ordering though) because
@@ -483,7 +483,7 @@ TEST_CASE ("compute quoting price quantity 3 legs", "[spreader]")
std::vector<LegSpreaderParams> params; params.push_back(leg_params_1);params.push_back(leg_params_2);params.push_back(leg_params_3);
SpreaderManagerForTest* sm = create_spreader_manager_n_legs (OrderKey(), legs, params);
std::vector<QuoterLegDriverData> driver_vector;
std::vector<QuoteLegDriver> driver_vector;
for(size_t i = 0; i < legs.size(); i++) driver_vector.emplace_back();
SECTION("basic joining")
@@ -570,8 +570,9 @@ TEST_CASE ("compute quoting price quantity 3 legs", "[spreader]")
TEST_CASE ("compute hedge price quantity 2 legs", "[spreader]")
{
ContractLeg leg_1, leg_2;
Price out_hedge_price; Quantity out_hedge_quantity;
Price spreader_price = Price (0.35);
Price out_hedge_price;
double out_hedge_quantity;
Price spreader_price = Price(0.35);
double size_mult_1 = 0.25, size_mult_2 = -1.1, // same used for price and qty
price_mult_1 = 0.35, price_mult_2 = -1.5;
double lean_mult = 1.0;
@@ -580,23 +581,23 @@ TEST_CASE ("compute hedge price quantity 2 legs", "[spreader]")
LegPropertyData leg_prop_data_1 = create_leg_property_data (leg_1, 0, price_mult_1, true, lean_mult);
LegPropertyData leg_prop_data_2 = create_leg_property_data (leg_2, 1, price_mult_2, true, lean_mult);
// md
SpreaderUtils::compute_hedge_price_size_2_legs (spreader_price, leg_prop_data_2, Price (2500.5), Quantity (3),
SpreaderUtils::compute_hedge_price_size_2_legs (spreader_price, leg_prop_data_2, Price(2500.5), 3,
leg_prop_data_1, out_hedge_price, out_hedge_quantity);
REQUIRE (out_hedge_quantity.value() == 1);
REQUIRE (std::abs(out_hedge_quantity - 0.6818181818181818181) < 0.000000001);
REQUIRE (out_hedge_price.value() == 10717.25);
SpreaderUtils::compute_hedge_price_size_2_legs (spreader_price, leg_prop_data_2, Price (2500.5), Quantity (2),
SpreaderUtils::compute_hedge_price_size_2_legs (spreader_price, leg_prop_data_2, Price(2500.5), 2,
leg_prop_data_1, out_hedge_price, out_hedge_quantity);
REQUIRE (out_hedge_quantity.value() == 0);
REQUIRE (std::abs(out_hedge_quantity - 0.45454545454545454545) < 0.000000001);
REQUIRE (out_hedge_price.value() == 10717.25);
// reversing the side of the quote fill
SpreaderUtils::compute_hedge_price_size_2_legs (spreader_price, leg_prop_data_1, Price (10000.5), Quantity (2),
SpreaderUtils::compute_hedge_price_size_2_legs (spreader_price, leg_prop_data_1, Price(10000.5), 2,
leg_prop_data_2, out_hedge_price, out_hedge_quantity);
REQUIRE (out_hedge_quantity.value() == 9);
REQUIRE (std::abs(out_hedge_quantity - 8.8)<0.000000001);
REQUIRE (out_hedge_price.value() == 2333.25);
SpreaderUtils::compute_hedge_price_size_2_legs (spreader_price, leg_prop_data_1, Price (9997), Quantity (6),
SpreaderUtils::compute_hedge_price_size_2_legs (spreader_price, leg_prop_data_1, Price(9997), 6,
leg_prop_data_2, out_hedge_price, out_hedge_quantity);
REQUIRE (out_hedge_quantity.value() == 26);
REQUIRE (std::abs(out_hedge_quantity - 26.40) < 0.000000001);
REQUIRE (out_hedge_price.value() == 2332.5);
}
/************************************************************************************************************/
@@ -2341,6 +2342,201 @@ TEST_CASE ("prehedge_payup", "[spreader]"){
REQUIRE(bts::securities::test_price_equality(out_price.value(),21.10,0.05));
}
}
// n legged spreader price evaluation /////////////////////
///////////////////////////////////////////////////////////
TEST_CASE ("nlegs_compute_driver", "[spreader]"){
bts::orderman::OrderKey order_key(1);
ContractLeg leg_1, leg_2, leg_3, leg_4;
fill_future_contract_leg (leg_1, 1, "CL", bts::api::Venue::VENUE_GLBX, 2018, 1, 15, 1.0, 1.0, api::Side::SIDE_BUY, 1.0, 0.01);
fill_future_contract_leg (leg_2, 2, "CL", bts::api::Venue::VENUE_GLBX, 2018, 2, 15, 1.0, 1.0, api::Side::SIDE_BUY, 1.0, 0.01);
fill_future_contract_leg (leg_3, 3, "CL", bts::api::Venue::VENUE_GLBX, 2018, 3, 15, 1.0, 1.0, api::Side::SIDE_BUY, 1.0, 0.01);
fill_future_contract_leg (leg_4, 4, "CL", bts::api::Venue::VENUE_GLBX, 2018, 4, 15, -3.0, -3.0, api::Side::SIDE_SELL, 1.0, 0.01);
std::vector<ContractLeg> legs;
legs.push_back(leg_1);legs.push_back(leg_2);legs.push_back(leg_3);legs.push_back(leg_4);
Security sec;
fill_synthetic_security (sec, 2000, "4legs", legs);
// execution params/order def
LegSpreaderParams lsp_1, lsp_2, lsp_3, lsp_4;
fill_leg_spreader_params(lsp_1, 1.0, false, 1.0);fill_leg_spreader_params(lsp_2, 1.0, false, 1.0);
fill_leg_spreader_params(lsp_3, 1.0, false, 1.0);fill_leg_spreader_params(lsp_4, -4.0, true, 1.0);
// // post hedge time out here
std::vector<LegSpreaderParams> params;
params.push_back(lsp_1);params.push_back(lsp_2);
params.push_back(lsp_3);params.push_back(lsp_4);
bts::api::orderman::OrderDefinition order_def;
fill_spreader_order_definition(order_def, Side::SIDE_BUY, sec, params);
// putting it together in SpreaderBookkeepingData
SpreaderPreActivationOrderServices::TestOnlySpreaderPreActivationOrderServices
testonly_spreader_pao_services = _create_spreader_pao_services(sec);
SpreaderPreActivationOrderServices pao_services (&testonly_spreader_pao_services);
std::vector<LegPropertyData> leg_props;
SpreaderUtils::resolve_synthetic_spread(pao_services, order_def, leg_props);
SpreaderManagerForTest spreader_manager (order_key, order_def, pao_services, leg_props);
SpreaderBookkeepingData& bookkeeping_data = spreader_manager.bookkeeping_data();
SECTION("compute_1"){
Quantity quoting_leg_target (300);
Price spread_price(0.25);
fill_book(spreader_manager, 0, new double[5]{65.31,65.32,65.34,65.36, 65.40},
new uint16_t[5]{12,18,20,19,23},bts::api::Side::SIDE_SELL,true);
fill_book(spreader_manager, 1, new double[5]{64.40,64.41,64.43,64.45,64.50},
new uint16_t[5]{1,5,20,12,15},bts::api::Side::SIDE_SELL,true);
fill_book(spreader_manager, 2, new double[5]{64.10,64.18,64.19,64.20,64.21},
new uint16_t[5]{10,43,17,50,15},bts::api::Side::SIDE_SELL,true);
std::vector<QuoteLegDriver> out_driver_vector;
for (int i=0; i<4; i++)
out_driver_vector.emplace_back();
SpreaderUtils::compute_drivers(quoting_leg_target, 3, spreader_manager, out_driver_vector);
_verify_driver (out_driver_vector[0],92,65.40,276);
_verify_driver (out_driver_vector[1],53,64.50,159);
_verify_driver (out_driver_vector[2],100,64.20,300);
// size of the quoting leg
Quantity computed_quoted_qty=SpreaderUtils::compute_quoting_quantity_from_drivers(3,out_driver_vector);
REQUIRE(computed_quoted_qty.value()==159);
// 2nd pass drivers
SpreaderUtils::compute_drivers(computed_quoted_qty, 3, spreader_manager, out_driver_vector);
_verify_driver (out_driver_vector[0],53,65.36,159);
_verify_driver (out_driver_vector[1],53,64.50,159);
_verify_driver (out_driver_vector[2],53,64.18,159);
// price of the quoting leg
Price computed_price=SpreaderUtils::compute_quoting_price_from_drivers(spread_price,3,spreader_manager,out_driver_vector);
REQUIRE(computed_price.value()==64.60);
//hedging
Price hedge_price; double hedge_quantity;
SpreaderUtils::compute_hedge_price_size_n_legs(spreader_manager.get_leg_data(3),
Price(0),
18,
spreader_manager.get_leg_data(0),
out_driver_vector[0],
hedge_price,
hedge_quantity);
REQUIRE(hedge_price.value()==65.36);
REQUIRE(hedge_quantity==6);
}
SECTION("compute_2_fractional"){
Quantity quoting_leg_target (100);
Price spread_price(0.25);
fill_book(spreader_manager, 0, new double[5]{65.31,65.32,65.34,65.36, 65.40},
new uint16_t[5]{12,18,20,19,23},bts::api::Side::SIDE_SELL,true);
fill_book(spreader_manager, 1, new double[5]{64.40,64.41,64.43,64.45,64.50},
new uint16_t[5]{1,5,20,12,15},bts::api::Side::SIDE_SELL,true);
fill_book(spreader_manager, 2, new double[5]{64.10,64.18,64.19,64.20,64.21},
new uint16_t[5]{11,1,1,5,1},bts::api::Side::SIDE_SELL,true);
std::vector<QuoteLegDriver> out_driver_vector;
for (int i=0; i<4; i++)
out_driver_vector.emplace_back();
SpreaderUtils::compute_drivers(quoting_leg_target, 3, spreader_manager, out_driver_vector);
_verify_driver (out_driver_vector[0],34,65.34,100);
_verify_driver (out_driver_vector[1],34,64.45,100);
_verify_driver (out_driver_vector[2],19,64.21,57);
// size of the quoting leg
Quantity computed_quoted_qty=SpreaderUtils::compute_quoting_quantity_from_drivers(3,out_driver_vector);
REQUIRE(computed_quoted_qty.value()==57);
// // 2nd pass drivers
SpreaderUtils::compute_drivers(computed_quoted_qty, 3, spreader_manager, out_driver_vector);
_verify_driver (out_driver_vector[0],19,65.32,57);
_verify_driver (out_driver_vector[1],19,64.43,57);
_verify_driver (out_driver_vector[2],19,64.21,57);
// // price of the quoting leg
Price computed_price=SpreaderUtils::compute_quoting_price_from_drivers(spread_price,3,spreader_manager,out_driver_vector);
REQUIRE(computed_price.value()==64.57);
// //hedging
Price hedge_price; double hedge_quantity;
SpreaderUtils::compute_hedge_price_size_n_legs(spreader_manager.get_leg_data(3),
Price(0),
10.0,
spreader_manager.get_leg_data(0),
out_driver_vector[0],
hedge_price,
hedge_quantity);
REQUIRE(hedge_price.value()==65.32);
REQUIRE(std::abs(hedge_quantity - 3.333333) < 0.000001);
}
}
TEST_CASE ("quote driver caching", "[spreader]"){
SECTION("zero_search"){
QuoteDriverSetHistory driver_cache(5,10,0.05);
const QuoteLegDriver* driver_ptr=driver_cache.find_driver(Price(100),3);
REQUIRE(driver_ptr==nullptr);
}
SECTION("simple_search"){
size_t n_legs=5, cache_sz=10;
QuoteDriverSetHistory driver_cache(n_legs,cache_sz,0.05);
Price p1(100); std::vector<QuoteLegDriver> d1;
generate_driver_set(d1,n_legs);
driver_cache.add(d1,p1);
Price p2(100.05); std::vector<QuoteLegDriver> d2;
generate_driver_set(d2,n_legs);
driver_cache.add(d2,p2);
Price p3(100.10); std::vector<QuoteLegDriver> d3;
generate_driver_set(d3,n_legs);
driver_cache.add(d3,p3);
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(p1,i);
verify_driver(*d,d1[i]);
}
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(p2,i);
verify_driver(*d,d2[i]);
}
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(p3,i);
verify_driver(*d,d3[i]);
}
// if not found, returns the latest
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(Price(100.01),i);
verify_driver(*d,d3[i]);
}
}
SECTION("collision_search and continous add"){
// adding multiple updates to test circular nature
size_t n_runs=10003, n_legs=4, cache_sz=10;
QuoteDriverSetHistory driver_cache(n_legs,cache_sz,0.05);
for(size_t i=0; i<n_runs; i++){
Price p(gen_rand(1,100));
std::vector<QuoteLegDriver> d;
generate_driver_set(d,n_legs);
driver_cache.add(d,p);
}
Price p1(100); std::vector<QuoteLegDriver> d1;
generate_driver_set(d1,n_legs);
driver_cache.add(d1,p1);
Price p2(100.05); std::vector<QuoteLegDriver> d2;
generate_driver_set(d2,n_legs);
driver_cache.add(d2,p2);
Price p3(100.10); std::vector<QuoteLegDriver> d3;
generate_driver_set(d3,n_legs);
driver_cache.add(d3,p3);
Price p4(100); std::vector<QuoteLegDriver> d4;
generate_driver_set(d4,n_legs);
driver_cache.add(d4,p4);
Price p5(100.15); std::vector<QuoteLegDriver> d5;
generate_driver_set(d5,n_legs);
driver_cache.add(d5,p5);
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(p1,i);
verify_driver(*d,d4[i]);
}
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(p2,i);
verify_driver(*d,d2[i]);
}
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(p3,i);
verify_driver(*d,d3[i]);
}
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(p4,i);
verify_driver(*d,d4[i]);
}
// 100 is there twice, should returns the
// most recently added one
for(size_t i=0; i<n_legs; i++){
const auto d=driver_cache.find_driver(p5,i);
verify_driver(*d,d5[i]);
}
}
}
class RV
{
public:
@@ -292,6 +292,20 @@ bts::orderman::Book get_book(SecurityKey key,
{
return get_book(key, n_levels, bid_prices, bid_sizes, n_levels, ask_prices, ask_sizes, delete_arrays);
}
void fill_book (SpreaderManagerForTest& sm,
size_t index,
double* prices,
uint16_t* sizes,
bts::api::Side side,
bool delete_arrays){
Book book;
fill_book (book, prices, sizes, side);
sm.set_book(book, index);
if (delete_arrays){
delete[] prices;
delete[] sizes;
}
}
/**********************************************************************/
/******************************hedger**********************************/
/**********************************************************************/
@@ -464,14 +478,38 @@ void set_spreader_orders (SpreaderOrder& spreader, double* leg_1_bid_prices, uin
/**********************************************************************/
/******************************misc************************************/
/**********************************************************************/
void _verify_driver(const QuoteLegDriver& driver_data,
uint16_t driver_qty,
double driver_price,
uint16_t quoter_leg_quantity){
double err = 1e-9;
REQUIRE(std::abs(driver_data.driver_price().value() - driver_price)< err);
REQUIRE(driver_data.driver_quantity().value() == driver_qty);
REQUIRE(driver_data.quoter_leg_quantity().value() == quoter_leg_quantity);
}
void verify_driver(const QuoteLegDriver& d1,
const QuoteLegDriver& d2){
_verify_driver(d1,
d2.driver_quantity().value(),
d2.driver_price().value(),
d2.quoter_leg_quantity().value());
}
double gen_rand (double min, double max)
{
double f = (double)rand() / RAND_MAX;
return min + f * (max - min);
}
// SpreaderChildOrderState create_order_update(const LegPropertyData& leg_data)
// {
// SpreaderChildOrderState rv(false, leg_data, true);
// return rv;
// }
}}
void fill_random_driver(QuoteLegDriver& driver){
driver.set(Price(gen_rand(0,10)),
Quantity(gen_rand(1,100)),
Quantity(gen_rand(1,300)));
}
void generate_driver_set(std::vector<QuoteLegDriver>& drivers, size_t n_legs){
drivers.clear();
for(size_t i=0; i < n_legs; i++){
QuoteLegDriver d;
fill_random_driver(d);
drivers.push_back(d);
}
}
}}
@@ -0,0 +1,44 @@
This test verifies that fractional hedges work. That is, when a fill in a quoting leg does
not equate to one full contract in the hedging leg, we accumulate the unhedged
delta, until we can send one or more hedging contracts.
1. Create a new synthetic spread. For the legs choose SIF9 and SIH9 (if you dont
have these contracts, other contracts can be chosen too) and for the quantities choose 1 and -1.5
respectively (and same for the Price multiplier). Disable all quoting, pre-hedge and post-hedge
parameters. Name the synthetic SIF9-1.5SIH9.
1. Bring up the ladder for the synthetic and for the 2 legs.
2. Enter a 10 lot sell order at 20.0 for SIF9 (If the ladder is showing blanks use the market view
to enter orders). The quoting order will lean against this.
3. Enter a 10 lot buy order for the synthetic at -9.900.
4. Verify that a 15 lot quoting order is sent for SIH9 at 19.935.
5. Now we will start filling the quoting order and verify the hedges. A 1 lot fill in the
quoting leg means a 2/3 contract in the hedging leg. Since we can not send 2/3 constracts,
we will add it to the "unhedged delta" till the unhedged delta > 1, and then we will send one
or more orders to get the unhedged delta < 1.
Force quoting fills based on the left most column (by lifting the quoting offer)
and verify that hedge orders of size "hedge_qty" (3rd column) are sent as a result.
The "unhedged_delta" column shows the unhedged delta immediatly after the quoting fill
and the "remaining_unhedged_delta" column is the unhedged delta after the hedge order
has been sent.
You can check the hedge orders being sent easily by looking at the order reduction
happening on the order sent in step 2 (or look at the hedges in the orderbook).
------------------------------------------------------------------------------
quoting fill unhedged_delta hedge_order_sent remaining_unhedged_delta
------------------------------------------------------------------------------
1 2/3 0 2/3
1 4/3 1 1/3
1 1 1 0
4 8/3 2 2/3
5 4 4 0
2 4/3 1 1/3
1 1 1 0
6. At the end of this, the whole order should be filled. Check the order book
to make sure the fill price is -9.900, there are 10 total fills in SIF9 and
15 fills in SIH9.
+34
View File
@@ -0,0 +1,34 @@
This test makes sure the basic spreader functionality works for 4 legged synthetic spreads
with 1 quoting leg.
0. Run this test in sim, with no external markets. We will control the markets with limit orders.
1. Create a 4 legged synthetic spread with legs SIF9, SIH9, SIK9, SIN9, with leg quantities and
price multipliers of 1,1,1 and -1 respectively. Leg SIN9 is the only quoting leg.
Disable all Quote, Pre-Hedge and Post-Hedge paramaters (leg contracts are arbitrary, so if you
dont have those contracts, choose any set of 4 contracts).
2. Enter a synthetic buy order of 10 lots at 40.00. The ladders will most likely be empty, so
use the market view to enter orders.
3. You will see no quoting orders since all the 3 hedging legs have no markets. Now enter
a 10 lot sell orders at 19.950 for SIH9, and 20.200 for SIK9. There should still be no quoting
order for SIN9 since the SIF9 hedge leg still doesnt have a market. Enter a 1 lot sell order at
20.00 for SIF9. This will result in the spreader sending a 1 lot quoting order at 20.150.
4. Now cancel the SIK9 order. This should result in the quoting order getting cancelled as well.
Re-enter the SIK9 order, 10 lots at 20.200 and the quoting order should appear as well.
5. Now lift the quoting leg ask with a 1 lot buy order at the same price. Make sure hedges are sent (and filled)
for all the 3 hedging legs. SIH9 should have no market now and hence we should have no quoting order.
6. Enter a 10 lot sell order at 20.000 for SIF9. This should result in a 9 lot quoting order.
7. Now randomly move the hedge orders as well as the spreader order a few times, and make sure the
quoting order follows it and is correct
quoting_order_price = (SIF9's ask + SIH9's ask + SIK9's ask - spreader_price)
rounded up to the tick.
8. Now lift the complete quoter offer by crossing it with a 9 lot bid. Make sure all the hedges
are sent and the spreader order reached a Completed state in the orderbook.
+13
View File
@@ -0,0 +1,13 @@
@echo off
@setlocal
SETLOCAL
if "%SPARKVM_PORT%"=="" set SPARKVM_PORT=22
if "%1" == "" goto :err
echo.
ssh bts@sparkvm -p %SPARKVM_PORT% -o StrictHostKeyChecking=no -t "cd /home/bts/spark && git checkout %1 && git reset --hard && git clean -fd && git pull && git submodule update"
ENDLOCAL
goto :eof
:err
echo usage git_switch_linux [branch]